From 2e77c8e8f54882d97fefe373e7a3f9ea86c5cb50 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 25 Oct 2021 14:13:55 -0400 Subject: [PATCH] TT#109800 add special handling for dtx-shift=0 Change-Id: I7e99f60476e74eb38b215573eb7d574f09b4c31a --- daemon/codec.c | 243 ++++++++++++++++++++++++++++-------------- daemon/media_socket.c | 1 + daemon/rtpengine.pod | 4 + 3 files changed, 168 insertions(+), 80 deletions(-) diff --git a/daemon/codec.c b/daemon/codec.c index 14d8b8852..97d7120cd 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -2420,13 +2420,10 @@ static void dtx_packet_free(struct dtx_packet *dtxp) { obj_put(&dtxp->decoder_handler->h); g_slice_free1(sizeof(*dtxp), dtxp); } -static bool __dtx_handle_drift(struct dtx_buffer *dtxb, struct dtx_packet *dtxp, unsigned long ts, +static bool __dtx_drift_shift(struct dtx_buffer *dtxb, struct dtx_packet *dtxp, unsigned long ts, long tv_diff, long ts_diff, struct codec_ssrc_handler *ch) { - if (!dtxp) - return false; - bool discard = false; if (tv_diff < rtpe_config.dtx_delay * 1000) { @@ -2468,11 +2465,56 @@ static bool __dtx_handle_drift(struct dtx_buffer *dtxb, struct dtx_packet *dtxp, return discard; } +static bool __dtx_drift_drop(struct dtx_buffer *dtxb, struct dtx_packet *dtxp, unsigned long ts, + long tv_diff, long ts_diff, + struct codec_ssrc_handler *ch) +{ + bool discard = false; + + if (ts_diff < dtxb->tspp) { + // TS underflow + // special case: DTMF timestamps are static + if (ts_diff == 0 && ch->handler->source_pt.codec_def->dtmf) { + ; + } + else { + ilogs(dtx, LOG_DEBUG, "Packet timestamps have caught up with DTX timer " + "(TS %lu, diff %li), " + "adjusting input TS clock back by one frame (%i)", + ts, ts_diff, dtxb->tspp); + dtxb->head_ts -= dtxb->tspp; + } + } + else if (dtxb->packets.length >= rtpe_config.dtx_buffer) { + // inspect TS is most recent packet + struct dtx_packet *dtxp_last = g_queue_peek_tail(&dtxb->packets); + ts_diff = dtxp_last->packet->ts - ts; + long long ts_diff_us = (long long) ts_diff * 1000000 / dtxb->clockrate; + if (ts_diff_us >= rtpe_config.dtx_lag * 1000) { + // overflow + ilogs(dtx, LOG_DEBUG, "DTX timer queue overflowing (%i packets in queue, " + "%lli ms delay), discarding packet", + dtxb->packets.length, ts_diff_us / 1000); + discard = true; + } + } + + return discard; +} +static bool __dtx_handle_drift(struct dtx_buffer *dtxb, struct dtx_packet *dtxp, unsigned long ts, + long tv_diff, long ts_diff, + struct codec_ssrc_handler *ch) +{ + if (!dtxp) + return false; + if (rtpe_config.dtx_shift) + return __dtx_drift_shift(dtxb, dtxp, ts, tv_diff, ts_diff, ch); + return __dtx_drift_drop(dtxb, dtxp, ts, tv_diff, ts_diff, ch); +} static void __dtx_send_later(struct timerthread_queue *ttq, void *p) { struct dtx_buffer *dtxb = (void *) ttq; struct media_packet mp_copy = {0,}; int ret = 0; - bool discard = false; unsigned long ts; int p_left = 0; long tv_diff = -1, ts_diff = 0; @@ -2482,73 +2524,116 @@ static void __dtx_send_later(struct timerthread_queue *ttq, void *p) { if (dtxb->call) log_info_call(dtxb->call); - // do we have a packet? - struct dtx_packet *dtxp = g_queue_peek_head(&dtxb->packets); - if (dtxp) { - // inspect head packet and check TS, see if it's ready to be decoded - ts = dtxp->packet->ts; - ts_diff = ts - dtxb->head_ts; - long long ts_diff_us = (long long) ts_diff * 1000000 / dtxb->clockrate; + // vars assigned in the loop + struct dtx_packet *dtxp; + struct call *call; + struct codec_ssrc_handler *ch; + struct packet_stream *ps; + + while (true) { + // do we have a packet? + dtxp = g_queue_peek_head(&dtxb->packets); + if (dtxp) { + // inspect head packet and check TS, see if it's ready to be decoded + ts = dtxp->packet->ts; + ts_diff = ts - dtxb->head_ts; + long long ts_diff_us = (long long) ts_diff * 1000000 / dtxb->clockrate; + + if (!dtxb->head_ts) + ; // first packet + else if (ts_diff < 0) + ilogs(dtx, LOG_DEBUG, "DTX timestamp reset (from %lu to %lu)", dtxb->head_ts, ts); + else if (ts_diff_us > MAX(20 * rtpe_config.dtx_delay, 200000)) + ilogs(dtx, LOG_DEBUG, "DTX timestamp reset (from %lu to %lu = %lli ms)", + dtxb->head_ts, ts, ts_diff_us); + else if (ts_diff >= dtxb->tspp * 2) { + ilogs(dtx, LOG_DEBUG, "First packet in DTX buffer not ready yet (packet TS %lu, " + "DTX TS %lu, diff %li)", + ts, dtxb->head_ts, ts_diff); + dtxp = NULL; + } - if (!dtxb->head_ts) - ; // first packet - else if (ts_diff < 0) - ilogs(dtx, LOG_DEBUG, "DTX timestamp reset (from %lu to %lu)", dtxb->head_ts, ts); - else if (ts_diff_us > MAX(20 * rtpe_config.dtx_delay, 200000)) - ilogs(dtx, LOG_DEBUG, "DTX timestamp reset (from %lu to %lu = %lli ms)", - dtxb->head_ts, ts, ts_diff_us); - else if (ts_diff >= dtxb->tspp * 2) { - ilogs(dtx, LOG_DEBUG, "First packet in DTX buffer not ready yet (packet TS %lu, " - "DTX TS %lu, diff %li)", - ts, dtxb->head_ts, ts_diff); - dtxp = NULL; - } - - // go or no go? - if (dtxp) - g_queue_pop_head(&dtxb->packets); - } + // go or no go? + if (dtxp) + g_queue_pop_head(&dtxb->packets); + } - p_left = dtxb->packets.length; + p_left = dtxb->packets.length; - if (dtxp) { - // save the `mp` for possible future DTX - media_packet_release(&dtxb->last_mp); - media_packet_copy(&dtxb->last_mp, &dtxp->mp); - media_packet_copy(&mp_copy, &dtxp->mp); - ts_diff = dtxp->packet->ts - dtxb->head_ts; - ts = dtxb->head_ts = dtxp->packet->ts; - tv_diff = timeval_diff(&rtpe_now, &mp_copy.tv); - } - else { - // no packet ready to decode: DTX - media_packet_copy(&mp_copy, &dtxb->last_mp); - // shift forward TS - dtxb->head_ts += dtxb->tspp; - ts = dtxb->head_ts; - } - struct packet_stream *ps = mp_copy.stream; - log_info_stream_fd(mp_copy.sfd); - - // copy out other fields so we can unlock - struct codec_ssrc_handler *ch = (dtxp && dtxp->decoder_handler) ? obj_get(&dtxp->decoder_handler->h) - : NULL; - if (!ch && dtxb->csh) - ch = obj_get(&dtxb->csh->h); - struct call *call = dtxb->call ? obj_get(dtxb->call) : NULL; - - if (!call || !ch || !ps || !ps->ssrc_in - || dtxb->ssrc != ps->ssrc_in->parent->h.ssrc - || dtxb->ttq_entry.when.tv_sec == 0) { - // shut down or SSRC change - ilogs(dtx, LOG_DEBUG, "DTX buffer for %lx has been shut down", (unsigned long) dtxb->ssrc); - dtxb->ttq_entry.when.tv_sec = 0; - dtxb->head_ts = 0; + if (dtxp) { + // save the `mp` for possible future DTX + media_packet_release(&dtxb->last_mp); + media_packet_copy(&dtxb->last_mp, &dtxp->mp); + media_packet_copy(&mp_copy, &dtxp->mp); + if (dtxb->head_ts) + ts_diff = dtxp->packet->ts - dtxb->head_ts; + else + ts_diff = dtxb->tspp; // first packet + ts = dtxb->head_ts = dtxp->packet->ts; + tv_diff = timeval_diff(&rtpe_now, &mp_copy.tv); + } + else { + // no packet ready to decode: DTX + media_packet_copy(&mp_copy, &dtxb->last_mp); + // shift forward TS + dtxb->head_ts += dtxb->tspp; + ts = dtxb->head_ts; + } + ps = mp_copy.stream; + log_info_stream_fd(mp_copy.sfd); + + // copy out other fields so we can unlock + ch = (dtxp && dtxp->decoder_handler) ? obj_get(&dtxp->decoder_handler->h) + : NULL; + if (!ch && dtxb->csh) + ch = obj_get(&dtxb->csh->h); + call = dtxb->call ? obj_get(dtxb->call) : NULL; + + if (!call || !ch || !ps || !ps->ssrc_in + || dtxb->ssrc != ps->ssrc_in->parent->h.ssrc + || dtxb->ttq_entry.when.tv_sec == 0) { + // shut down or SSRC change + ilogs(dtx, LOG_DEBUG, "DTX buffer for %lx has been shut down", (unsigned long) dtxb->ssrc); + dtxb->ttq_entry.when.tv_sec = 0; + dtxb->head_ts = 0; + mutex_unlock(&dtxb->lock); + goto out; // shut down + } + + if (!dtxp) // we need to do DTX + break; + + bool discard = __dtx_handle_drift(dtxb, dtxp, ts, tv_diff, ts_diff, ch); + + if (!discard) + break; + + // release and try again mutex_unlock(&dtxb->lock); - goto out; // shut down - } - discard = __dtx_handle_drift(dtxb, dtxp, ts, tv_diff, ts_diff, ch); + if (call && mp_copy.ssrc_out) { + // packet consumed - track seq + rwlock_lock_r(&call->master_lock); + __ssrc_lock_both(&mp_copy); + mp_copy.ssrc_out->parent->seq_diff--; + __ssrc_unlock_both(&mp_copy); + rwlock_unlock_r(&call->master_lock); + } + if (call) + obj_put(call); + if (ch) + obj_put(&ch->h); + if (dtxp) + dtx_packet_free(dtxp); + media_packet_release(&mp_copy); + + call = NULL; + ch = NULL; + dtxp = NULL; + ps = NULL; + + mutex_lock(&dtxb->lock); + } int ptime = dtxb->ptime; @@ -2558,20 +2643,18 @@ static void __dtx_send_later(struct timerthread_queue *ttq, void *p) { __ssrc_lock_both(&mp_copy); if (dtxp) { - if (!discard) { - ilogs(dtx, LOG_DEBUG, "Decoding DTX-buffered RTP packet (TS %lu) now; " - "%i packets left in queue", ts, p_left); - - mp_copy.ptime = -1; - ret = dtxp->func(ch, dtxp->packet, &mp_copy); - if (!ret) { - if (mp_copy.ptime > 0) - ptime = mp_copy.ptime; - } - else - ilogs(dtx, LOG_WARN | LOG_FLAG_LIMIT, - "Decoder error while processing buffered RTP packet"); + ilogs(dtx, LOG_DEBUG, "Decoding DTX-buffered RTP packet (TS %lu) now; " + "%i packets left in queue", ts, p_left); + + mp_copy.ptime = -1; + ret = dtxp->func(ch, dtxp->packet, &mp_copy); + if (!ret) { + if (mp_copy.ptime > 0) + ptime = mp_copy.ptime; } + else + ilogs(dtx, LOG_WARN | LOG_FLAG_LIMIT, + "Decoder error while processing buffered RTP packet"); } else { unsigned int diff = rtpe_now.tv_sec - dtxb->start; diff --git a/daemon/media_socket.c b/daemon/media_socket.c index 96554eb53..942d7f394 100644 --- a/daemon/media_socket.c +++ b/daemon/media_socket.c @@ -1991,6 +1991,7 @@ void media_packet_release(struct media_packet *mp) { g_queue_clear_full(&mp->packets_out, codec_packet_free); g_free(mp->rtp); g_free(mp->rtcp); + ZERO(*mp); } diff --git a/daemon/rtpengine.pod b/daemon/rtpengine.pod index 0249f0803..58025ee79 100644 --- a/daemon/rtpengine.pod +++ b/daemon/rtpengine.pod @@ -820,6 +820,10 @@ or backwards (delayed) in case of a DTX buffer overflow or underflow. An underflow occurs when RTP packets are received slower than expected, while an overflow occurs when packets are received faster than expected. +If this value is set to zero then no adjustments of the DTX timer will be made. +Instead, in order to keep up with the flow of received RTP packets, packets +will be dropped or additional DTX audio will be generated as needed. + =item B<--dtx-cn-params=>I Specify one comfort noise parameter. This option follows the same format as