/* set defaults */ static int jb_framedata_init(struct jb_framedata *framedata, struct ast_jb_conf *jb_conf) { int jb_impl_type = DEFAULT_TYPE; /* Initialize defaults */ framedata->timer_fd = -1; memcpy(&framedata->jb_conf, jb_conf, sizeof(*jb_conf)); /* Figure out implementation type from the configuration implementation string */ if (!ast_strlen_zero(jb_conf->impl)) { if (!strcasecmp(jb_conf->impl, "fixed")) { jb_impl_type = AST_JB_FIXED; } else if (!strcasecmp(jb_conf->impl, "adaptive")) { jb_impl_type = AST_JB_ADAPTIVE; } else { ast_log(LOG_WARNING, "Unknown Jitterbuffer type %s. Failed to create jitterbuffer.\n", jb_conf->impl); return -1; } } if (!(framedata->jb_impl = ast_jb_get_impl(jb_impl_type))) { return -1; } if (!(framedata->timer = ast_timer_open())) { return -1; } framedata->timer_fd = ast_timer_fd(framedata->timer); framedata->timer_interval = DEFAULT_TIMER_INTERVAL; ast_timer_set_rate(framedata->timer, 1000 / framedata->timer_interval); framedata->start_tv = ast_tvnow(); framedata->jb_obj = framedata->jb_impl->create(&framedata->jb_conf); return 0; }
static int at_response_orig (pvt_t* pvt, char* str, attribute_unused size_t len) { int call_index = 1; int call_type = 0; channel_queue_control (pvt, AST_CONTROL_PROGRESS); if (at_send_ddsetex (pvt) || at_fifo_queue_add (pvt, CMD_AT_DDSETEX, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error sending AT^DDSETEX\n", pvt->id); return -1; } if (pvt->a_timer) { ast_timer_set_rate (pvt->a_timer, 50); } /* * parse ORIG info in the following format: * ^ORIG:<call_index>,<call_type> */ if (!sscanf (str, "^ORIG:%d,%d", &call_index, &call_type)) { ast_log (LOG_ERROR, "[%s] Error parsing ORIG event '%s'\n", pvt->id, str); } ast_debug (1, "[%s] Received call_index: %d\n", pvt->id, call_index); ast_debug (1, "[%s] Received call_type: %d\n", pvt->id, call_type); return 0; }
/* set defaults */ static int jb_framedata_init(struct jb_framedata *framedata, const char *data, const char *value) { int jb_impl_type = DEFAULT_TYPE; /* Initialize defaults */ framedata->timer_fd = -1; jb_conf_default(&framedata->jb_conf); if (!(framedata->jb_impl = ast_jb_get_impl(jb_impl_type))) { return -1; } if (!(framedata->timer = ast_timer_open())) { return -1; } framedata->timer_fd = ast_timer_fd(framedata->timer); framedata->timer_interval = DEFAULT_TIMER_INTERVAL; ast_timer_set_rate(framedata->timer, 1000 / framedata->timer_interval); framedata->start_tv = ast_tvnow(); /* Now check user options to see if any of the defaults need to change. */ if (!ast_strlen_zero(data)) { if (!strcasecmp(data, "fixed")) { jb_impl_type = AST_JB_FIXED; } else if (!strcasecmp(data, "adaptive")) { jb_impl_type = AST_JB_ADAPTIVE; } else { ast_log(LOG_WARNING, "Unknown Jitterbuffer type %s. Failed to create jitterbuffer.\n", data); return -1; } ast_copy_string(framedata->jb_conf.impl, data, sizeof(framedata->jb_conf.impl)); } if (!ast_strlen_zero(value) && strcasecmp(value, "default")) { char *parse = ast_strdupa(value); int res = 0; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(max_size); AST_APP_ARG(resync_threshold); AST_APP_ARG(target_extra); );
static inline int at_response_ok (pvt_t* pvt) { at_queue_t* e; if ((e = at_fifo_queue_head (pvt)) && (e->res == RES_OK || e->res == RES_CMGR)) { switch (e->cmd) { /* initilization stuff */ case CMD_AT_Z: if (at_send_ate0 (pvt) || at_fifo_queue_add (pvt, CMD_AT_E, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error disabling echo\n", pvt->id); goto e_return; } break; case CMD_AT_E: if (!pvt->initialized) { if (at_send_curc (pvt) || at_fifo_queue_add (pvt, CMD_AT_CURC, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error setting service messages\n", pvt->id); goto e_return; } } break; case CMD_AT_CURC: if (!pvt->initialized) { if (pvt->u2diag != -1) { if (at_send_u2diag (pvt, pvt->u2diag) || at_fifo_queue_add (pvt, CMD_AT_U2DIAG, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error setting U2DIAG\n", pvt->id); goto e_return; } } else { if (at_send_cgmi (pvt) || at_fifo_queue_add (pvt, CMD_AT_CGMI, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error asking datacard for manufacturer info\n", pvt->id); goto e_return; } } } break; case CMD_AT_U2DIAG: if (!pvt->initialized) { if (at_send_cgmi (pvt) || at_fifo_queue_add (pvt, CMD_AT_CGMI, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error asking datacard for manufacturer info\n", pvt->id); goto e_return; } } break; case CMD_AT_CGMI: if (!pvt->initialized) { if (at_send_cgmm (pvt) || at_fifo_queue_add (pvt, CMD_AT_CGMM, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error asking datacard for model info\n", pvt->id); goto e_return; } } break; case CMD_AT_CGMM: if (!pvt->initialized) { if (at_send_cgmr (pvt) || at_fifo_queue_add (pvt, CMD_AT_CGMR, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error asking datacard for firmware info\n", pvt->id); goto e_return; } } break; case CMD_AT_CGMR: if (!pvt->initialized) { if (at_send_cmee (pvt, 0) || at_fifo_queue_add (pvt, CMD_AT_CMEE, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error setting error verbosity level\n", pvt->id); goto e_return; } } break; case CMD_AT_CMEE: if (!pvt->initialized) { if (at_send_cgsn (pvt) || at_fifo_queue_add (pvt, CMD_AT_CGSN, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error asking datacard for IMEI number\n", pvt->id); goto e_return; } } break; case CMD_AT_CGSN: if (!pvt->initialized) { if (at_send_cimi (pvt) || at_fifo_queue_add (pvt, CMD_AT_CIMI, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error asking datacard for IMSI number\n", pvt->id); goto e_return; } } break; case CMD_AT_CIMI: if (!pvt->initialized) { if (at_send_cpin_test (pvt) || at_fifo_queue_add (pvt, CMD_AT_CPIN, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error asking datacard for PIN state\n", pvt->id); goto e_return; } } break; case CMD_AT_CPIN: if (!pvt->initialized) { if (at_send_cops_init (pvt, 0, 0) || at_fifo_queue_add (pvt, CMD_AT_COPS_INIT, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error setting operator select parameters\n", pvt->id); goto e_return; } } break; case CMD_AT_COPS_INIT: ast_debug (1, "[%s] Operator select parameters set\n", pvt->id); if (!pvt->initialized) { if (at_send_creg_init (pvt, 2) || at_fifo_queue_add (pvt, CMD_AT_CREG_INIT, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error enabeling registration info\n", pvt->id); goto e_return; } } break; case CMD_AT_CREG_INIT: ast_debug (1, "[%s] registration info enabled\n", pvt->id); if (!pvt->initialized) { if (at_send_creg (pvt) || at_fifo_queue_add (pvt, CMD_AT_CREG, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error sending registration query\n", pvt->id); goto e_return; } } break; case CMD_AT_CREG: ast_debug (1, "[%s] registration query sent\n", pvt->id); if (!pvt->initialized) { if (at_send_cnum (pvt) || at_fifo_queue_add (pvt, CMD_AT_CNUM, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error checking subscriber phone number\n", pvt->id); goto e_return; } } break; case CMD_AT_CNUM: ast_debug (1, "[%s] Subscriber phone number query successed\n", pvt->id); if (!pvt->initialized) { if (at_send_cvoice_test (pvt) || at_fifo_queue_add (pvt, CMD_AT_CVOICE, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error checking voice capabilities\n", pvt->id); goto e_return; } } break; case CMD_AT_CVOICE: ast_debug (1, "[%s] Datacard has voice support\n", pvt->id); pvt->has_voice = 1; if (!pvt->initialized) { if (at_send_clip (pvt, 1) || at_fifo_queue_add (pvt, CMD_AT_CLIP, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error enabling calling line notification\n", pvt->id); goto e_return; } } break; case CMD_AT_CLIP: ast_debug (1, "[%s] Calling line indication enabled\n", pvt->id); if (!pvt->initialized) { if (at_send_cssn (pvt, 1, 1) || at_fifo_queue_add (pvt, CMD_AT_CSSN, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error activating Supplementary Service Notification\n", pvt->id); goto e_return; } } break; case CMD_AT_CSSN: ast_debug (1, "[%s] Supplementary Service Notification enabled successful\n", pvt->id); if (!pvt->initialized) { /* set the SMS operating mode to text mode */ if (at_send_cmgf (pvt, 1) || at_fifo_queue_add (pvt, CMD_AT_CMGF, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error setting CMGF\n", pvt->id); goto e_return; } } break; case CMD_AT_CMGF: ast_debug (1, "[%s] SMS text mode enabled\n", pvt->id); if (!pvt->initialized) { /* set text encoding to UCS-2 */ if (at_send_cscs (pvt, "UCS2") || at_fifo_queue_add (pvt, CMD_AT_CSCS, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error setting CSCS (text encoding)\n", pvt->id); goto e_return; } } break; case CMD_AT_CSCS: ast_debug (1, "[%s] UCS-2 text encoding enabled\n", pvt->id); pvt->use_ucs2_encoding = 1; if (!pvt->initialized) { /* set SMS storage location */ if (at_send_cpms (pvt) || at_fifo_queue_add (pvt, CMD_AT_CPMS, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error setting CPMS\n", pvt->id); goto e_return; } } break; case CMD_AT_CPMS: ast_debug (1, "[%s] SMS storage location is established\n", pvt->id); if (!pvt->initialized) { /* turn on SMS new message indication */ if (at_send_cnmi (pvt) || at_fifo_queue_add (pvt, CMD_AT_CNMI, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error sending CNMI\n", pvt->id); goto e_return; } } break; case CMD_AT_CNMI: ast_debug (1, "[%s] SMS new message indication enabled\n", pvt->id); ast_debug (1, "[%s] Datacard has sms support\n", pvt->id); pvt->has_sms = 1; if (!pvt->initialized) { if (at_send_csq (pvt) || at_fifo_queue_add (pvt, CMD_AT_CSQ, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error querying signal strength\n", pvt->id); goto e_return; } } break; case CMD_AT_CSQ: ast_debug (1, "[%s] Got signal strength result\n", pvt->id); if (!pvt->initialized) { ast_verb (3, "Datacard %s initialized and ready\n", pvt->id); pvt->timeout = 7000; pvt->initialized = 1; if (pvt->has_sms && pvt->auto_delete_sms) { if (at_send_cmgd (pvt, 1, 4) || at_fifo_queue_add (pvt, CMD_AT_CMGD, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error delete SMS messages\n", pvt->id); goto e_return; } } } break; /* end initilization stuff */ case CMD_AT_A: ast_debug (1, "[%s] Answer sent successfully\n", pvt->id); if (at_send_ddsetex (pvt) || at_fifo_queue_add (pvt, CMD_AT_DDSETEX, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error sending AT^DDSETEX\n", pvt->id); goto e_return; } if (pvt->a_timer) { ast_timer_set_rate (pvt->a_timer, 50); } break; case CMD_AT_CLIR: ast_debug (1, "[%s] CLIR sent successfully\n", pvt->id); if (e->ptype != 0 || at_send_atd (pvt, e->param.data) || at_fifo_queue_add (pvt, CMD_AT_D, RES_OK)) { ast_log (LOG_ERROR, "[%s] Error sending ATD command\n", pvt->id); goto e_return; } break; case CMD_AT_D: ast_debug (1, "[%s] Dial sent successfully\n", pvt->id); break; case CMD_AT_DDSETEX: ast_debug (1, "[%s] AT^DDSETEX sent successfully\n", pvt->id); break; case CMD_AT_CHUP: ast_debug (1, "[%s] Successful hangup\n", pvt->id); break; case CMD_AT_CMGS: ast_debug (1, "[%s] Successfully sent sms message\n", pvt->id); pvt->outgoing_sms = 0; break; case CMD_AT_DTMF: ast_debug (1, "[%s] DTMF sent successfully\n", pvt->id); break; case CMD_AT_CUSD: ast_debug (1, "[%s] CUSD code sent successfully\n", pvt->id); break; case CMD_AT_COPS: ast_debug (1, "[%s] Provider query successfully\n", pvt->id); break; case CMD_AT_CMGR: ast_debug (1, "[%s] SMS message see later\n", pvt->id); break; case CMD_AT_CMGD: ast_debug (1, "[%s] SMS message deleted successfully\n", pvt->id); break; case CMD_AT_CCWA: ast_log (LOG_NOTICE, "[%s] Call-Waiting disabled.\n", pvt->id); break; case CMD_AT_CFUN: ast_debug (1, "[%s] CFUN sent successfully\n", pvt->id); break; case CMD_AT_CLVL: ast_debug (1, "[%s] Audio level is set\n", pvt->id); break; default: ast_log (LOG_ERROR, "[%s] Received 'OK' for unhandled command '%s'\n", pvt->id, at_cmd2str (e->cmd)); break; } at_fifo_queue_rem (pvt); } else if (e) { ast_log (LOG_ERROR, "[%s] Received 'OK' when expecting '%s', ignoring\n", pvt->id, at_res2str (e->res)); } else { ast_log (LOG_ERROR, "[%s] Received unexpected 'OK'\n", pvt->id); } return 0; e_return: at_fifo_queue_rem (pvt); return -1; }
static char *timing_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ast_timer *timer; int count = 0; struct timeval start, end; unsigned int test_rate = 50; switch (cmd) { case CLI_INIT: e->command = "timing test"; e->usage = "Usage: timing test <rate>\n" " Test a timer with a specified rate, 50/sec by default.\n" ""; return NULL; case CLI_GENERATE: return NULL; } if (a->argc != 2 && a->argc != 3) { return CLI_SHOWUSAGE; } if (a->argc == 3) { unsigned int rate; if (sscanf(a->argv[2], "%30u", &rate) == 1) { test_rate = rate; } else { ast_cli(a->fd, "Invalid rate '%s', using default of %u\n", a->argv[2], test_rate); } } ast_cli(a->fd, "Attempting to test a timer with %u ticks per second.\n", test_rate); if (!(timer = ast_timer_open())) { ast_cli(a->fd, "Failed to open timing fd\n"); return CLI_FAILURE; } ast_cli(a->fd, "Using the '%s' timing module for this test.\n", timer->holder->iface->name); start = ast_tvnow(); ast_timer_set_rate(timer, test_rate); while (ast_tvdiff_ms((end = ast_tvnow()), start) < 1000) { int res; struct pollfd pfd = { .fd = ast_timer_fd(timer), .events = POLLIN | POLLPRI, }; res = ast_poll(&pfd, 1, 100); if (res == 1) { count++; if (ast_timer_ack(timer, 1) < 0) { ast_cli(a->fd, "Timer failed to acknowledge.\n"); ast_timer_close(timer); return CLI_FAILURE; } } else if (!res) { ast_cli(a->fd, "poll() timed out! This is bad.\n"); } else if (errno != EAGAIN && errno != EINTR) { ast_cli(a->fd, "poll() returned error: %s\n", strerror(errno)); } } ast_timer_close(timer); timer = NULL; ast_cli(a->fd, "It has been %" PRIi64 " milliseconds, and we got %d timer ticks\n", ast_tvdiff_ms(end, start), count); return CLI_SUCCESS; } static struct ast_cli_entry cli_timing[] = { AST_CLI_DEFINE(timing_test, "Run a timing test"), }; static void timing_shutdown(void) { ast_cli_unregister_multiple(cli_timing, ARRAY_LEN(cli_timing)); ast_heap_destroy(timing_interfaces); timing_interfaces = NULL; }
struct ast_channel *stasis_app_control_snoop(struct ast_channel *chan, enum stasis_app_snoop_direction spy, enum stasis_app_snoop_direction whisper, const char *app, const char *app_args, const char *snoop_id) { RAII_VAR(struct stasis_app_snoop *, snoop, NULL, ao2_cleanup); struct ast_format_cap *caps; pthread_t thread; struct ast_assigned_ids assignedids = { .uniqueid = snoop_id, }; if (spy == STASIS_SNOOP_DIRECTION_NONE && whisper == STASIS_SNOOP_DIRECTION_NONE) { return NULL; } snoop = ao2_alloc_options(sizeof(*snoop), snoop_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!snoop) { return NULL; } /* Allocate a buffer to store the Stasis application and arguments in */ snoop->app = ast_str_create(64); if (!snoop->app) { return NULL; } ast_str_set(&snoop->app, 0, "%s", app); if (!ast_strlen_zero(app_args)) { ast_str_append(&snoop->app, 0, ",%s", app_args); } /* Set up a timer for the Snoop channel so it wakes up at a specific interval */ snoop->timer = ast_timer_open(); if (!snoop->timer) { return NULL; } ast_timer_set_rate(snoop->timer, 1000 / SNOOP_INTERVAL); /* Determine which signed linear format should be used */ snoop_determine_format(chan, snoop); /* Allocate a Snoop channel and set up various parameters */ snoop->chan = ast_channel_alloc(1, AST_STATE_UP, "", "", "", "", "", &assignedids, NULL, 0, "Snoop/%s-%08x", ast_channel_uniqueid(chan), (unsigned)ast_atomic_fetchadd_int((int *)&chan_idx, +1)); if (!snoop->chan) { return NULL; } ast_copy_string(snoop->uniqueid, ast_channel_uniqueid(chan), sizeof(snoop->uniqueid)); /* To keep the channel valid on the Snoop structure until it is destroyed we bump the ref up here */ ast_channel_ref(snoop->chan); ast_channel_tech_set(snoop->chan, &snoop_tech); ao2_ref(snoop, +1); ast_channel_tech_pvt_set(snoop->chan, snoop); ast_channel_set_fd(snoop->chan, 0, ast_timer_fd(snoop->timer)); /* The format on the Snoop channel will be this signed linear format, and it will never change */ caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); if (!caps) { ast_channel_unlock(snoop->chan); ast_hangup(snoop->chan); return NULL; } ast_format_cap_append(caps, snoop->spy_format, 0); ast_channel_nativeformats_set(snoop->chan, caps); ao2_ref(caps, -1); ast_channel_set_writeformat(snoop->chan, snoop->spy_format); ast_channel_set_rawwriteformat(snoop->chan, snoop->spy_format); ast_channel_set_readformat(snoop->chan, snoop->spy_format); ast_channel_set_rawreadformat(snoop->chan, snoop->spy_format); ast_channel_unlock(snoop->chan); if (spy != STASIS_SNOOP_DIRECTION_NONE) { if (snoop_setup_audiohook(chan, AST_AUDIOHOOK_TYPE_SPY, spy, &snoop->spy_direction, &snoop->spy)) { ast_hangup(snoop->chan); return NULL; } snoop->spy_samples = ast_format_get_sample_rate(snoop->spy_format) / (1000 / SNOOP_INTERVAL); snoop->spy_active = 1; } /* If whispering is enabled set up the audiohook */ if (whisper != STASIS_SNOOP_DIRECTION_NONE) { if (snoop_setup_audiohook(chan, AST_AUDIOHOOK_TYPE_WHISPER, whisper, &snoop->whisper_direction, &snoop->whisper)) { ast_hangup(snoop->chan); return NULL; } snoop->whisper_active = 1; } /* Create the thread which services the Snoop channel */ ao2_ref(snoop, +1); if (ast_pthread_create_detached_background(&thread, NULL, snoop_stasis_thread, snoop)) { ao2_cleanup(snoop); /* No other thread is servicing this channel so we can immediately hang it up */ ast_hangup(snoop->chan); return NULL; } publish_chanspy_message(snoop, 1); /* The caller of this has a reference as well */ return ast_channel_ref(snoop->chan); }
static struct ast_frame *hook_event_cb(struct ast_channel *chan, struct ast_frame *frame, enum ast_framehook_event event, void *data) { struct jb_framedata *framedata = data; struct timeval now_tv; unsigned long now; int putframe = 0; /* signifies if audio frame was placed into the buffer or not */ switch (event) { case AST_FRAMEHOOK_EVENT_READ: break; case AST_FRAMEHOOK_EVENT_ATTACHED: case AST_FRAMEHOOK_EVENT_DETACHED: case AST_FRAMEHOOK_EVENT_WRITE: return frame; } if (ast_channel_fdno(chan) == AST_JITTERBUFFER_FD && framedata->timer) { if (ast_timer_ack(framedata->timer, 1) < 0) { ast_log(LOG_ERROR, "Failed to acknowledge timer in jitter buffer\n"); return frame; } } if (!frame) { return frame; } now_tv = ast_tvnow(); now = ast_tvdiff_ms(now_tv, framedata->start_tv); if (frame->frametype == AST_FRAME_VOICE) { int res; struct ast_frame *jbframe; if (!ast_test_flag(frame, AST_FRFLAG_HAS_TIMING_INFO) || frame->len < 2 || frame->ts < 0) { /* only frames with timing info can enter the jitterbuffer */ return frame; } jbframe = ast_frisolate(frame); ao2_replace(framedata->last_format, frame->subclass.format); if (frame->len && (frame->len != framedata->timer_interval)) { framedata->timer_interval = frame->len; ast_timer_set_rate(framedata->timer, 1000 / framedata->timer_interval); } if (!framedata->first) { framedata->first = 1; res = framedata->jb_impl->put_first(framedata->jb_obj, jbframe, now); } else { res = framedata->jb_impl->put(framedata->jb_obj, jbframe, now); } if (res == AST_JB_IMPL_OK) { if (jbframe != frame) { ast_frfree(frame); } frame = &ast_null_frame; } else if (jbframe != frame) { ast_frfree(jbframe); } putframe = 1; } if (frame->frametype == AST_FRAME_NULL) { int res; long next = framedata->jb_impl->next(framedata->jb_obj); /* If now is earlier than the next expected output frame * from the jitterbuffer we may choose to pass on retrieving * a frame during this read iteration. The only exception * to this rule is when an audio frame is placed into the buffer * and the time for the next frame to come out of the buffer is * at least within the timer_interval of the next output frame. By * doing this we are able to feed off the timing of the input frames * and only rely on our jitterbuffer timer when frames are dropped. * During testing, this hybrid form of timing gave more reliable results. */ if (now < next) { long int diff = next - now; if (!putframe) { return frame; } else if (diff >= framedata->timer_interval) { return frame; } } ast_frfree(frame); frame = &ast_null_frame; res = framedata->jb_impl->get(framedata->jb_obj, &frame, now, framedata->timer_interval); switch (res) { case AST_JB_IMPL_OK: /* got it, and pass it through */ break; case AST_JB_IMPL_DROP: ast_frfree(frame); frame = &ast_null_frame; break; case AST_JB_IMPL_INTERP: if (framedata->last_format) { struct ast_frame tmp = { 0, }; tmp.frametype = AST_FRAME_VOICE; tmp.subclass.format = framedata->last_format; /* example: 8000hz / (1000 / 20ms) = 160 samples */ tmp.samples = ast_format_get_sample_rate(framedata->last_format) / (1000 / framedata->timer_interval); tmp.delivery = ast_tvadd(framedata->start_tv, ast_samp2tv(next, 1000)); tmp.offset = AST_FRIENDLY_OFFSET; tmp.src = "func_jitterbuffer interpolation"; ast_frfree(frame); frame = ast_frdup(&tmp); break; } /* else fall through */ case AST_JB_IMPL_NOFRAME: ast_frfree(frame); frame = &ast_null_frame; break; } } if (frame->frametype == AST_FRAME_CONTROL) { switch(frame->subclass.integer) { case AST_CONTROL_HOLD: case AST_CONTROL_UNHOLD: case AST_CONTROL_T38_PARAMETERS: case AST_CONTROL_SRCUPDATE: case AST_CONTROL_SRCCHANGE: framedata->jb_impl->force_resync(framedata->jb_obj); break; default: break; } } return frame; }