diff --git a/doc/CHANGES-staging/func_jitterbuffer_video.txt b/doc/CHANGES-staging/func_jitterbuffer_video.txt new file mode 100644 index 0000000000000000000000000000000000000000..02f9b0b5df178e7a176820f04c563f4c55b51ac3 --- /dev/null +++ b/doc/CHANGES-staging/func_jitterbuffer_video.txt @@ -0,0 +1,6 @@ +Subject: func_jitterbuffer + +The JITTERBUFFER dialplan function now has an option to enable video synchronization +support. When enabled and used with a compatible channel driver (chan_sip, chan_pjsip) +the video is buffered according to the size of the audio jitterbuffer and is +synchronized to the audio. diff --git a/funcs/func_jitterbuffer.c b/funcs/func_jitterbuffer.c index e4c68eda781af5bd5693c292c8431b90d1b9489e..9daa6ca8a67905dfc37402ec5ecf0940f7230968 100644 --- a/funcs/func_jitterbuffer.c +++ b/funcs/func_jitterbuffer.c @@ -62,8 +62,9 @@ </syntax> <description> <para>Jitterbuffers are constructed in two different ways. - The first always take three arguments: <replaceable>max_size</replaceable>, - <replaceable>resync_threshold</replaceable>, and <replaceable>target_extra</replaceable>. + The first always take four arguments: <replaceable>max_size</replaceable>, + <replaceable>resync_threshold</replaceable>, <replaceable>target_extra</replaceable>, + and <replaceable>sync_video</replaceable>. Alternatively, a single argument of <literal>default</literal> can be provided, which will construct the default jitterbuffer for the given <replaceable>jitterbuffer type</replaceable>.</para> @@ -76,12 +77,17 @@ <para>target_extra: This option only affects the adaptive jitterbuffer. It represents the amount time in milliseconds by which the new jitter buffer will pad its size. Defaults to 40ms.</para> + <para>sync_video: This option enables video synchronization with the audio stream. It can be + turned on and off. Defaults to off.</para> <example title="Fixed with defaults" language="text"> exten => 1,1,Set(JITTERBUFFER(fixed)=default) </example> <example title="Fixed with 200ms max size" language="text"> exten => 1,1,Set(JITTERBUFFER(fixed)=200) </example> + <example title="Fixed with 200ms max size and video sync support" language="text"> + exten => 1,1,Set(JITTERBUFFER(fixed)=200,,,yes) + </example> <example title="Fixed with 200ms max size, resync threshold 1500" language="text"> exten => 1,1,Set(JITTERBUFFER(fixed)=200,1500) </example> @@ -91,6 +97,9 @@ <example title="Adaptive with 200ms max size, 60ms target extra" language="text"> exten => 1,1,Set(JITTERBUFFER(adaptive)=200,,60) </example> + <example title="Adaptive with 200ms max size and video sync support" language="text"> + exten => 1,1,Set(JITTERBUFFER(adaptive)=200,,,yes) + </example> <example title="Set a fixed jitterbuffer with defaults; then remove it" language="text"> exten => 1,1,Set(JITTERBUFFER(fixed)=default) exten => 1,n,Set(JITTERBUFFER(disabled)=) @@ -133,6 +142,7 @@ static int jb_helper(struct ast_channel *chan, const char *cmd, char *data, cons AST_APP_ARG(max_size); AST_APP_ARG(resync_threshold); AST_APP_ARG(target_extra); + AST_APP_ARG(sync_video); ); AST_STANDARD_APP_ARGS(args, parse); @@ -151,6 +161,11 @@ static int jb_helper(struct ast_channel *chan, const char *cmd, char *data, cons "jbtargetextra", args.target_extra); } + if (!ast_strlen_zero(args.sync_video)) { + res |= ast_jb_read_conf(&jb_conf, + "jbsyncvideo", + args.sync_video); + } if (res) { ast_log(LOG_WARNING, "Invalid jitterbuffer parameters %s\n", value); } diff --git a/include/asterisk/abstract_jb.h b/include/asterisk/abstract_jb.h index b300d1295f512f0c0571a67100cf3eee9846baa6..3e2467de6bd218c20c83bc2938171f19ede55316 100644 --- a/include/asterisk/abstract_jb.h +++ b/include/asterisk/abstract_jb.h @@ -44,7 +44,8 @@ struct ast_frame; enum { AST_JB_ENABLED = (1 << 0), AST_JB_FORCED = (1 << 1), - AST_JB_LOG = (1 << 2) + AST_JB_LOG = (1 << 2), + AST_JB_SYNC_VIDEO = (1 << 3) }; enum ast_jb_type { @@ -89,6 +90,7 @@ struct ast_jb_conf #define AST_JB_CONF_TARGET_EXTRA "targetextra" #define AST_JB_CONF_IMPL "impl" #define AST_JB_CONF_LOG "log" +#define AST_JB_CONF_SYNC_VIDEO "syncvideo" /* Hooks for the abstract jb implementation */ /*! \brief Create */ diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h index 57f29b4d77b487303f14655e5cbb691540bdc736..758fad7d9683f8baad7320ff38f4db665ad8d833 100644 --- a/include/asterisk/rtp_engine.h +++ b/include/asterisk/rtp_engine.h @@ -2800,6 +2800,17 @@ struct ast_json *ast_rtp_convert_stats_json(const struct ast_rtp_instance_stats */ struct ast_json *ast_rtp_instance_get_stats_all_json(struct ast_rtp_instance *instance); +/*! + * \brief Retrieve the sample rate of a format according to RTP specifications + * \since 16.7.0 + * \since 17.1.0 + * + * \param format The media format + * + * \retval The sample rate + */ +int ast_rtp_get_rate(const struct ast_format *format); + /*! * \since 12 * \brief \ref stasis topic for RTP and RTCP related messages diff --git a/main/abstract_jb.c b/main/abstract_jb.c index b375739a459d7a2b18d64f26442cd2126f6cbc22..7549008cdae33905e1f8be8b352045e47c0f1a2c 100644 --- a/main/abstract_jb.c +++ b/main/abstract_jb.c @@ -41,6 +41,8 @@ #include "asterisk/utils.h" #include "asterisk/pbx.h" #include "asterisk/timing.h" +#include "asterisk/rtp_engine.h" +#include "asterisk/format_cache.h" #include "asterisk/abstract_jb.h" #include "fixedjitterbuf.h" @@ -53,6 +55,9 @@ enum { JB_CREATED = (1 << 2) }; +/*! The maximum size we allow the early frame buffer to get */ +#define MAXIMUM_EARLY_FRAME_COUNT 200 + /* Implementation functions */ /* fixed */ @@ -568,6 +573,8 @@ int ast_jb_read_conf(struct ast_jb_conf *conf, const char *varname, const char * } } else if (!strcasecmp(name, AST_JB_CONF_LOG)) { ast_set2_flag(conf, ast_true(value), AST_JB_LOG); + } else if (!strcasecmp(name, AST_JB_CONF_SYNC_VIDEO)) { + ast_set2_flag(conf, ast_true(value), AST_JB_SYNC_VIDEO); } else { return -1; } @@ -832,6 +839,11 @@ static int jb_is_late_adaptive(void *jb, long ts) #define DEFAULT_RESYNC 1000 #define DEFAULT_TYPE AST_JB_FIXED +struct jb_stream_sync { + unsigned int timestamp; + struct timeval ntp; +}; + struct jb_framedata { const struct ast_jb_impl *jb_impl; struct ast_jb_conf jb_conf; @@ -841,11 +853,21 @@ struct jb_framedata { int timer_interval; /* ms between deliveries */ int timer_fd; int first; + int audio_stream_id; + struct jb_stream_sync audio_stream_sync; + int video_stream_id; + struct jb_stream_sync video_stream_sync; + AST_LIST_HEAD_NOLOCK(, ast_frame) early_frames; + unsigned int early_frame_count; + struct timeval last_audio_ntp_timestamp; + int audio_flowing; void *jb_obj; }; static void jb_framedata_destroy(struct jb_framedata *framedata) { + struct ast_frame *frame; + if (framedata->timer) { ast_timer_close(framedata->timer); framedata->timer = NULL; @@ -859,11 +881,15 @@ static void jb_framedata_destroy(struct jb_framedata *framedata) framedata->jb_obj = NULL; } ao2_cleanup(framedata->last_format); + while ((frame = AST_LIST_REMOVE_HEAD(&framedata->early_frames, frame_list))) { + ast_frfree(frame); + } ast_free(framedata); } void ast_jb_conf_default(struct ast_jb_conf *conf) { + ast_clear_flag(conf, AST_FLAGS_ALL); conf->max_size = DEFAULT_SIZE; conf->resync_threshold = DEFAULT_RESYNC; ast_copy_string(conf->impl, "fixed", sizeof(conf->impl)); @@ -886,6 +912,44 @@ static void hook_destroy_cb(void *framedata) jb_framedata_destroy((struct jb_framedata *) framedata); } +static struct timeval jitterbuffer_frame_get_ntp_timestamp(const struct jb_stream_sync *stream_sync, const struct ast_frame *frame) +{ + int timestamp_diff; + unsigned int rate; + + /* It's possible for us to receive frames before we receive the information allowing + * us to do NTP/RTP timestamp calculations. Since the information isn't available we + * can't generate one and give an empty timestamp. + */ + if (ast_tvzero(stream_sync->ntp)) { + return ast_tv(0, 0); + } + + /* Convert the Asterisk timestamp into an RTP timestamp, and then based on the difference we can + * determine how many samples are in the frame and how long has elapsed since the synchronization + * RTP and NTP timestamps were received giving us the NTP timestamp for this frame. + */ + if (frame->frametype == AST_FRAME_VOICE) { + rate = ast_rtp_get_rate(frame->subclass.format); + timestamp_diff = (frame->ts * (rate / 1000)) - stream_sync->timestamp; + } else { + /* Video is special - internally we reference it as 1000 to preserve the RTP timestamp but + * it is actualy 90000, this is why we can just directly subtract the timestamp. + */ + rate = 90000; + timestamp_diff = frame->ts - stream_sync->timestamp; + } + + if (timestamp_diff < 0) { + /* It's possible for us to be asked for an NTP timestamp from before our latest + * RTCP SR report. To handle this we subtract so we go back in time. + */ + return ast_tvsub(stream_sync->ntp, ast_samp2tv(abs(timestamp_diff), rate)); + } else { + return ast_tvadd(stream_sync->ntp, ast_samp2tv(timestamp_diff, rate)); + } +} + 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; @@ -928,6 +992,77 @@ static struct ast_frame *hook_event_cb(struct ast_channel *chan, struct ast_fram return frame; } + if (ast_test_flag(&framedata->jb_conf, AST_JB_SYNC_VIDEO)) { + if (frame->frametype == AST_FRAME_VOICE) { + /* Store the stream identifier for the audio stream so we can associate the incoming RTCP SR + * with the correct stream sync structure. + */ + framedata->audio_stream_id = frame->stream_num; + } else if (frame->frametype == AST_FRAME_RTCP && frame->subclass.integer == AST_RTP_RTCP_SR) { + struct ast_rtp_rtcp_report *rtcp_report = frame->data.ptr; + struct jb_stream_sync *stream_sync = NULL; + + /* Determine which stream this RTCP is in regards to */ + if (framedata->audio_stream_id == frame->stream_num) { + stream_sync = &framedata->audio_stream_sync; + } else if (framedata->video_stream_id == frame->stream_num) { + stream_sync = &framedata->video_stream_sync; + } + + if (stream_sync) { + /* Store the RTP and NTP timestamp mapping so we can derive an NTP timestamp for each frame */ + stream_sync->timestamp = rtcp_report->sender_information.rtp_timestamp; + stream_sync->ntp = rtcp_report->sender_information.ntp_timestamp; + } + } else if (frame->frametype == AST_FRAME_VIDEO) { + /* If a video frame is late according to the audio timestamp don't stash it away, just return it. + * If however it is ahead then we keep it until such time as the audio catches up. + */ + struct ast_frame *jbframe; + + framedata->video_stream_id = frame->stream_num; + + /* If no timing information is available we can't store this away, so just let it through now */ + if (!ast_test_flag(frame, AST_FRFLAG_HAS_TIMING_INFO)) { + return frame; + } + + /* To ensure that the video starts when the audio starts we only start allowing frames through once + * audio starts flowing. + */ + if (framedata->audio_flowing) { + struct timeval video_timestamp; + + video_timestamp = jitterbuffer_frame_get_ntp_timestamp(&framedata->video_stream_sync, frame); + if (ast_tvdiff_ms(framedata->last_audio_ntp_timestamp, video_timestamp) >= 0) { + return frame; + } + } + + /* To prevent the early frame buffer from growing uncontrolled we impose a maximum count that it can + * get to. If this is reached then we drop a video frame, which should cause the receiver to ask for a + * new key frame. + */ + if (framedata->early_frame_count == MAXIMUM_EARLY_FRAME_COUNT) { + jbframe = AST_LIST_REMOVE_HEAD(&framedata->early_frames, frame_list); + framedata->early_frame_count--; + ast_frfree(jbframe); + } + + jbframe = ast_frisolate(frame); + if (!jbframe) { + /* If we can't isolate the frame the safest thing we can do is return it, even if the A/V sync + * may be off. + */ + return frame; + } + + AST_LIST_INSERT_TAIL(&framedata->early_frames, jbframe, frame_list); + framedata->early_frame_count++; + return &ast_null_frame; + } + } + now_tv = ast_tvnow(); now = ast_tvdiff_ms(now_tv, framedata->start_tv); @@ -1022,6 +1157,8 @@ static struct ast_frame *hook_event_cb(struct ast_channel *chan, struct ast_fram } if (frame->frametype == AST_FRAME_CONTROL) { + struct ast_frame *early_frame; + switch(frame->subclass.integer) { case AST_CONTROL_HOLD: case AST_CONTROL_UNHOLD: @@ -1029,12 +1166,50 @@ static struct ast_frame *hook_event_cb(struct ast_channel *chan, struct ast_fram case AST_CONTROL_SRCUPDATE: case AST_CONTROL_SRCCHANGE: framedata->jb_impl->force_resync(framedata->jb_obj); + /* Since we are resyncing go ahead and clear out the video frames too */ + while ((early_frame = AST_LIST_REMOVE_HEAD(&framedata->early_frames, frame_list))) { + ast_frfree(early_frame); + } + framedata->audio_flowing = 0; + framedata->early_frame_count = 0; break; default: break; } } + /* If a voice frame is being passed through see if we need to add any additional frames to it */ + if (ast_test_flag(&framedata->jb_conf, AST_JB_SYNC_VIDEO) && frame->frametype == AST_FRAME_VOICE) { + AST_LIST_HEAD_NOLOCK(, ast_frame) additional_frames; + struct ast_frame *early_frame; + + /* We store the last NTP timestamp for the audio given to the core so that subsequents frames which + * are late can be passed immediately through (this will occur for video frames which are returned here) + */ + framedata->last_audio_ntp_timestamp = jitterbuffer_frame_get_ntp_timestamp(&framedata->audio_stream_sync, frame); + framedata->audio_flowing = 1; + + AST_LIST_HEAD_INIT_NOLOCK(&additional_frames); + + AST_LIST_TRAVERSE_SAFE_BEGIN(&framedata->early_frames, early_frame, frame_list) { + struct timeval early_timestamp = jitterbuffer_frame_get_ntp_timestamp(&framedata->video_stream_sync, early_frame); + int diff = ast_tvdiff_ms(framedata->last_audio_ntp_timestamp, early_timestamp); + + /* If this frame is from the past we need to include it with the audio frame that is going + * out. + */ + if (diff >= 0) { + AST_LIST_REMOVE_CURRENT(frame_list); + framedata->early_frame_count--; + AST_LIST_INSERT_TAIL(&additional_frames, early_frame, frame_list); + } + } + AST_LIST_TRAVERSE_SAFE_END; + + /* Append any additional frames we may want to include (such as video) */ + AST_LIST_NEXT(frame, frame_list) = AST_LIST_FIRST(&additional_frames); + } + return frame; } @@ -1066,6 +1241,9 @@ static int jb_framedata_init(struct jb_framedata *framedata, struct ast_jb_conf return -1; } + framedata->audio_stream_id = -1; + framedata->video_stream_id = -1; + AST_LIST_HEAD_INIT_NOLOCK(&framedata->early_frames); framedata->timer_fd = ast_timer_fd(framedata->timer); framedata->timer_interval = DEFAULT_TIMER_INTERVAL; ast_timer_set_rate(framedata->timer, 1000 / framedata->timer_interval); diff --git a/main/rtp_engine.c b/main/rtp_engine.c index ac429ca233cbc54dd467233c4a08524fd825c01d..95510d8b6ca3bb124702704c1b306cda02bbec1c 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -3958,3 +3958,12 @@ struct ast_json *ast_rtp_instance_get_stats_all_json(struct ast_rtp_instance *in return ast_rtp_convert_stats_json(&stats); } + +int ast_rtp_get_rate(const struct ast_format *format) +{ + /* For those wondering: due to a fluke in RFC publication, G.722 is advertised + * as having a sample rate of 8kHz, while implementations must know that its + * real rate is 16kHz. Seriously. + */ + return (ast_format_cmp(format, ast_format_g722) == AST_FORMAT_CMP_EQUAL) ? 8000 : (int)ast_format_get_sample_rate(format); +} diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index e96223f7a71ea019303833735cb32767b6bdcdee..c65cf627d16be493c7b1ff9648f2dd0eb5a94a96 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -3204,15 +3204,6 @@ static int rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, return res; } -static int rtp_get_rate(struct ast_format *format) -{ - /* For those wondering: due to a fluke in RFC publication, G.722 is advertised - * as having a sample rate of 8kHz, while implementations must know that its - * real rate is 16kHz. Seriously. - */ - return (ast_format_cmp(format, ast_format_g722) == AST_FORMAT_CMP_EQUAL) ? 8000 : (int)ast_format_get_sample_rate(format); -} - static unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp) { unsigned int interval; @@ -4096,7 +4087,7 @@ static int ast_rtp_dtmf_end_with_duration(struct ast_rtp_instance *instance, cha rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000)); - if (duration > 0 && (measured_samples = duration * rtp_get_rate(rtp->f.subclass.format) / 1000) > rtp->send_duration) { + if (duration > 0 && (measured_samples = duration * ast_rtp_get_rate(rtp->f.subclass.format) / 1000) > rtp->send_duration) { ast_debug(2, "Adjusting final end duration from %d to %u\n", rtp->send_duration, measured_samples); rtp->send_duration = measured_samples; } @@ -4349,7 +4340,7 @@ static int ast_rtcp_generate_report(struct ast_rtp_instance *instance, unsigned report_block->lost_count.fraction = (fraction_lost & 0xff); report_block->lost_count.packets = (lost_packets & 0xffffff); report_block->highest_seq_no = (rtp->cycles | (rtp->lastrxseqno & 0xffff)); - report_block->ia_jitter = (unsigned int)(rtp->rxjitter * rtp_get_rate(rtp->f.subclass.format)); + report_block->ia_jitter = (unsigned int)(rtp->rxjitter * ast_rtp_get_rate(rtp->f.subclass.format)); report_block->lsr = rtp->rtcp->themrxlsr; /* If we haven't received an SR report, DLSR should be 0 */ if (!ast_tvzero(rtp->rtcp->rxlsr)) { @@ -4431,7 +4422,7 @@ static int ast_rtcp_calculate_sr_rr_statistics(struct ast_rtp_instance *instance ast_verbose(" Fraction lost: %d\n", report_block->lost_count.fraction); ast_verbose(" Cumulative loss: %u\n", report_block->lost_count.packets); ast_verbose(" Highest seq no: %u\n", report_block->highest_seq_no); - ast_verbose(" IA jitter: %.4f\n", (double)report_block->ia_jitter / rtp_get_rate(rtp->f.subclass.format)); + ast_verbose(" IA jitter: %.4f\n", (double)report_block->ia_jitter / ast_rtp_get_rate(rtp->f.subclass.format)); ast_verbose(" Their last SR: %u\n", report_block->lsr); ast_verbose(" DLSR: %4.4f (sec)\n\n", (double)(report_block->dlsr / 65536.0)); } @@ -4684,7 +4675,7 @@ static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *fr int pred, mark = 0; unsigned int ms = calc_txstamp(rtp, &frame->delivery); struct ast_sockaddr remote_address = { {0,} }; - int rate = rtp_get_rate(frame->subclass.format) / 1000; + int rate = ast_rtp_get_rate(frame->subclass.format) / 1000; unsigned int seqno; #ifdef TEST_FRAMEWORK struct ast_rtp_engine_test *test = ast_rtp_instance_get_test(instance); @@ -5204,7 +5195,7 @@ static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int t double d; double dtv; double prog; - int rate = rtp_get_rate(rtp->f.subclass.format); + int rate = ast_rtp_get_rate(rtp->f.subclass.format); double normdev_rxjitter_current; if ((!rtp->rxcore.tv_sec && !rtp->rxcore.tv_usec) || mark) { @@ -5359,7 +5350,7 @@ static void process_dtmf_rfc2833(struct ast_rtp_instance *instance, unsigned cha rtp->dtmf_duration = new_duration; rtp->resp = resp; f = ast_frdup(create_dtmf_frame(instance, AST_FRAME_DTMF_END, 0)); - f->len = ast_tvdiff_ms(ast_samp2tv(rtp->dtmf_duration, rtp_get_rate(f->subclass.format)), ast_tv(0, 0)); + f->len = ast_tvdiff_ms(ast_samp2tv(rtp->dtmf_duration, ast_rtp_get_rate(f->subclass.format)), ast_tv(0, 0)); rtp->resp = 0; rtp->dtmf_duration = rtp->dtmf_timeout = 0; AST_LIST_INSERT_TAIL(frames, f, frame_list); @@ -5390,7 +5381,7 @@ static void process_dtmf_rfc2833(struct ast_rtp_instance *instance, unsigned cha if (rtp->resp && rtp->resp != resp) { /* Another digit already began. End it */ f = ast_frdup(create_dtmf_frame(instance, AST_FRAME_DTMF_END, 0)); - f->len = ast_tvdiff_ms(ast_samp2tv(rtp->dtmf_duration, rtp_get_rate(f->subclass.format)), ast_tv(0, 0)); + f->len = ast_tvdiff_ms(ast_samp2tv(rtp->dtmf_duration, ast_rtp_get_rate(f->subclass.format)), ast_tv(0, 0)); rtp->resp = 0; rtp->dtmf_duration = rtp->dtmf_timeout = 0; AST_LIST_INSERT_TAIL(frames, f, frame_list); @@ -5487,10 +5478,10 @@ static struct ast_frame *process_dtmf_cisco(struct ast_rtp_instance *instance, u } } else if ((rtp->resp == resp) && !power) { f = create_dtmf_frame(instance, AST_FRAME_DTMF_END, ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_DTMF_COMPENSATE)); - f->samples = rtp->dtmfsamples * (rtp_get_rate(rtp->lastrxformat) / 1000); + f->samples = rtp->dtmfsamples * (ast_rtp_get_rate(rtp->lastrxformat) / 1000); rtp->resp = 0; } else if (rtp->resp == resp) { - rtp->dtmfsamples += 20 * (rtp_get_rate(rtp->lastrxformat) / 1000); + rtp->dtmfsamples += 20 * (ast_rtp_get_rate(rtp->lastrxformat) / 1000); } rtp->dtmf_timeout = 0; @@ -6229,6 +6220,7 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, s transport_rtp->f.delivery.tv_sec = 0; transport_rtp->f.delivery.tv_usec = 0; transport_rtp->f.src = "RTP"; + transport_rtp->f.stream_num = rtp->stream_num; f = &transport_rtp->f; break; case AST_RTP_RTCP_RTPFB: @@ -7104,7 +7096,7 @@ static struct ast_frame *ast_rtp_interpret(struct ast_rtp_instance *instance, st if (rtp->resp) { struct ast_frame *f; f = create_dtmf_frame(instance, AST_FRAME_DTMF_END, 0); - f->len = ast_tvdiff_ms(ast_samp2tv(rtp->dtmf_duration, rtp_get_rate(f->subclass.format)), ast_tv(0, 0)); + f->len = ast_tvdiff_ms(ast_samp2tv(rtp->dtmf_duration, ast_rtp_get_rate(f->subclass.format)), ast_tv(0, 0)); rtp->resp = 0; rtp->dtmf_timeout = rtp->dtmf_duration = 0; AST_LIST_INSERT_TAIL(&frames, f, frame_list); @@ -7188,7 +7180,7 @@ static struct ast_frame *ast_rtp_interpret(struct ast_rtp_instance *instance, st calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark); /* Add timing data to let ast_generic_bridge() put the frame into a jitterbuf */ ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO); - rtp->f.ts = timestamp / (rtp_get_rate(rtp->f.subclass.format) / 1000); + rtp->f.ts = timestamp / (ast_rtp_get_rate(rtp->f.subclass.format) / 1000); rtp->f.len = rtp->f.samples / ((ast_format_get_sample_rate(rtp->f.subclass.format) / 1000)); } else if (ast_format_get_type(rtp->f.subclass.format) == AST_MEDIA_TYPE_VIDEO) { /* Video -- samples is # of samples vs. 90000 */ @@ -7196,7 +7188,7 @@ static struct ast_frame *ast_rtp_interpret(struct ast_rtp_instance *instance, st rtp->lastividtimestamp = timestamp; calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark); ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO); - rtp->f.ts = timestamp / (rtp_get_rate(rtp->f.subclass.format) / 1000); + rtp->f.ts = timestamp / (ast_rtp_get_rate(rtp->f.subclass.format) / 1000); rtp->f.samples = timestamp - rtp->lastividtimestamp; rtp->lastividtimestamp = timestamp; rtp->f.delivery.tv_sec = 0;