From 1e0765bf6ef509cf0001cb8eabf5e25d9a6a51ab Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 8 Feb 2021 12:07:35 -0500 Subject: [PATCH] TT#91151 tie codec handlers to output media/sink Change-Id: Id577f9afabbe5645a6e220b6450c39a35ff839a9 --- daemon/call_interfaces.c | 15 ++++++++- daemon/codec.c | 72 ++++++++++++++++++++++++++++------------ daemon/dtmf.c | 14 +++++--- daemon/jitter_buffer.c | 2 +- daemon/media_socket.c | 5 +-- include/codec.h | 3 +- include/dtmf.h | 3 +- t/test-transcode.c | 10 +++++- 8 files changed, 91 insertions(+), 33 deletions(-) diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 08cdce10c..7e4575532 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -2383,7 +2383,20 @@ const char *call_play_dtmf_ng(bencode_item_t *input, bencode_item_t *output) { goto out; found:; - err = dtmf_inject(media, code, volume, duration, pause); + struct call_monologue *dialogue = monologue->active_dialogue; + struct call_media *sink = NULL; + for (GList *l = dialogue->medias.head; l; l = l->next) { + sink = l->data; + if (media->type_id != MT_AUDIO) + continue; + goto found_sink; + } + + err = "Sink monologue has no media capable of DTMF playback"; + goto out; + +found_sink: + err = dtmf_inject(media, code, volume, duration, pause, sink); if (err) break; } diff --git a/daemon/codec.c b/daemon/codec.c index 858279147..5990c4694 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -193,6 +193,7 @@ static struct ssrc_entry *__ssrc_handler_transcode_new(void *p); static struct ssrc_entry *__ssrc_handler_new(void *p); static void __ssrc_handler_stop(void *p); static void __free_ssrc_handler(void *); +INLINE struct codec_handler *codec_handler_lookup(GHashTable *ht, int pt, struct call_media *sink); static void __transcode_packet_free(struct transcode_packet *); @@ -263,7 +264,9 @@ void codec_handler_free(struct codec_handler **handler) { *handler = NULL; } -static struct codec_handler *__handler_new(const struct rtp_payload_type *pt, struct call_media *media) { +static struct codec_handler *__handler_new(const struct rtp_payload_type *pt, struct call_media *media, + struct call_media *sink) +{ struct codec_handler *handler = g_slice_alloc0(sizeof(*handler)); handler->source_pt.payload_type = -1; if (pt) @@ -275,6 +278,7 @@ static struct codec_handler *__handler_new(const struct rtp_payload_type *pt, st handler->packet_encoded = packet_encoded_rtp; handler->packet_decoded = packet_decoded_fifo; handler->media = media; + handler->sink = sink; return handler; } @@ -407,7 +411,7 @@ check_output:; struct codec_handler *codec_handler_make_playback(const struct rtp_payload_type *src_pt, const struct rtp_payload_type *dst_pt, unsigned long last_ts, struct call_media *media) { - struct codec_handler *handler = __handler_new(src_pt, media); + struct codec_handler *handler = __handler_new(src_pt, media, NULL); rtp_payload_type_copy(&handler->dest_pt, dst_pt); handler->func = handler_func_playback; handler->ssrc_handler = (void *) __ssrc_handler_transcode_new(handler); @@ -591,7 +595,7 @@ static void __check_dtmf_injector(struct call_media *receiver, struct call_media return; } - parent->dtmf_injector = __handler_new(&src_pt, receiver); + parent->dtmf_injector = __handler_new(&src_pt, receiver, sink); __make_transcoder(parent->dtmf_injector, &parent->dest_pt, output_transcoders, -1, 0, -1); parent->dtmf_injector->func = handler_func_inject_dtmf; } @@ -599,28 +603,28 @@ static void __check_dtmf_injector(struct call_media *receiver, struct call_media -static struct codec_handler *__get_pt_handler(struct call_media *receiver, struct rtp_payload_type *pt) { +static struct codec_handler *__get_pt_handler(struct call_media *receiver, struct rtp_payload_type *pt, + struct call_media *sink) +{ ensure_codec_def(pt, receiver); struct codec_handler *handler; - handler = g_hash_table_lookup(receiver->codec_handlers, GINT_TO_POINTER(pt->payload_type)); + handler = codec_handler_lookup(receiver->codec_handlers, pt->payload_type, sink); if (handler) { // make sure existing handler matches this PT if (rtp_payload_type_cmp(pt, &handler->source_pt)) { ilogs(codec, LOG_DEBUG, "Resetting codec handler for PT %i", pt->payload_type); + g_hash_table_remove(receiver->codec_handlers, handler); __handler_shutdown(handler); handler = NULL; g_atomic_pointer_set(&receiver->codec_handler_cache, NULL); - g_hash_table_remove(receiver->codec_handlers, GINT_TO_POINTER(pt->payload_type)); } } if (!handler) { ilogs(codec, LOG_DEBUG, "Creating codec handler for " STR_FORMAT " (%i)", STR_FMT(&pt->encoding_with_params), pt->payload_type); - handler = __handler_new(pt, receiver); - g_hash_table_insert(receiver->codec_handlers, - GINT_TO_POINTER(handler->source_pt.payload_type), - handler); + handler = __handler_new(pt, receiver, sink); + g_hash_table_insert(receiver->codec_handlers, handler, handler); g_queue_push_tail(&receiver->codec_handlers_store, handler); } @@ -640,7 +644,7 @@ static void __check_t38_decoder(struct call_media *t38_media) { if (t38_media->t38_handler) return; ilogs(codec, LOG_DEBUG, "Creating T.38 packet handler"); - t38_media->t38_handler = __handler_new(NULL, t38_media); + t38_media->t38_handler = __handler_new(NULL, t38_media, NULL); t38_media->t38_handler->func = handler_func_t38; } @@ -702,7 +706,7 @@ static void __check_t38_gateway(struct call_media *pcm_media, struct call_media // links to the T.38 encoder for (GList *l = pcm_media->codecs.codec_prefs.head; l; l = l->next) { struct rtp_payload_type *pt = l->data; - struct codec_handler *handler = __get_pt_handler(pcm_media, pt); + struct codec_handler *handler = __get_pt_handler(pcm_media, pt, t38_media); if (!pt->codec_def) { // should not happen ilogs(codec, LOG_WARN, "Unsupported codec " STR_FORMAT " for T.38 transcoding", @@ -831,6 +835,29 @@ static void __codec_rtcp_timer(struct call_media *receiver) { // XXX unify with media player into a generic RTCP player } +static unsigned int __codec_handler_hash(const void *p) { + const struct codec_handler *h = p; + return h->source_pt.payload_type ^ GPOINTER_TO_UINT(h->sink); +} +static int __codec_handler_eq(const void *a, const void *b) { + const struct codec_handler *h = a, *j = b; + return h->source_pt.payload_type == j->source_pt.payload_type + && h->sink == j->sink; +} +INLINE struct codec_handler __codec_handler_lookup_struct(int pt, struct call_media *sink) { + struct codec_handler lookup = { + .source_pt = { + .payload_type = pt, + }, + .sink = sink, + }; + return lookup; +} +INLINE struct codec_handler *codec_handler_lookup(GHashTable *ht, int pt, struct call_media *sink) { + struct codec_handler lookup = __codec_handler_lookup_struct(pt, sink); + return g_hash_table_lookup(ht, &lookup); +} + // call must be locked in W void codec_handlers_update(struct call_media *receiver, struct call_media *sink, const struct sdp_ng_flags *flags, const struct stream_params *sp) @@ -855,7 +882,7 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink, } if (!receiver->codec_handlers) - receiver->codec_handlers = g_hash_table_new(g_direct_hash, g_direct_equal); + receiver->codec_handlers = g_hash_table_new(__codec_handler_hash, __codec_handler_eq); // should we transcode to a non-RTP protocol? if (proto_is_not_rtp(sink->protocol)) { @@ -894,7 +921,7 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink, ilogs(codec, LOG_DEBUG, "Checking receiver codec " STR_FORMAT " (%i)", STR_FMT(&pt->encoding_with_full_params), pt->payload_type); - struct codec_handler *handler = __get_pt_handler(receiver, pt); + struct codec_handler *handler = __get_pt_handler(receiver, pt, sink); // check our own support for this codec if (!pt->codec_def) { @@ -1136,19 +1163,22 @@ next: } -static struct codec_handler *codec_handler_get_rtp(struct call_media *m, int payload_type) { +static struct codec_handler *codec_handler_get_rtp(struct call_media *m, int payload_type, + struct call_media *sink) +{ struct codec_handler *h; if (payload_type < 0) return NULL; + struct codec_handler lookup = __codec_handler_lookup_struct(payload_type, sink); h = g_atomic_pointer_get(&m->codec_handler_cache); - if (G_LIKELY(G_LIKELY(h) && G_LIKELY(h->source_pt.payload_type == payload_type))) + if (G_LIKELY(G_LIKELY(h) && G_LIKELY(__codec_handler_eq(&lookup, h)))) return h; if (G_UNLIKELY(!m->codec_handlers)) return NULL; - h = g_hash_table_lookup(m->codec_handlers, GINT_TO_POINTER(payload_type)); + h = g_hash_table_lookup(m->codec_handlers, &lookup); if (!h) return NULL; @@ -1233,7 +1263,7 @@ void mqtt_timer_stop(struct mqtt_timer **mqtp) { // call must be locked in R -struct codec_handler *codec_handler_get(struct call_media *m, int payload_type) { +struct codec_handler *codec_handler_get(struct call_media *m, int payload_type, struct call_media *sink) { #ifdef WITH_TRANSCODING struct codec_handler *ret = NULL; @@ -1241,7 +1271,7 @@ struct codec_handler *codec_handler_get(struct call_media *m, int payload_type) goto out; if (m->protocol->rtp) - ret = codec_handler_get_rtp(m, payload_type); + ret = codec_handler_get_rtp(m, payload_type, sink); else if (m->protocol->index == PROTO_UDPTL) ret = codec_handler_get_udptl(m); @@ -1669,7 +1699,7 @@ static struct codec_handler *__input_handler(struct codec_handler *h, struct med if (prim_pt == 255) continue; - struct codec_handler *sequencer_h = codec_handler_get(mp->media, prim_pt); + struct codec_handler *sequencer_h = codec_handler_get(mp->media, prim_pt, mp->media_out); if (sequencer_h == h) continue; if (sequencer_h->source_pt.codec_def && sequencer_h->source_pt.codec_def->supplemental) @@ -3570,7 +3600,7 @@ void codec_store_answer(struct codec_store *dst, struct codec_store *src, struct add_codec = 0; struct rtp_payload_type *pt = l->data; - struct codec_handler *h = codec_handler_get(src_media, pt->payload_type); + struct codec_handler *h = codec_handler_get(src_media, pt->payload_type, dst_media); if (!h || h->dest_pt.payload_type == -1) { // passthrough or missing if (pt->for_transcoding) diff --git a/daemon/dtmf.c b/daemon/dtmf.c index 78c1039fb..511f998bf 100644 --- a/daemon/dtmf.c +++ b/daemon/dtmf.c @@ -228,7 +228,8 @@ static char dtmf_code_to_char(int code) { } // takes over the csh reference -static const char *dtmf_inject_pcm(struct call_media *media, struct call_monologue *monologue, +static const char *dtmf_inject_pcm(struct call_media *media, struct call_media *sink, + struct call_monologue *monologue, struct packet_stream *ps, struct ssrc_ctx *ssrc_in, struct codec_handler *ch, struct codec_ssrc_handler *csh, int code, int volume, int duration, int pause) @@ -265,7 +266,7 @@ static const char *dtmf_inject_pcm(struct call_media *media, struct call_monolog .tv = rtpe_now, .call = call, .media = media, - .media_out = media, + .media_out = sink, .rtp = &rtp, .ssrc_in = ssrc_in, .ssrc_out = ssrc_out, @@ -301,7 +302,9 @@ static const char *dtmf_inject_pcm(struct call_media *media, struct call_monolog return 0; } -const char *dtmf_inject(struct call_media *media, int code, int volume, int duration, int pause) { +const char *dtmf_inject(struct call_media *media, int code, int volume, int duration, int pause, + struct call_media *sink) +{ struct call_monologue *monologue = media->monologue; if (!media->streams.head) @@ -321,7 +324,7 @@ const char *dtmf_inject(struct call_media *media, int code, int volume, int dura if (pt == 255) continue; - ch = codec_handler_get(media, pt); + ch = codec_handler_get(media, pt, sink); if (!ch) continue; if (ch->output_handler && ch->output_handler->ssrc_hash) // context switch if we have multiple inputs going to one output @@ -353,7 +356,8 @@ const char *dtmf_inject(struct call_media *media, int code, int volume, int dura // if we don't have a DTMF payload type, we have to generate PCM if (ch->dtmf_payload_type == -1 && ch->dtmf_injector) - return dtmf_inject_pcm(media, monologue, ps, ssrc_in, ch, csh, code, volume, duration, pause); + return dtmf_inject_pcm(media, sink, monologue, ps, ssrc_in, ch, csh, code, volume, duration, + pause); ilog(LOG_DEBUG, "Injecting RFC DTMF event #%i for %i ms (vol %i) from '" STR_FORMAT "' (media #%u) " "into RTP PT %i, SSRC %" PRIx32, diff --git a/daemon/jitter_buffer.c b/daemon/jitter_buffer.c index 26593ecb6..ae11e1198 100644 --- a/daemon/jitter_buffer.c +++ b/daemon/jitter_buffer.c @@ -66,7 +66,7 @@ static void reset_jitter_buffer(struct jitter_buffer *jb) { static struct rtp_payload_type *get_rtp_payload_type(struct media_packet *mp, int payload_type) { struct rtp_payload_type *rtp_pt = NULL; - struct codec_handler *transcoder = codec_handler_get(mp->media, payload_type); + struct codec_handler *transcoder = codec_handler_get(mp->media, payload_type, mp->media_out); if(transcoder) { if(transcoder->source_pt.payload_type == payload_type) rtp_pt = &transcoder->source_pt; diff --git a/daemon/media_socket.c b/daemon/media_socket.c index 707551cc4..8f246b74b 100644 --- a/daemon/media_socket.c +++ b/daemon/media_socket.c @@ -1233,7 +1233,7 @@ void kernelize(struct packet_stream *stream) { } rs = l->data; // only add payload types that are passthrough - struct codec_handler *ch = codec_handler_get(media, rs->payload_type); + struct codec_handler *ch = codec_handler_get(media, rs->payload_type, sink->media); if (!ch->kernelize) continue; reti.payload_types[reti.num_payload_types] = rs->payload_type; @@ -2105,7 +2105,8 @@ static int stream_packet(struct packet_handler_ctx *phc) { goto drop; } else { - struct codec_handler *transcoder = codec_handler_get(phc->mp.media, phc->payload_type); + struct codec_handler *transcoder = codec_handler_get(phc->mp.media, phc->payload_type, + phc->mp.media_out); // this transfers the packet from 's' to 'packets_out' if (transcoder->func(transcoder, &phc->mp)) goto drop; diff --git a/include/codec.h b/include/codec.h index 5adebaf22..6f0dd7707 100644 --- a/include/codec.h +++ b/include/codec.h @@ -44,6 +44,7 @@ struct codec_handler { struct codec_handler *input_handler; // == main handler for supp codecs struct codec_handler *output_handler; // == self, or other PT handler struct call_media *media; + struct call_media *sink; #ifdef WITH_TRANSCODING int (*packet_encoded)(encoder_t *enc, void *u1, void *u2); int (*packet_decoded)(decoder_t *, AVFrame *, void *, void *); @@ -78,7 +79,7 @@ void rtcp_timer_stop(struct rtcp_timer **); void mqtt_timer_stop(struct mqtt_timer **); void mqtt_timer_start(struct mqtt_timer **mqtp, struct call *call, struct call_media *media); -struct codec_handler *codec_handler_get(struct call_media *, int payload_type); +struct codec_handler *codec_handler_get(struct call_media *, int payload_type, struct call_media *sink); void codec_handlers_free(struct call_media *); struct codec_handler *codec_handler_make_playback(const struct rtp_payload_type *src_pt, const struct rtp_payload_type *dst_pt, unsigned long ts, struct call_media *); diff --git a/include/dtmf.h b/include/dtmf.h index 64bc3f680..b80c85990 100644 --- a/include/dtmf.h +++ b/include/dtmf.h @@ -22,7 +22,8 @@ int dtmf_event(struct media_packet *, str *, int); int dtmf_event_payload(str *, uint64_t *, uint64_t, struct dtmf_event *, GQueue *); void dtmf_event_free(void *); int dtmf_code_from_char(char); -const char *dtmf_inject(struct call_media *media, int code, int volume, int duration, int pause); +const char *dtmf_inject(struct call_media *media, int code, int volume, int duration, int pause, + struct call_media *sink); int dtmf_do_logging(void); diff --git a/t/test-transcode.c b/t/test-transcode.c index e8c2f01a3..3f4454f42 100644 --- a/t/test-transcode.c +++ b/t/test-transcode.c @@ -212,7 +212,14 @@ static void __packet_seq_ts(const char *file, int line, struct call_media *media long long ts_exp, int seq_diff_exp, int fatal) { printf("running test %s:%i\n", file, line); - struct codec_handler *h = codec_handler_get(media, pt_in & 0x7f); + struct call_media *other_media; + if (media == media_A) + other_media = media_B; + else if (media == media_B) + other_media = media_A; + else + abort(); + struct codec_handler *h = codec_handler_get(media, pt_in & 0x7f, other_media); str pl = pload; str pl_exp = pload_exp; @@ -220,6 +227,7 @@ static void __packet_seq_ts(const char *file, int line, struct call_media *media struct media_packet mp = { .call = &call, .media = media, + .media_out = other_media, .ssrc_in = get_ssrc_ctx(ssrc, media->monologue->ssrc_hash, SSRC_DIR_INPUT, NULL), }; // from __stream_ssrc()