|
|
|
@ -85,6 +85,11 @@ struct dtx_entry {
|
|
|
|
void *ssrc_ptr; // opaque pointer, doesn't hold a reference
|
|
|
|
void *ssrc_ptr; // opaque pointer, doesn't hold a reference
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct silence_event {
|
|
|
|
|
|
|
|
uint64_t start;
|
|
|
|
|
|
|
|
uint64_t end;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct codec_ssrc_handler {
|
|
|
|
struct codec_ssrc_handler {
|
|
|
|
struct ssrc_entry h; // must be first
|
|
|
|
struct ssrc_entry h; // must be first
|
|
|
|
struct codec_handler *handler;
|
|
|
|
struct codec_handler *handler;
|
|
|
|
@ -109,6 +114,9 @@ struct codec_ssrc_handler {
|
|
|
|
GQueue dtmf_events;
|
|
|
|
GQueue dtmf_events;
|
|
|
|
struct dtmf_event dtmf_event;
|
|
|
|
struct dtmf_event dtmf_event;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// silence detection
|
|
|
|
|
|
|
|
GQueue silence_events;
|
|
|
|
|
|
|
|
|
|
|
|
uint64_t skip_pts;
|
|
|
|
uint64_t skip_pts;
|
|
|
|
|
|
|
|
|
|
|
|
int rtp_mark:1;
|
|
|
|
int rtp_mark:1;
|
|
|
|
@ -1169,6 +1177,7 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
|
|
|
|
struct rtp_payload_type *dtmf_pt = NULL;
|
|
|
|
struct rtp_payload_type *dtmf_pt = NULL;
|
|
|
|
struct rtp_payload_type *reverse_dtmf_pt = NULL;
|
|
|
|
struct rtp_payload_type *reverse_dtmf_pt = NULL;
|
|
|
|
int dtmf_pt_match = __supp_codec_match(receiver, sink, dtmf_payload_type, &dtmf_pt, &reverse_dtmf_pt);
|
|
|
|
int dtmf_pt_match = __supp_codec_match(receiver, sink, dtmf_payload_type, &dtmf_pt, &reverse_dtmf_pt);
|
|
|
|
|
|
|
|
int cn_pt_match = __supp_codec_match(receiver, sink, cn_payload_type, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
|
|
// stop transcoding if we've determined that we don't need it
|
|
|
|
// stop transcoding if we've determined that we don't need it
|
|
|
|
if (MEDIA_ISSET(sink, TRANSCODE) && !sink_transcoding) {
|
|
|
|
if (MEDIA_ISSET(sink, TRANSCODE) && !sink_transcoding) {
|
|
|
|
@ -1240,8 +1249,8 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
|
|
|
|
|
|
|
|
|
|
|
|
GQueue *dest_codecs = NULL;
|
|
|
|
GQueue *dest_codecs = NULL;
|
|
|
|
if (!flags || !flags->always_transcode) {
|
|
|
|
if (!flags || !flags->always_transcode) {
|
|
|
|
// we ignore output codec matches if we must transcode DTMF
|
|
|
|
// we ignore output codec matches if we must transcode supp codecs
|
|
|
|
if (dtmf_pt_match == 1 && MEDIA_ISSET(sink, TRANSCODE))
|
|
|
|
if ((dtmf_pt_match == 1 || cn_pt_match == 1) && MEDIA_ISSET(sink, TRANSCODE))
|
|
|
|
;
|
|
|
|
;
|
|
|
|
else if (pcm_dtmf_detect)
|
|
|
|
else if (pcm_dtmf_detect)
|
|
|
|
;
|
|
|
|
;
|
|
|
|
@ -1293,6 +1302,11 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
|
|
|
|
if (rtp_payload_type_cmp_nf(pt, dest_pt))
|
|
|
|
if (rtp_payload_type_cmp_nf(pt, dest_pt))
|
|
|
|
goto transcode;
|
|
|
|
goto transcode;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// do we need silence detection?
|
|
|
|
|
|
|
|
if (cn_pt_match == 2 && MEDIA_ISSET(sink, TRANSCODE))
|
|
|
|
|
|
|
|
goto transcode;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// XXX check format parameters as well
|
|
|
|
ilog(LOG_DEBUG, "Sink supports codec " STR_FORMAT, STR_FMT(&pt->encoding_with_params));
|
|
|
|
ilog(LOG_DEBUG, "Sink supports codec " STR_FORMAT, STR_FMT(&pt->encoding_with_params));
|
|
|
|
__make_passthrough_gsl(handler, &passthrough_handlers);
|
|
|
|
__make_passthrough_gsl(handler, &passthrough_handlers);
|
|
|
|
if (pt->codec_def && pt->codec_def->dtmf)
|
|
|
|
if (pt->codec_def && pt->codec_def->dtmf)
|
|
|
|
@ -2198,6 +2212,102 @@ void codec_handlers_stop(GQueue *q) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void silence_event_free(void *p) {
|
|
|
|
|
|
|
|
g_slice_free1(sizeof(struct silence_event), p);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define __silence_detect_type(type) \
|
|
|
|
|
|
|
|
static void __silence_detect_ ## type(struct codec_ssrc_handler *ch, AVFrame *frame, type thres) { \
|
|
|
|
|
|
|
|
type *s = (void *) frame->data[0]; \
|
|
|
|
|
|
|
|
struct silence_event *last = g_queue_peek_tail(&ch->silence_events); \
|
|
|
|
|
|
|
|
\
|
|
|
|
|
|
|
|
if (last && last->end) /* last event finished? */ \
|
|
|
|
|
|
|
|
last = NULL; \
|
|
|
|
|
|
|
|
\
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < frame->nb_samples; i++) { \
|
|
|
|
|
|
|
|
if (s[i] <= thres && s[1] >= -thres) { \
|
|
|
|
|
|
|
|
/* silence */ \
|
|
|
|
|
|
|
|
if (!last) { \
|
|
|
|
|
|
|
|
/* new event */ \
|
|
|
|
|
|
|
|
last = g_slice_alloc0(sizeof(*last)); \
|
|
|
|
|
|
|
|
last->start = frame->pts + i; \
|
|
|
|
|
|
|
|
g_queue_push_tail(&ch->silence_events, last); \
|
|
|
|
|
|
|
|
} \
|
|
|
|
|
|
|
|
} \
|
|
|
|
|
|
|
|
else { \
|
|
|
|
|
|
|
|
/* not silence */ \
|
|
|
|
|
|
|
|
if (last && !last->end) { \
|
|
|
|
|
|
|
|
/* close off event */ \
|
|
|
|
|
|
|
|
last->end = frame->pts + i; \
|
|
|
|
|
|
|
|
last = NULL; \
|
|
|
|
|
|
|
|
} \
|
|
|
|
|
|
|
|
} \
|
|
|
|
|
|
|
|
} \
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
__silence_detect_type(double)
|
|
|
|
|
|
|
|
__silence_detect_type(float)
|
|
|
|
|
|
|
|
__silence_detect_type(int32_t)
|
|
|
|
|
|
|
|
__silence_detect_type(int16_t)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void __silence_detect(struct codec_ssrc_handler *ch, AVFrame *frame) {
|
|
|
|
|
|
|
|
if (!rtpe_config.silence_detect_int)
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ch->handler->cn_payload_type < 0)
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (frame->format) {
|
|
|
|
|
|
|
|
case AV_SAMPLE_FMT_DBL:
|
|
|
|
|
|
|
|
__silence_detect_double(ch, frame, rtpe_config.silence_detect_double);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AV_SAMPLE_FMT_FLT:
|
|
|
|
|
|
|
|
__silence_detect_float(ch, frame, rtpe_config.silence_detect_double);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AV_SAMPLE_FMT_S32:
|
|
|
|
|
|
|
|
__silence_detect_int32_t(ch, frame, rtpe_config.silence_detect_int);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AV_SAMPLE_FMT_S16:
|
|
|
|
|
|
|
|
__silence_detect_int16_t(ch, frame, rtpe_config.silence_detect_int >> 16);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Unsupported sample format %i for silence detection",
|
|
|
|
|
|
|
|
frame->format);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int is_silence_event(str *inout, GQueue *events, uint64_t pts, uint64_t duration) {
|
|
|
|
|
|
|
|
uint64_t end = pts + duration;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (events->length) {
|
|
|
|
|
|
|
|
struct silence_event *first = g_queue_peek_head(events);
|
|
|
|
|
|
|
|
if (first->start > pts) // future event
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!first->end) // ongoing event
|
|
|
|
|
|
|
|
goto silence;
|
|
|
|
|
|
|
|
if (first->end > end) // event finished with end in the future
|
|
|
|
|
|
|
|
goto silence;
|
|
|
|
|
|
|
|
// event has ended: remove it
|
|
|
|
|
|
|
|
g_queue_pop_head(events);
|
|
|
|
|
|
|
|
// does the event fill the entire span?
|
|
|
|
|
|
|
|
if (first->end == end) {
|
|
|
|
|
|
|
|
silence_event_free(first);
|
|
|
|
|
|
|
|
goto silence;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// keep going, there might be more
|
|
|
|
|
|
|
|
silence_event_free(first);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
silence:
|
|
|
|
|
|
|
|
// replace with CN payload
|
|
|
|
|
|
|
|
inout->len = rtpe_config.cn_payload.len;
|
|
|
|
|
|
|
|
memcpy(inout->s, rtpe_config.cn_payload.s, inout->len);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static struct ssrc_entry *__ssrc_handler_transcode_new(void *p) {
|
|
|
|
static struct ssrc_entry *__ssrc_handler_transcode_new(void *p) {
|
|
|
|
struct codec_handler *h = p;
|
|
|
|
struct codec_handler *h = p;
|
|
|
|
|
|
|
|
|
|
|
|
@ -2296,6 +2406,7 @@ static void __free_ssrc_handler(void *chp) {
|
|
|
|
dtmf_rx_free(ch->dtmf_dsp);
|
|
|
|
dtmf_rx_free(ch->dtmf_dsp);
|
|
|
|
resample_shutdown(&ch->dtmf_resampler);
|
|
|
|
resample_shutdown(&ch->dtmf_resampler);
|
|
|
|
g_queue_clear_full(&ch->dtmf_events, dtmf_event_free);
|
|
|
|
g_queue_clear_full(&ch->dtmf_events, dtmf_event_free);
|
|
|
|
|
|
|
|
g_queue_clear_full(&ch->silence_events, silence_event_free);
|
|
|
|
if (ch->dtx_buffer)
|
|
|
|
if (ch->dtx_buffer)
|
|
|
|
obj_put(&ch->dtx_buffer->ttq.tt_obj);
|
|
|
|
obj_put(&ch->dtx_buffer->ttq.tt_obj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -2340,6 +2451,7 @@ static int packet_encoded_rtp(encoder_t *enc, void *u1, void *u2) {
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int repeats = 0;
|
|
|
|
unsigned int repeats = 0;
|
|
|
|
int payload_type = -1;
|
|
|
|
int payload_type = -1;
|
|
|
|
|
|
|
|
|
|
|
|
int is_dtmf = dtmf_event_payload(&inout, (uint64_t *) &enc->avpkt.pts, enc->avpkt.duration,
|
|
|
|
int is_dtmf = dtmf_event_payload(&inout, (uint64_t *) &enc->avpkt.pts, enc->avpkt.duration,
|
|
|
|
&ch->dtmf_event, &ch->dtmf_events);
|
|
|
|
&ch->dtmf_event, &ch->dtmf_events);
|
|
|
|
if (is_dtmf) {
|
|
|
|
if (is_dtmf) {
|
|
|
|
@ -2349,6 +2461,10 @@ static int packet_encoded_rtp(encoder_t *enc, void *u1, void *u2) {
|
|
|
|
else if (is_dtmf == 3)
|
|
|
|
else if (is_dtmf == 3)
|
|
|
|
repeats = 2; // DTMF end event
|
|
|
|
repeats = 2; // DTMF end event
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
if (is_silence_event(&inout, &ch->silence_events, enc->avpkt.pts, enc->avpkt.duration))
|
|
|
|
|
|
|
|
payload_type = ch->handler->cn_payload_type;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ready to send
|
|
|
|
// ready to send
|
|
|
|
|
|
|
|
|
|
|
|
@ -2469,6 +2585,7 @@ static int packet_decoded_common(decoder_t *decoder, AVFrame *frame, void *u1, v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
__dtmf_detect(ch, frame);
|
|
|
|
__dtmf_detect(ch, frame);
|
|
|
|
|
|
|
|
__silence_detect(ch, frame);
|
|
|
|
|
|
|
|
|
|
|
|
// locking deliberately ignored
|
|
|
|
// locking deliberately ignored
|
|
|
|
if (mp->media_out)
|
|
|
|
if (mp->media_out)
|
|
|
|
|