|
|
|
|
@ -113,6 +113,25 @@ struct dtx_packet {
|
|
|
|
|
struct transcode_packet *packet, struct media_packet *mp);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef int (*encoder_input_func_t)(encoder_t *enc, AVFrame *frame,
|
|
|
|
|
int (*callback)(encoder_t *, void *u1, void *u2), void *u1, void *u2);
|
|
|
|
|
|
|
|
|
|
struct delay_buffer {
|
|
|
|
|
struct codec_timer ct;
|
|
|
|
|
struct call *call;
|
|
|
|
|
struct codec_handler *handler;
|
|
|
|
|
mutex_t lock;
|
|
|
|
|
unsigned int delay;
|
|
|
|
|
GQueue frames; // in reverse order: newest packet first, oldest last
|
|
|
|
|
};
|
|
|
|
|
struct delay_frame {
|
|
|
|
|
AVFrame *frame;
|
|
|
|
|
struct media_packet mp;
|
|
|
|
|
encoder_input_func_t func;
|
|
|
|
|
struct codec_handler *handler;
|
|
|
|
|
struct codec_ssrc_handler *ch;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct silence_event {
|
|
|
|
|
uint64_t start;
|
|
|
|
|
uint64_t end;
|
|
|
|
|
@ -214,6 +233,12 @@ static int __buffer_dtx(struct dtx_buffer *dtxb, struct codec_ssrc_handler *ch,
|
|
|
|
|
static void __dtx_shutdown(struct dtx_buffer *dtxb);
|
|
|
|
|
static struct codec_handler *__input_handler(struct codec_handler *h, struct media_packet *mp);
|
|
|
|
|
|
|
|
|
|
static void __delay_frame_process(struct delay_buffer *, struct delay_frame *dframe);
|
|
|
|
|
static void __delay_buffer_setup(struct delay_buffer **dbufp,
|
|
|
|
|
struct codec_handler *h, struct call *call, unsigned int delay);
|
|
|
|
|
static void __delay_buffer_shutdown(struct delay_buffer *dbuf, bool);
|
|
|
|
|
static void delay_buffer_stop(struct delay_buffer **pcmbp);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static struct codec_handler codec_handler_stub_ssrc = {
|
|
|
|
|
.source_pt.payload_type = -1,
|
|
|
|
|
@ -228,6 +253,10 @@ static struct codec_handler codec_handler_stub_ssrc = {
|
|
|
|
|
static void __handler_shutdown(struct codec_handler *handler) {
|
|
|
|
|
ssrc_hash_foreach(handler->ssrc_hash, __ssrc_handler_stop, NULL);
|
|
|
|
|
free_ssrc_hash(&handler->ssrc_hash);
|
|
|
|
|
if (handler->delay_buffer) {
|
|
|
|
|
__delay_buffer_shutdown(handler->delay_buffer, true);
|
|
|
|
|
delay_buffer_stop(&handler->delay_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (handler->ssrc_handler)
|
|
|
|
|
obj_put(&handler->ssrc_handler->h);
|
|
|
|
|
@ -347,7 +376,7 @@ static void __make_transcoder(struct codec_handler *handler, struct rtp_payload_
|
|
|
|
|
STR_FMT(&dest->encoding_with_params),
|
|
|
|
|
dest->payload_type);
|
|
|
|
|
|
|
|
|
|
goto check_output;
|
|
|
|
|
goto no_handler_reset;
|
|
|
|
|
|
|
|
|
|
reset:
|
|
|
|
|
__handler_shutdown(handler);
|
|
|
|
|
@ -396,7 +425,8 @@ reset:
|
|
|
|
|
|
|
|
|
|
ssrc_hash_foreach(handler->media->monologue->ssrc_hash, __reset_sequencer, NULL);
|
|
|
|
|
|
|
|
|
|
check_output:;
|
|
|
|
|
no_handler_reset:
|
|
|
|
|
__delay_buffer_setup(&handler->delay_buffer, handler, handler->media->call, handler->media->buffer_delay);
|
|
|
|
|
// check if we have multiple decoders transcoding to the same output PT
|
|
|
|
|
struct codec_handler *output_handler = NULL;
|
|
|
|
|
if (output_transcoders)
|
|
|
|
|
@ -2082,6 +2112,59 @@ static int codec_decoder_event(enum codec_event event, void *ptr, void *data) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// must be locked
|
|
|
|
|
static void __delay_buffer_schedule(struct delay_buffer *dbuf) {
|
|
|
|
|
if (dbuf->ct.next.tv_sec) // already scheduled?
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
struct delay_frame *dframe = g_queue_peek_tail(&dbuf->frames);
|
|
|
|
|
if (!dframe)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
struct timeval to_run = dframe->mp.tv;
|
|
|
|
|
timeval_add_usec(&to_run, dbuf->delay * 1000);
|
|
|
|
|
dbuf->ct.next = to_run;
|
|
|
|
|
timerthread_obj_schedule_abs(&dbuf->ct.tt_obj, &dbuf->ct.next);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool __buffer_delay_do_direct(struct delay_buffer *dbuf) {
|
|
|
|
|
if (!dbuf)
|
|
|
|
|
return true;
|
|
|
|
|
LOCK(&dbuf->lock);
|
|
|
|
|
if (dbuf->delay == 0 && dbuf->frames.length == 0)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int delay_frame_cmp(const void *A, const void *B, void *ptr) {
|
|
|
|
|
const struct delay_frame *a = A, *b = B;
|
|
|
|
|
return -1 * timeval_cmp(&a->mp.tv, &b->mp.tv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// consumes frame
|
|
|
|
|
static void __buffer_delay_frame(struct delay_buffer *dbuf, struct codec_ssrc_handler *ch,
|
|
|
|
|
encoder_input_func_t input_func, AVFrame *frame, struct media_packet *mp)
|
|
|
|
|
{
|
|
|
|
|
if (__buffer_delay_do_direct(dbuf)) {
|
|
|
|
|
// input now
|
|
|
|
|
input_func(ch->encoder, frame, ch->handler->packet_encoded, ch, mp);
|
|
|
|
|
av_frame_free(&frame);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct delay_frame *dframe = g_slice_alloc0(sizeof(*dframe));
|
|
|
|
|
dframe->frame = frame;
|
|
|
|
|
dframe->func = input_func;
|
|
|
|
|
dframe->ch = obj_get(&ch->h);
|
|
|
|
|
media_packet_copy(&dframe->mp, mp);
|
|
|
|
|
|
|
|
|
|
LOCK(&dbuf->lock);
|
|
|
|
|
g_queue_insert_sorted(&dbuf->frames, dframe, delay_frame_cmp, NULL);
|
|
|
|
|
|
|
|
|
|
__delay_buffer_schedule(dbuf);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// consumes `packet` if buffered (returns 1)
|
|
|
|
|
static int __buffer_dtx(struct dtx_buffer *dtxb, struct codec_ssrc_handler *decoder_handler,
|
|
|
|
|
struct codec_ssrc_handler *input_handler,
|
|
|
|
|
@ -2131,6 +2214,37 @@ static int __buffer_dtx(struct dtx_buffer *dtxb, struct codec_ssrc_handler *deco
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void delay_frame_free(struct delay_frame *dframe) {
|
|
|
|
|
media_packet_release(&dframe->mp);
|
|
|
|
|
av_frame_free(&dframe->frame);
|
|
|
|
|
if (dframe->ch)
|
|
|
|
|
obj_put(&dframe->ch->h);
|
|
|
|
|
g_slice_free1(sizeof(*dframe), dframe);
|
|
|
|
|
}
|
|
|
|
|
static void delay_frame_send(struct delay_frame *dframe) {
|
|
|
|
|
// XXX this should be unified with other instances of the same code
|
|
|
|
|
struct sink_handler *sh = &dframe->mp.sink;
|
|
|
|
|
struct packet_stream *sink = sh->sink;
|
|
|
|
|
|
|
|
|
|
if (!sink)
|
|
|
|
|
media_socket_dequeue(&dframe->mp, NULL); // just free
|
|
|
|
|
else {
|
|
|
|
|
if (sh->handler && media_packet_encrypt(sh->handler->out->rtp_crypt, sink, &dframe->mp))
|
|
|
|
|
ilogs(transcoding, LOG_ERR | LOG_FLAG_LIMIT, "Error encrypting buffered RTP media");
|
|
|
|
|
|
|
|
|
|
mutex_lock(&sink->out_lock);
|
|
|
|
|
if (media_socket_dequeue(&dframe->mp, sink))
|
|
|
|
|
ilogs(transcoding, LOG_ERR | LOG_FLAG_LIMIT,
|
|
|
|
|
"Error sending buffered media to RTP sink");
|
|
|
|
|
mutex_unlock(&sink->out_lock);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
static void delay_frame_flush(struct delay_buffer *dbuf, struct delay_frame *dframe) {
|
|
|
|
|
// call is locked in W here
|
|
|
|
|
__delay_frame_process(dbuf, dframe);
|
|
|
|
|
delay_frame_send(dframe);
|
|
|
|
|
delay_frame_free(dframe);
|
|
|
|
|
}
|
|
|
|
|
static void dtx_packet_free(struct dtx_packet *dtxp) {
|
|
|
|
|
if (dtxp->packet)
|
|
|
|
|
__transcode_packet_free(dtxp->packet);
|
|
|
|
|
@ -2141,9 +2255,88 @@ static void dtx_packet_free(struct dtx_packet *dtxp) {
|
|
|
|
|
obj_put(&dtxp->input_handler->h);
|
|
|
|
|
g_slice_free1(sizeof(*dtxp), dtxp);
|
|
|
|
|
}
|
|
|
|
|
static void delay_buffer_stop(struct delay_buffer **pcmbp) {
|
|
|
|
|
codec_timer_stop((struct codec_timer **) pcmbp);
|
|
|
|
|
}
|
|
|
|
|
static void dtx_buffer_stop(struct dtx_buffer **dtxbp) {
|
|
|
|
|
codec_timer_stop((struct codec_timer **) dtxbp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void __delay_frame_process(struct delay_buffer *dbuf, struct delay_frame *dframe) {
|
|
|
|
|
if (dframe->mp.rtp) {
|
|
|
|
|
// adjust output seq num. pushing packets into the delay buffer looks as if they
|
|
|
|
|
// were consumed and resulted in a decreased seq_diff. we need to adjust the
|
|
|
|
|
// perceived seq num forward to compensate for this. each entry in the buffer
|
|
|
|
|
// accounts for one packet that was only delayed and not consumed:
|
|
|
|
|
//LOCK(&dbuf->lock); // XXX needed?
|
|
|
|
|
dframe->mp.rtp->seq_num = htons(ntohs(dframe->mp.rtp->seq_num) + dbuf->frames.length + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct codec_ssrc_handler *csh = dframe->ch;
|
|
|
|
|
|
|
|
|
|
if (csh && csh->handler && csh->encoder) {
|
|
|
|
|
dframe->func(csh->encoder, dframe->frame, csh->handler->packet_encoded,
|
|
|
|
|
csh, &dframe->mp);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Delay buffer bug");
|
|
|
|
|
}
|
|
|
|
|
static void __delay_send_later(struct codec_timer *ct) {
|
|
|
|
|
struct delay_buffer *dbuf = (void *) ct;
|
|
|
|
|
|
|
|
|
|
struct call *call = NULL;
|
|
|
|
|
struct delay_frame *dframe = NULL;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// short-term lock - copy out references to all relevant objects
|
|
|
|
|
LOCK(&dbuf->lock);
|
|
|
|
|
|
|
|
|
|
call = dbuf->call;
|
|
|
|
|
if (call)
|
|
|
|
|
obj_get(call);
|
|
|
|
|
|
|
|
|
|
dframe = g_queue_pop_tail(&dbuf->frames);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!call) // do nothing
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
// we can now do a top-down lock
|
|
|
|
|
rwlock_lock_r(&call->master_lock);
|
|
|
|
|
log_info_call(call);
|
|
|
|
|
|
|
|
|
|
if (!dframe)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
__ssrc_lock_both(&dframe->mp);
|
|
|
|
|
|
|
|
|
|
__delay_frame_process(dbuf, dframe);
|
|
|
|
|
|
|
|
|
|
__ssrc_unlock_both(&dframe->mp);
|
|
|
|
|
|
|
|
|
|
delay_frame_send(dframe);
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// schedule next run
|
|
|
|
|
LOCK(&dbuf->lock);
|
|
|
|
|
dbuf->ct.next.tv_sec = 0;
|
|
|
|
|
__delay_buffer_schedule(dbuf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
// release all references
|
|
|
|
|
if (call) {
|
|
|
|
|
rwlock_unlock_r(&call->master_lock);
|
|
|
|
|
obj_put(call);
|
|
|
|
|
}
|
|
|
|
|
if (dframe)
|
|
|
|
|
delay_frame_free(dframe);
|
|
|
|
|
|
|
|
|
|
log_info_clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
@ -2465,12 +2658,30 @@ static void __dtx_shutdown(struct dtx_buffer *dtxb) {
|
|
|
|
|
dtxb->call = NULL;
|
|
|
|
|
g_queue_clear_full(&dtxb->packets, (GDestroyNotify) dtx_packet_free);
|
|
|
|
|
}
|
|
|
|
|
static void __delay_buffer_shutdown(struct delay_buffer *dbuf, bool flush) {
|
|
|
|
|
if (flush) {
|
|
|
|
|
while (dbuf->frames.length) {
|
|
|
|
|
struct delay_frame *dframe = g_queue_pop_tail(&dbuf->frames);
|
|
|
|
|
delay_frame_flush(dbuf, dframe);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
g_queue_clear_full(&dbuf->frames, (GDestroyNotify) delay_frame_free);
|
|
|
|
|
if (dbuf->call)
|
|
|
|
|
obj_put(dbuf->call);
|
|
|
|
|
dbuf->call = NULL;
|
|
|
|
|
}
|
|
|
|
|
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 __delay_buffer_free(void *p) {
|
|
|
|
|
struct delay_buffer *dbuf = p;
|
|
|
|
|
__delay_buffer_shutdown(dbuf, false);
|
|
|
|
|
mutex_destroy(&dbuf->lock);
|
|
|
|
|
}
|
|
|
|
|
static void __dtx_setup(struct codec_ssrc_handler *ch) {
|
|
|
|
|
if (!decoder_has_dtx(ch->decoder) || ch->dtx_buffer)
|
|
|
|
|
return;
|
|
|
|
|
@ -2495,6 +2706,30 @@ static void __dtx_setup(struct codec_ssrc_handler *ch) {
|
|
|
|
|
dtx->clockrate = ch->handler->source_pt.clock_rate;
|
|
|
|
|
dtx->tspp = dtx->ptime * dtx->clockrate / 1000;
|
|
|
|
|
}
|
|
|
|
|
static void __delay_buffer_setup(struct delay_buffer **dbufp,
|
|
|
|
|
struct codec_handler *h, struct call *call, unsigned int delay)
|
|
|
|
|
{
|
|
|
|
|
if (!dbufp)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
struct delay_buffer *dbuf = *dbufp;
|
|
|
|
|
|
|
|
|
|
if (dbuf) {
|
|
|
|
|
// update options
|
|
|
|
|
dbuf->delay = delay;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!delay)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
dbuf = obj_alloc0("delay_buffer", sizeof(*dbuf), __delay_buffer_free);
|
|
|
|
|
dbuf->ct.tt_obj.tt = &codec_timers_thread;
|
|
|
|
|
dbuf->ct.func = __delay_send_later;
|
|
|
|
|
dbuf->handler = h;
|
|
|
|
|
dbuf->call = obj_get(call);
|
|
|
|
|
dbuf->delay = delay;
|
|
|
|
|
mutex_init(&dbuf->lock);
|
|
|
|
|
}
|
|
|
|
|
static void __ssrc_handler_stop(void *p, void *dummy) {
|
|
|
|
|
struct codec_ssrc_handler *ch = p;
|
|
|
|
|
if (ch->dtx_buffer) {
|
|
|
|
|
@ -2508,6 +2743,14 @@ static void __ssrc_handler_stop(void *p, void *dummy) {
|
|
|
|
|
void codec_handlers_stop(GQueue *q) {
|
|
|
|
|
for (GList *l = q->head; l; l = l->next) {
|
|
|
|
|
struct codec_handler *h = l->data;
|
|
|
|
|
|
|
|
|
|
if (h->delay_buffer) {
|
|
|
|
|
mutex_lock(&h->delay_buffer->lock);
|
|
|
|
|
__delay_buffer_shutdown(h->delay_buffer, true);
|
|
|
|
|
mutex_unlock(&h->delay_buffer->lock);
|
|
|
|
|
|
|
|
|
|
delay_buffer_stop(&h->delay_buffer);
|
|
|
|
|
}
|
|
|
|
|
ssrc_hash_foreach(h->ssrc_hash, __ssrc_handler_stop, NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -2841,8 +3084,7 @@ static void __dtmf_detect(struct codec_ssrc_handler *ch, AVFrame *frame) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int packet_decoded_common(decoder_t *decoder, AVFrame *frame, void *u1, void *u2,
|
|
|
|
|
int (*input_func)(encoder_t *enc, AVFrame *frame,
|
|
|
|
|
int (*callback)(encoder_t *, void *u1, void *u2), void *u1, void *u2))
|
|
|
|
|
encoder_input_func_t input_func)
|
|
|
|
|
{
|
|
|
|
|
struct codec_ssrc_handler *ch = u1;
|
|
|
|
|
struct media_packet *mp = u2;
|
|
|
|
|
@ -2892,7 +3134,8 @@ static int packet_decoded_common(decoder_t *decoder, AVFrame *frame, void *u1, v
|
|
|
|
|
if (mp->media_out)
|
|
|
|
|
ch->encoder->codec_options.amr.cmr = mp->media_out->u.amr.cmr;
|
|
|
|
|
|
|
|
|
|
input_func(ch->encoder, frame, h->packet_encoded, ch, mp);
|
|
|
|
|
__buffer_delay_frame(h->delay_buffer, ch, input_func, frame, mp);
|
|
|
|
|
frame = NULL; // consumed
|
|
|
|
|
|
|
|
|
|
discard:
|
|
|
|
|
av_frame_free(&frame);
|
|
|
|
|
|