From dba2386c60832fe88d57731d6b20eed5f17b569d Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 27 Aug 2025 12:25:52 -0400 Subject: [PATCH] MT#63317 support extmap-mask Change-Id: Ib8ca6d4ada4a0bc1e67f5c02522eb9fe7f6ba2ce --- daemon/call.c | 102 +++++++++++++++++++++++++++++++----- daemon/call_interfaces.c | 4 ++ docs/ng_control_protocol.md | 5 ++ include/call_interfaces.h | 1 + include/media_socket.h | 1 + 5 files changed, 100 insertions(+), 13 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index fad6f5f77..4277d028d 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2862,14 +2862,53 @@ static void media_set_siprec_label(struct call_media *other_media, struct call_m } __attribute__((nonnull(1))) -static void media_reset_extmap(struct call_media *media) { - // empty out old queue - t_queue_clear_full(&media->extmap, rtp_extension_free); - - t_hash_table_remove_all(media->ext_name_ht); - t_hash_table_remove_all(media->extmap_ht); +static void media_reset_extmap(struct call_media *media, + bool (*exclude)(struct rtp_extension *)) +{ + // reset basic table memset(media->extmap_a, 0, sizeof(media->extmap_a)); media->extmap_lookup = call_media_ext_lookup_array; + + if (!exclude) { + // shortcut, reset everything + t_queue_clear_full(&media->extmap, rtp_extension_free); + + t_hash_table_remove_all(media->ext_name_ht); + t_hash_table_remove_all(media->extmap_ht); + + return; + } + + // empty out old queue + __auto_type ele = media->extmap.head; + while (ele) { + __auto_type ext = ele->data; + + if (exclude(ext)) { + // return to table, check lookup function + if (ext->id > 0 && ext->id <= 14) + media->extmap_a[ext->id - 1] = ext; + else + media->extmap_lookup = call_media_ext_lookup_ht; + + ele = ele->next; + continue; + } + + // remove from tables + t_hash_table_remove(media->ext_name_ht, &ext->name); + t_hash_table_remove(media->extmap_ht, GUINT_TO_POINTER(ext->id)); + + if (ext->id > 0 && ext->id <= 14) + media->extmap_a[ext->id - 1] = NULL; + + // remove from list and free + __auto_type next = ele->next; + t_queue_delete_link(&media->extmap, ele); + ele = next; + + rtp_extension_free(ext); + } } __attribute__((nonnull(1, 2))) @@ -2897,8 +2936,33 @@ static bool media_extmap_strip(const str *name, const struct sdp_ng_flags *flags } __attribute__((nonnull(1, 2))) -static void media_update_extmap(struct call_media *media, struct stream_params *sp) { - media_reset_extmap(media); +static bool media_extmap_strip_mask(const str *name, const struct sdp_ng_flags *flags) { + if (!media_extmap_strip(name, flags)) + return false; + if (t_hash_table_is_set(flags->rtpext_mask)) { + if (t_hash_table_lookup(flags->rtpext_mask, STR_PTR("all"))) + return false; + if (t_hash_table_lookup(flags->rtpext_mask, name)) + return false; + } + return true; +} + +__attribute__((nonnull(1, 2))) +static void media_extmap_accept(struct rtp_extension *ext, const struct sdp_ng_flags *flags) { + if (t_hash_table_is_set(flags->rtpext_mask)) { + if (t_hash_table_lookup(flags->rtpext_mask, &ext->name) + || t_hash_table_lookup(flags->rtpext_mask, STR_PTR("all"))) + ext->accepted = true; + } +} + +__attribute__((nonnull(1, 2, 4))) +static void media_update_extmap(struct call_media *media, struct stream_params *sp, + void (*manip)(struct rtp_extension *, const struct sdp_ng_flags *), + const struct sdp_ng_flags *flags) +{ + media_reset_extmap(media, NULL); // take over from `sp` media->extmap = sp->extmap; @@ -2909,14 +2973,24 @@ static void media_update_extmap(struct call_media *media, struct stream_params * __auto_type ext = ll->data; media_init_extmap(media, ext); + + if (manip) + manip(ext, flags); } } +__attribute__((nonnull(1))) +static bool media_extmap_exclude_accepted(struct rtp_extension *ext) { + if (ext->accepted) + return true; + return false; +} + __attribute__((nonnull(1, 2, 4))) static void media_set_extmap(struct call_media *media, const extmap_q *emq, bool (*manip)(const str *, const struct sdp_ng_flags *), const struct sdp_ng_flags *flags) { - media_reset_extmap(media); + media_reset_extmap(media, media_extmap_exclude_accepted); // copy entries for (__auto_type ll = emq->head; ll; ll = ll->next) { @@ -3301,8 +3375,10 @@ int monologue_offer_answer(struct call_monologue *monologues[2], sdp_streams_q * media_set_address_family(receiver_media, sender_media, flags); media_set_ptime(sender_media, sp, flags->rev_ptime, flags->ptime); media_set_ptime(receiver_media, sp, flags->ptime, flags->rev_ptime); - media_update_extmap(sender_media, sp); - media_set_extmap(receiver_media, &sender_media->extmap, media_extmap_strip, flags); + media_update_extmap(sender_media, sp, + flags->opmode == OP_OFFER ? media_extmap_accept : NULL, flags); + media_set_extmap(receiver_media, &sender_media->extmap, + flags->opmode == OP_OFFER ? media_extmap_strip_mask : media_extmap_strip, flags); if (flags->opmode == OP_OFFER) { ilog(LOG_DEBUG, "Setting media recording slots to %u", flags->media_rec_slot_offer); @@ -3718,7 +3794,7 @@ int monologue_publish(struct call_monologue *ml, sdp_streams_q *streams, sdp_ng_ media_update_attrs(media, sp); media_update_format(media, sp); media_set_ptime(media, sp, flags->ptime, 0); - media_update_extmap(media, sp); + media_update_extmap(media, sp, NULL, flags); codec_store_populate(&media->codecs, &sp->codecs, .allow_asymmetric = !!flags->allow_asymmetric_codecs); @@ -3925,7 +4001,7 @@ int monologue_subscribe_answer(struct call_monologue *dst_ml, sdp_ng_flags *flag media_update_crypto(dst_media, sp, flags); media_update_format(dst_media, sp); media_set_ptime(dst_media, sp, flags->ptime, 0); - media_update_extmap(dst_media, sp); + media_update_extmap(dst_media, sp, NULL, flags); if (flags->allow_transcoding) { codec_store_populate(&dst_media->codecs, &sp->codecs, diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 8bfe67d14..5ed1d231f 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -1524,6 +1524,10 @@ void call_ng_extmap_flags(const ng_parser_t *parser, str *key, parser_arg value, case CSH_LOOKUP("strip"): call_ng_flags_str_list(parser, value, call_ng_flags_str_ht, &out->rtpext_strip); break; + + case CSH_LOOKUP("mask"): + call_ng_flags_str_list(parser, value, call_ng_flags_str_ht, &out->rtpext_mask); + break; } } diff --git a/docs/ng_control_protocol.md b/docs/ng_control_protocol.md index 577bdba12..946fef317 100644 --- a/docs/ng_control_protocol.md +++ b/docs/ng_control_protocol.md @@ -188,6 +188,11 @@ Optionally included keys are: appear in the SDP, usually an URI or an `ietf:` registered name. The string `all` can be given to strip all extensions. + The contained key `mask` lists header extensions that are to be removed + from an outgoing SDP, but are still accepted towards the sender of the + message. Using this mode, extensions are removed from the RTP header during + media forwarding. The string `all` can be given here as well. + Example in string syntax: `extmap=[strip=[all]]` * `delay-buffer` diff --git a/include/call_interfaces.h b/include/call_interfaces.h index f7ea08032..ebf7f7c5d 100644 --- a/include/call_interfaces.h +++ b/include/call_interfaces.h @@ -46,6 +46,7 @@ struct ng_media { #define RTPE_NG_FLAGS_STR_CASE_HT_PARAMS \ X(rtpext_strip) \ + X(rtpext_mask) \ X(codec_except) \ X(sdes_no) /* individual crypto suites which are excluded */ \ X(sdes_only) /* individual crypto suites which are only accepted */ diff --git a/include/media_socket.h b/include/media_socket.h index 555cb4762..c502fbf69 100644 --- a/include/media_socket.h +++ b/include/media_socket.h @@ -290,6 +290,7 @@ struct media_packet { struct rtp_extension { unsigned int id; str name; // urn:ietf:params:rtp- hdrext:... or URI + bool accepted:1; }; static inline void rtp_extension_free(struct rtp_extension *r) {