From fc9cb21d8a18cf753f4f1f1507ac1dd91d198f3d Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 28 Jul 2025 12:01:45 -0400 Subject: [PATCH] MT#63317 store extmap entries in media object Change-Id: Ia673ae3049f5c6ce8dd8516b5becc0a9322559d8 --- daemon/call.c | 23 ++++++++++++++++ daemon/sdp.c | 60 +++++++++++++++++++++++++++++++++++++----- include/call.h | 3 +++ include/media_socket.h | 10 +++++++ lib/rtplib.c | 4 +-- t/auto-daemon-tests.pl | 6 +++-- 6 files changed, 96 insertions(+), 10 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index f0dba2046..a3a1d4409 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2848,6 +2848,25 @@ static void media_set_siprec_label(struct call_media *other_media, struct call_m other_media->label = media->label; } +__attribute__((nonnull(1, 2))) +static void media_init_extmap(struct call_media *media, struct rtp_extension *ext) { + ext->name = call_str_cpy(&ext->name); +} + +__attribute__((nonnull(1, 2))) +static void media_update_extmap(struct call_media *media, struct stream_params *sp) { + // empty out old queue and take over from `sp` + t_queue_clear_full(&media->extmap, rtp_extension_free); + media->extmap = sp->extmap; + t_queue_init(&sp->extmap); + + // copy strings + for (__auto_type ll = media->extmap.head; ll; ll = ll->next) { + __auto_type ext = ll->data; + media_init_extmap(media, ext); + } +} + __attribute__((nonnull(1, 2))) static void media_init_from_flags(struct call_media *media, sdp_ng_flags *flags) { if (flags->opmode == OP_OFFER && flags->reset) { @@ -3086,6 +3105,7 @@ static struct call_media * monologue_add_zero_media(struct call_monologue *sende media_update_attrs(sender_media, sp); media_update_format(sender_media, sp); media_set_ptime(sender_media, sp, flags->rev_ptime, flags->ptime); + media_update_extmap(sender_media, sp); *num_ports_other = proto_num_ports(sp->num_ports, sender_media, flags, (flags->rtcp_mux_demux || flags->rtcp_mux_accept) ? true : false); __disable_streams(sender_media, *num_ports_other); @@ -3216,6 +3236,7 @@ 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); if (flags->opmode == OP_OFFER) { ilog(LOG_DEBUG, "Setting media recording slots to %u", flags->media_rec_slot_offer); @@ -3631,6 +3652,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); codec_store_populate(&media->codecs, &sp->codecs, .allow_asymmetric = !!flags->allow_asymmetric_codecs); @@ -4375,6 +4397,7 @@ void call_media_free(struct call_media **mdp) { mutex_destroy(&md->dtmf_lock); ssrc_hash_destroy(&md->ssrc_hash_in); ssrc_hash_destroy(&md->ssrc_hash_out); + t_queue_clear_full(&md->extmap, rtp_extension_free); g_free(md); *mdp = NULL; } diff --git a/daemon/sdp.c b/daemon/sdp.c index 2ed3a2985..52bae25e9 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -66,6 +66,7 @@ enum attr_id { ATTR_TLS_ID, ATTR_END_OF_CANDIDATES, ATTR_MOH_ATTR_NAME, + ATTR_EXTMAP, }; // make sure g_direct_hash can be used static_assert(sizeof(void *) >= sizeof(enum attr_id), "sizeof enum attr_id wrong"); @@ -255,9 +256,15 @@ struct attribute_t38faxudpecdepth { int maxred; }; +struct attribute_extmap { + str id_str; + str ext; + + int id; +}; + enum attribute_other { ATTR_OTHER_UNKNOWN = 0, - ATTR_OTHER_EXTMAP, }; struct sdp_attribute { @@ -283,6 +290,7 @@ struct sdp_attribute { int i; struct attribute_t38faxudpecdepth t38faxudpecdepth; struct attribute_t38faxratemanagement t38faxratemanagement; + struct attribute_extmap extmap; enum attribute_other other; }; }; @@ -1147,6 +1155,22 @@ static bool parse_attribute_t38faxudpecdepth(struct sdp_attribute *output) { return true; } +static bool parse_attribute_extmap(struct sdp_attribute *output) { + output->attr = ATTR_EXTMAP; + + PARSE_INIT; + EXTRACT_TOKEN(extmap.id_str); + EXTRACT_TOKEN(extmap.ext); + + output->extmap.id = str_to_i(&output->extmap.id_str, 0); + // RFC 8285, valid range: 1-14, 15 reserved, 16-255, 256 appbits (not supported), + // 256-4095 invalid, 4096-4351 remap (not supported) + if (output->extmap.id <= 0 || output->extmap.id == 15 || output->extmap.id >= 256) + return false; + + return true; +} + static bool parse_attribute(struct sdp_attribute *a) { a->strs.name = a->strs.line_value; @@ -1194,7 +1218,7 @@ static bool parse_attribute(struct sdp_attribute *a) { ret = parse_attribute_crypto(a); break; case CSH_LOOKUP("extmap"): - a->other = ATTR_OTHER_EXTMAP; + ret = parse_attribute_extmap(a); break; case CSH_LOOKUP("rtpmap"): ret = parse_attribute_rtpmap(a); @@ -1750,6 +1774,7 @@ static void sp_free(struct stream_params *s) { crypto_params_sdes_queue_clear(&s->sdes_params); t_queue_clear_full(&s->generic_attributes, sdp_attr_free); t_queue_clear_full(&s->all_attributes, sdp_attr_free); + t_queue_clear_full(&s->extmap, rtp_extension_free); g_free(s); } @@ -2078,6 +2103,18 @@ bool sdp_streams(const sdp_sessions_q *sessions, sdp_streams_q *streams, sdp_ng_ __sdp_t38(sp, media); + // a=extmap + attrs = attr_list_get_by_id(&media->attributes, ATTR_EXTMAP); + if (!attrs) + attrs = attr_list_get_by_id(&session->attributes, ATTR_EXTMAP); + for (__auto_type ll = attrs ? attrs->head : NULL; ll; ll = ll->next) { + attr = ll->data; + __auto_type ext = g_new0(struct rtp_extension, 1); + ext->id = attr->extmap.id; + ext->name = attr->extmap.ext; + t_queue_push_tail(&sp->extmap, ext); + } + /* determine RTCP endpoint */ if (attr_get_by_id(&media->attributes, ATTR_RTCP_MUX)) @@ -2188,8 +2225,6 @@ void sdp_insert_media_attributes(GString *gs, struct call_media *media, struct c return; for (__auto_type l = source_media->generic_attributes.head; l; l = l->next) { __auto_type s = l->data; - if (s->other == ATTR_OTHER_EXTMAP && flags->strip_extmap && !MEDIA_ISSET(source_media, PASSTHRU)) - continue; append_gen_attr_to_gstring(gs, &s->strs.name, &s->strs.value, flags, source_media->type_id); } } @@ -2203,8 +2238,6 @@ void sdp_insert_monologue_attributes(GString *gs, struct call_monologue *ml, con for (__auto_type l = source_ml->generic_attributes.head; l; l = l->next) { __auto_type s = l->data; - if (s->other == ATTR_OTHER_EXTMAP && flags->strip_extmap) - continue; append_gen_attr_to_gstring(gs, &s->strs.name, &s->strs.value, flags, MT_UNKNOWN); } } @@ -2219,6 +2252,19 @@ void sdp_insert_all_attributes(GString *s, struct call_media *media, struct sdp_ } } +static void sdp_print_extmap(GString *s, struct call_media *source_media, const sdp_ng_flags *flags) { + if (!source_media) + return; + if (flags->strip_extmap && !MEDIA_ISSET(source_media, PASSTHRU)) + return; + + for (__auto_type l = source_media->extmap.head; l; l = l->next) { + __auto_type ext = l->data; + sdp_append_attr(s, flags, source_media->type_id, + "extmap", "%u " STR_FORMAT, ext->id, STR_FMT(&ext->name)); + } +} + static bool insert_ice_address(const struct sdp_state *state, stream_fd *sfd, const sdp_ng_flags *flags) { if (!is_addr_unspecified(&flags->media_address)) sockaddr_print_gstring(state->s, &flags->media_address); @@ -2806,6 +2852,8 @@ static void print_sdp_media_section(GString *s, struct call_media *media, if (proto_is_rtp(media->protocol)) insert_codec_parameters(s, media, flags); + sdp_print_extmap(s, source_media, flags); + /* all unknown type attributes will be added here */ media->sdp_attr_print(s, media, source_media, flags); diff --git a/include/call.h b/include/call.h index 86193c14d..42a1c4405 100644 --- a/include/call.h +++ b/include/call.h @@ -378,6 +378,7 @@ struct stream_params { int media_sdp_id; struct session_bandwidth media_session_bandiwdth; str sdp_information; + extmap_q extmap; }; struct endpoint_map { @@ -484,6 +485,8 @@ struct call_media { struct ice_agent *ice_agent; + extmap_q extmap; // container + str media_id; str label; sdes_q sdes_in, sdes_out; diff --git a/include/media_socket.h b/include/media_socket.h index 22ed40b85..a41907a40 100644 --- a/include/media_socket.h +++ b/include/media_socket.h @@ -280,6 +280,16 @@ struct media_packet { int ptime; // returned from decoding }; +struct rtp_extension { + unsigned int id; + str name; // urn:ietf:params:rtp- hdrext:... or URI +}; + +static inline void rtp_extension_free(struct rtp_extension *r) { + g_free(r); +} + +TYPED_GQUEUE(extmap, struct rtp_extension); extern local_intf_q all_local_interfaces; // read-only during runtime diff --git a/lib/rtplib.c b/lib/rtplib.c index 3b3cc8bc7..24f690c80 100644 --- a/lib/rtplib.c +++ b/lib/rtplib.c @@ -6,7 +6,7 @@ -struct rtp_extension { +struct rtp_exthdr { uint16_t undefined; uint16_t length; } __attribute__ ((packed)); @@ -61,7 +61,6 @@ const int num_rfc_rtp_payload_types = G_N_ELEMENTS(rfc_rtp_payload_types); struct rtp_header *rtp_payload(str *p, const str *s) { struct rtp_header *rtp; - struct rtp_extension *ext; const char *err; err = "short packet (header)"; @@ -86,6 +85,7 @@ struct rtp_header *rtp_payload(str *p, const str *s) { if ((rtp->v_p_x_cc & 0x10)) { /* extension */ + struct rtp_exthdr *ext; err = "short packet (extension header)"; if (p->len < sizeof(*ext)) goto error; diff --git a/t/auto-daemon-tests.pl b/t/auto-daemon-tests.pl index d3cf0ed39..73f368186 100755 --- a/t/auto-daemon-tests.pl +++ b/t/auto-daemon-tests.pl @@ -4386,13 +4386,13 @@ a=rtpmap:126 H264/90000 a=fmtp:126 profile-level-id=428016;packetization-mode=1;max-mbps=490000;max-fs=8160;max-cpb=200;max-dpb=16320;max-br=5000;max-smbps=490000;max-fps=6000 a=rtpmap:123 X-ULPFECUC/90000 a=fmtp:123 multi_ssrc=1;feedback=0;max_esel=1450;m=8;max_n=42;FEC_ORDER=FEC_SRTP;non_seq=1 +a=extmap:4 http://protocols.cisco.com/timestamp#100us a=rtcp-fb:* ccm pan a=rtcp-fb:* nack pli a=rtcp-fb:* ccm fir a=rtcp-fb:* ccm tmmbr a=label:11 a=answer:full -a=extmap:4 http://protocols.cisco.com/timestamp#100us a=cisco-mari-psre:97 ltrf=3 a=cisco-mari-psre:126 ltrf=3 a=content:main @@ -21518,6 +21518,7 @@ t=0 0 m=audio 2000 RTP/AVP 0 c=IN IP4 198.51.100.1 a=extmap:0 foobar +a=extmap:1 quux a=sendrecv ---------------------------------- v=0 @@ -21527,7 +21528,7 @@ t=0 0 m=audio PORT RTP/AVP 0 c=IN IP4 203.0.113.1 a=rtpmap:0 PCMU/8000 -a=extmap:0 foobar +a=extmap:1 quux a=sendrecv a=rtcp:PORT SDP @@ -21542,6 +21543,7 @@ t=0 0 m=audio 2000 RTP/AVP 0 c=IN IP4 198.51.100.1 a=extmap:0 foobar +a=extmap:1 quux a=sendrecv ---------------------------------- v=0