|
|
|
@ -75,16 +75,16 @@ struct dtx_buffer { |
|
|
|
int ptime; // ms per packet |
|
|
|
int tspp; // timestamp increment per packet |
|
|
|
struct call *call; |
|
|
|
unsigned long ts; |
|
|
|
unsigned int ts_seq; // for subsequent packets with same TS, e.g. DTMF |
|
|
|
GQueue packets; |
|
|
|
struct media_packet last_mp; |
|
|
|
unsigned long head_ts; |
|
|
|
uint32_t ssrc; |
|
|
|
struct timerthread_queue_entry ttq_entry; |
|
|
|
time_t start; |
|
|
|
}; |
|
|
|
struct dtx_entry { |
|
|
|
struct timerthread_queue_entry ttq_entry; |
|
|
|
struct dtx_packet { |
|
|
|
struct transcode_packet *packet; |
|
|
|
struct media_packet mp; |
|
|
|
unsigned long ts; |
|
|
|
void *ssrc_ptr; // opaque pointer, doesn't hold a reference |
|
|
|
struct codec_ssrc_handler *decoder_handler; // holds reference |
|
|
|
int (*func)(struct codec_ssrc_handler *ch, struct transcode_packet *packet, struct media_packet *mp); |
|
|
|
}; |
|
|
|
@ -2377,18 +2377,6 @@ static int codec_decoder_event(enum codec_event event, void *ptr, void *data) { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static void __dtx_add_callback(struct dtx_buffer *dtxb, const struct timeval *base, unsigned int offset, |
|
|
|
const struct media_packet *mp, unsigned long ts, int seq_add, void *ssrc_ptr) |
|
|
|
{ |
|
|
|
struct dtx_entry *dtxe = g_slice_alloc0(sizeof(*dtxe)); |
|
|
|
dtxe->ttq_entry.when = *base; |
|
|
|
timeval_add_usec(&dtxe->ttq_entry.when, offset); |
|
|
|
dtxe->ts = ts; |
|
|
|
media_packet_copy(&dtxe->mp, mp); |
|
|
|
dtxe->mp.rtp->seq_num += htons(seq_add); |
|
|
|
dtxe->ssrc_ptr = ssrc_ptr; |
|
|
|
timerthread_queue_push(&dtxb->ttq, &dtxe->ttq_entry); |
|
|
|
} |
|
|
|
// consumes `packet` if buffered (returns 1) |
|
|
|
static int __buffer_dtx(struct dtx_buffer *dtxb, struct codec_ssrc_handler *decoder_handler, |
|
|
|
struct transcode_packet *packet, struct media_packet *mp, |
|
|
|
@ -2398,135 +2386,227 @@ static int __buffer_dtx(struct dtx_buffer *dtxb, struct codec_ssrc_handler *deco |
|
|
|
if (!dtxb || !mp->sfd || !mp->ssrc_in || !mp->ssrc_out) |
|
|
|
return 0; |
|
|
|
|
|
|
|
ilogs(transcoding, LOG_DEBUG, "Adding packet to DTX buffer"); |
|
|
|
|
|
|
|
unsigned long ts = packet->ts; |
|
|
|
|
|
|
|
// allocate packet object |
|
|
|
struct dtx_packet *dtxp = g_slice_alloc0(sizeof(*dtxp)); |
|
|
|
dtxp->packet = packet; |
|
|
|
dtxp->func = func; |
|
|
|
if (decoder_handler) |
|
|
|
dtxp->decoder_handler = obj_get(&decoder_handler->h); |
|
|
|
media_packet_copy(&dtxp->mp, mp); |
|
|
|
|
|
|
|
// add to processing queue |
|
|
|
|
|
|
|
mutex_lock(&dtxb->lock); |
|
|
|
if (ts != dtxb->ts) { |
|
|
|
dtxb->ts = ts; |
|
|
|
dtxb->ts_seq = 0; |
|
|
|
} |
|
|
|
else |
|
|
|
dtxb->ts_seq++; |
|
|
|
unsigned int ts_seq = dtxb->ts_seq; |
|
|
|
|
|
|
|
dtxb->start = rtpe_now.tv_sec; |
|
|
|
mutex_unlock(&dtxb->lock); |
|
|
|
g_queue_push_tail(&dtxb->packets, dtxp); |
|
|
|
ilogs(dtx, LOG_DEBUG, "Adding packet (TS %lu) to DTX buffer; now %i packets in DTX queue", |
|
|
|
ts, dtxb->packets.length); |
|
|
|
|
|
|
|
struct dtx_entry *dtxe = g_slice_alloc0(sizeof(*dtxe)); |
|
|
|
dtxe->ttq_entry.when = rtpe_now; |
|
|
|
timeval_add_usec(&dtxe->ttq_entry.when, rtpe_config.dtx_delay * 1000); |
|
|
|
dtxe->packet = packet; |
|
|
|
dtxe->func = func; |
|
|
|
if (decoder_handler) |
|
|
|
dtxe->decoder_handler = obj_get(&decoder_handler->h); |
|
|
|
media_packet_copy(&dtxe->mp, mp); |
|
|
|
timerthread_queue_push(&dtxb->ttq, &dtxe->ttq_entry); |
|
|
|
// packet now consumed |
|
|
|
packet = NULL; |
|
|
|
// schedule timer if not running yet |
|
|
|
if (!dtxb->ttq_entry.when.tv_sec) { |
|
|
|
if (!dtxb->ssrc) |
|
|
|
dtxb->ssrc = mp->ssrc_in->parent->h.ssrc; |
|
|
|
dtxb->ttq_entry.when = mp->tv; |
|
|
|
timeval_add_usec(&dtxb->ttq_entry.when, rtpe_config.dtx_delay * 1000); |
|
|
|
timerthread_queue_push(&dtxb->ttq, &dtxb->ttq_entry); |
|
|
|
} |
|
|
|
|
|
|
|
__dtx_add_callback(dtxb, &rtpe_now, (rtpe_config.dtx_delay + dtxb->ptime) * 1000, mp, ts + ts_seq, 1, |
|
|
|
mp->stream->ssrc_in); |
|
|
|
mutex_unlock(&dtxb->lock); |
|
|
|
|
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
static void __dtx_entry_free(void *p) { |
|
|
|
struct dtx_entry *dtxe = p; |
|
|
|
if (dtxe->packet) |
|
|
|
__transcode_packet_free(dtxe->packet); |
|
|
|
media_packet_release(&dtxe->mp); |
|
|
|
if (dtxe->decoder_handler) |
|
|
|
obj_put(&dtxe->decoder_handler->h); |
|
|
|
g_slice_free1(sizeof(*dtxe), dtxe); |
|
|
|
static void dtx_packet_free(struct dtx_packet *dtxp) { |
|
|
|
if (dtxp->packet) |
|
|
|
__transcode_packet_free(dtxp->packet); |
|
|
|
media_packet_release(&dtxp->mp); |
|
|
|
if (dtxp->decoder_handler) |
|
|
|
obj_put(&dtxp->decoder_handler->h); |
|
|
|
g_slice_free1(sizeof(*dtxp), dtxp); |
|
|
|
} |
|
|
|
static void __dtx_send_later(struct timerthread_queue *ttq, void *p) { |
|
|
|
struct dtx_buffer *dtxb = (void *) ttq; |
|
|
|
struct dtx_entry *dtxe = p; |
|
|
|
struct transcode_packet *packet = dtxe->packet; |
|
|
|
struct media_packet *mp = &dtxe->mp; |
|
|
|
struct packet_stream *ps = mp->stream; |
|
|
|
int ret = 0; |
|
|
|
struct media_packet mp_copy = {0,}; |
|
|
|
int ret = 0, discard = 0; |
|
|
|
unsigned long ts; |
|
|
|
int p_left = 0; |
|
|
|
long tv_diff = -1, ts_diff = 0; |
|
|
|
|
|
|
|
mutex_lock(&dtxb->lock); |
|
|
|
struct codec_ssrc_handler *ch = dtxe->decoder_handler ? obj_get(&dtxe->decoder_handler->h) : NULL; |
|
|
|
|
|
|
|
// 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; |
|
|
|
|
|
|
|
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 > 100000) // arbitrary value |
|
|
|
ilogs(dtx, LOG_DEBUG, "DTX timestamp reset (from %lu to %lu)", dtxb->head_ts, ts); |
|
|
|
else if (ts_diff > dtxb->tspp) { |
|
|
|
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); |
|
|
|
} |
|
|
|
|
|
|
|
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; |
|
|
|
mutex_unlock(&dtxb->lock); |
|
|
|
|
|
|
|
if (!call || !ch) |
|
|
|
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; |
|
|
|
mutex_unlock(&dtxb->lock); |
|
|
|
goto out; // shut down |
|
|
|
} |
|
|
|
|
|
|
|
// schedule next run |
|
|
|
timeval_add_usec(&dtxb->ttq_entry.when, dtxb->ptime * 1000); |
|
|
|
|
|
|
|
// handle timer drifts |
|
|
|
if (dtxp && tv_diff < rtpe_config.dtx_delay * 1000) { |
|
|
|
// timer underflow |
|
|
|
ilogs(dtx, LOG_DEBUG, "Packet reception time has caught up with DTX timer " |
|
|
|
"(%li ms < %i ms), " |
|
|
|
"pushing DTX timer forward my %i ms", |
|
|
|
tv_diff / 1000, rtpe_config.dtx_delay, rtpe_config.dtx_shift); |
|
|
|
timeval_add_usec(&dtxb->ttq_entry.when, rtpe_config.dtx_shift * 1000); |
|
|
|
} |
|
|
|
else if (dtxp && 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), " |
|
|
|
"pushing DTX timer forward by %i ms and discarding packet", |
|
|
|
ts, ts_diff, rtpe_config.dtx_shift); |
|
|
|
timeval_add_usec(&dtxb->ttq_entry.when, rtpe_config.dtx_shift * 1000); |
|
|
|
discard = 1; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (dtxp && 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 / ch->handler->source_pt.clock_rate; |
|
|
|
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), speeding up DTX timer by %i ms", |
|
|
|
dtxb->packets.length, ts_diff_us / 1000, rtpe_config.dtx_shift); |
|
|
|
timeval_add_usec(&dtxb->ttq_entry.when, rtpe_config.dtx_shift * -1000); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
log_info_stream_fd(mp->sfd); |
|
|
|
timerthread_queue_push(&dtxb->ttq, &dtxb->ttq_entry); |
|
|
|
|
|
|
|
mutex_unlock(&dtxb->lock); |
|
|
|
|
|
|
|
rwlock_lock_r(&call->master_lock); |
|
|
|
__ssrc_lock_both(mp); |
|
|
|
__ssrc_lock_both(&mp_copy); |
|
|
|
|
|
|
|
if (packet) { |
|
|
|
ilogs(transcoding, LOG_DEBUG, "Decoding DTX-buffered RTP packet (TS %lu) now", packet->ts); |
|
|
|
if (dtxp) { |
|
|
|
if (!discard) { |
|
|
|
ilogs(dtx, LOG_DEBUG, "Decoding DTX-buffered RTP packet (TS %lu) now; " |
|
|
|
"%i packets left in queue", ts, p_left); |
|
|
|
|
|
|
|
ret = dtxe->func(ch, packet, &dtxe->mp); |
|
|
|
if (ret) |
|
|
|
ilogs(transcoding, LOG_WARN | LOG_FLAG_LIMIT, "Decoder error while processing buffered RTP packet"); |
|
|
|
ret = dtxp->func(ch, dtxp->packet, &mp_copy); |
|
|
|
if (ret) |
|
|
|
ilogs(dtx, LOG_WARN | LOG_FLAG_LIMIT, |
|
|
|
"Decoder error while processing buffered RTP packet"); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
unsigned long dtxe_ts = dtxe->ts; |
|
|
|
|
|
|
|
mutex_lock(&dtxb->lock); |
|
|
|
unsigned int diff = rtpe_now.tv_sec - dtxb->start; |
|
|
|
unsigned long dtxb_ts = dtxb->ts + dtxb->ts_seq; |
|
|
|
void *ssrc_ptr = dtxe->mp.stream->ssrc_in; |
|
|
|
|
|
|
|
if (dtxe_ts == dtxb_ts |
|
|
|
&& ssrc_ptr == dtxe->ssrc_ptr |
|
|
|
&& (rtpe_config.max_dtx <= 0 || diff < rtpe_config.max_dtx)) |
|
|
|
{ |
|
|
|
ilogs(transcoding, LOG_DEBUG, "RTP media for TS %lu+ missing, triggering DTX", |
|
|
|
dtxe_ts); |
|
|
|
|
|
|
|
dtxb_ts += dtxb->tspp; |
|
|
|
dtxb_ts -= dtxb->ts_seq; |
|
|
|
dtxe_ts = dtxb_ts; |
|
|
|
dtxb->ts = dtxb_ts; |
|
|
|
dtxb->ts_seq = 0; |
|
|
|
mutex_unlock(&dtxb->lock); |
|
|
|
|
|
|
|
ret = decoder_lost_packet(ch->decoder, dtxe_ts, |
|
|
|
ch->handler->packet_decoded, ch, &dtxe->mp); |
|
|
|
if (ret) |
|
|
|
ilogs(transcoding, LOG_WARN | LOG_FLAG_LIMIT, "Decoder error handling DTX/lost packet"); |
|
|
|
|
|
|
|
__dtx_add_callback(dtxb, &dtxe->ttq_entry.when, dtxb->ptime * 1000, mp, dtxe_ts, 0, ssrc_ptr); |
|
|
|
if (rtpe_config.max_dtx <= 0 || diff < rtpe_config.max_dtx) { |
|
|
|
ilogs(dtx, LOG_DEBUG, "RTP media for TS %lu missing, triggering DTX", ts); |
|
|
|
|
|
|
|
// synthetic packet |
|
|
|
mp_copy.rtp->seq_num += htons(1); |
|
|
|
|
|
|
|
ret = decoder_lost_packet(ch->decoder, ts, |
|
|
|
ch->handler->packet_decoded, ch, &mp_copy); |
|
|
|
if (ret) |
|
|
|
ilogs(dtx, LOG_WARN | LOG_FLAG_LIMIT, |
|
|
|
"Decoder error handling DTX/lost packet"); |
|
|
|
} |
|
|
|
else |
|
|
|
mutex_unlock(&dtxb->lock); |
|
|
|
ilogs(dtx, LOG_DEBUG, "Stopping DTX at TS %lu", ts); |
|
|
|
} |
|
|
|
|
|
|
|
__ssrc_unlock_both(mp); |
|
|
|
__ssrc_unlock_both(&mp_copy); |
|
|
|
|
|
|
|
if (mp->packets_out.length && ret == 0) { |
|
|
|
if (mp_copy.packets_out.length && ret == 0) { |
|
|
|
struct packet_stream *sink = ps->rtp_sink; |
|
|
|
|
|
|
|
if (!sink) |
|
|
|
media_socket_dequeue(mp, NULL); // just free |
|
|
|
media_socket_dequeue(&mp_copy, NULL); // just free |
|
|
|
else { |
|
|
|
if (ps->handler && media_packet_encrypt(ps->handler->out->rtp_crypt, sink, mp)) |
|
|
|
ilogs(transcoding, LOG_ERR | LOG_FLAG_LIMIT, "Error encrypting buffered RTP media"); |
|
|
|
if (ps->handler && media_packet_encrypt(ps->handler->out->rtp_crypt, sink, &mp_copy)) |
|
|
|
ilogs(dtx, LOG_ERR | LOG_FLAG_LIMIT, "Error encrypting buffered RTP media"); |
|
|
|
|
|
|
|
mutex_lock(&sink->out_lock); |
|
|
|
if (media_socket_dequeue(mp, sink)) |
|
|
|
ilogs(transcoding, LOG_ERR | LOG_FLAG_LIMIT, "Error sending buffered media to RTP sink"); |
|
|
|
if (media_socket_dequeue(&mp_copy, sink)) |
|
|
|
ilogs(dtx, LOG_ERR | LOG_FLAG_LIMIT, |
|
|
|
"Error sending buffered media to RTP sink"); |
|
|
|
mutex_unlock(&sink->out_lock); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
rwlock_unlock_r(&call->master_lock); |
|
|
|
obj_put(call); |
|
|
|
obj_put(&ch->h); |
|
|
|
|
|
|
|
out: |
|
|
|
__dtx_entry_free(dtxe); |
|
|
|
if (call) |
|
|
|
obj_put(call); |
|
|
|
if (ch) |
|
|
|
obj_put(&ch->h); |
|
|
|
if (dtxp) |
|
|
|
dtx_packet_free(dtxp); |
|
|
|
media_packet_release(&mp_copy); |
|
|
|
log_info_clear(); |
|
|
|
} |
|
|
|
static void __dtx_shutdown(struct dtx_buffer *dtxb) { |
|
|
|
@ -2536,10 +2616,12 @@ static void __dtx_shutdown(struct dtx_buffer *dtxb) { |
|
|
|
if (dtxb->call) |
|
|
|
obj_put(dtxb->call); |
|
|
|
dtxb->call = NULL; |
|
|
|
g_queue_clear_full(&dtxb->packets, (GDestroyNotify) dtx_packet_free); |
|
|
|
} |
|
|
|
static void __dtx_free(void *p) { |
|
|
|
struct dtx_buffer *dtxb = p; |
|
|
|
__dtx_shutdown(dtxb); |
|
|
|
media_packet_release(&dtxb->last_mp); |
|
|
|
mutex_destroy(&dtxb->lock); |
|
|
|
} |
|
|
|
static void __dtx_setup(struct codec_ssrc_handler *ch) { |
|
|
|
@ -2551,14 +2633,14 @@ static void __dtx_setup(struct codec_ssrc_handler *ch) { |
|
|
|
|
|
|
|
struct dtx_buffer *dtx = |
|
|
|
ch->dtx_buffer = timerthread_queue_new("dtx_buffer", sizeof(*ch->dtx_buffer), |
|
|
|
&codec_timers_thread, NULL, __dtx_send_later, __dtx_free, __dtx_entry_free); |
|
|
|
&codec_timers_thread, NULL, __dtx_send_later, __dtx_free, NULL); |
|
|
|
dtx->csh = obj_get(&ch->h); |
|
|
|
dtx->call = obj_get(ch->handler->media->call); |
|
|
|
mutex_init(&dtx->lock); |
|
|
|
dtx->ptime = ch->ptime; |
|
|
|
if (!dtx->ptime) |
|
|
|
dtx->ptime = 20; // XXX ? |
|
|
|
dtx->tspp = dtx->ptime * ch->handler->source_pt.clock_rate / 1000; |
|
|
|
dtx->ptime = 20; // XXX should be replaced with length of actual decoded packet |
|
|
|
dtx->tspp = dtx->ptime * ch->handler->source_pt.clock_rate / 1000; // XXX ditto |
|
|
|
} |
|
|
|
static void __ssrc_handler_stop(void *p) { |
|
|
|
struct codec_ssrc_handler *ch = p; |
|
|
|
@ -3862,6 +3944,7 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_ |
|
|
|
|
|
|
|
void codecs_init(void) { |
|
|
|
#ifdef WITH_TRANSCODING |
|
|
|
// XXX not real queue timer - unify to simple timerthread |
|
|
|
timerthread_init(&codec_timers_thread, timerthread_queue_run); |
|
|
|
rtcp_timer_queue = timerthread_queue_new("rtcp_timer_queue", sizeof(*rtcp_timer_queue), |
|
|
|
&codec_timers_thread, NULL, __rtcp_timer_run, NULL, __rtcp_timer_free); |
|
|
|
@ -3869,6 +3952,7 @@ void codecs_init(void) { |
|
|
|
} |
|
|
|
void codecs_cleanup(void) { |
|
|
|
#ifdef WITH_TRANSCODING |
|
|
|
obj_put(&rtcp_timer_queue->ttq.tt_obj); |
|
|
|
timerthread_free(&codec_timers_thread); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|