From 3c28cb325ae86e18c69525eeedd23216f57ba59c Mon Sep 17 00:00:00 2001 From: Donat Zenichev Date: Thu, 9 Nov 2023 09:25:03 +0100 Subject: [PATCH] MT#57719 Add SDP session attributes print (subscribe) Add handling of SDP session level attributes for: - `monologue_subscribe_request()` - `monologue_subscribe_answer()` This will be used by `janus.c` related functionality. Change-Id: I1c50b5b9da08e7d8cb2c98eb6995d8610386d6ed --- daemon/call.c | 24 +++++++++++++++++++----- daemon/call_interfaces.c | 8 ++++---- daemon/janus.c | 8 ++++---- daemon/sdp.c | 29 ++++++++++++++++++++++++++--- include/call.h | 14 +++++++++----- include/sdp.h | 4 +++- t/auto-daemon-tests-websocket.py | 2 ++ 7 files changed, 67 insertions(+), 22 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index 7d43c8bfa..8e7d1ece2 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -50,7 +50,7 @@ #include "janus.h" #include "dtmf.h" #include "audio_player.h" - +#include "sdp.h" struct iterator_helper { uint64_t count; @@ -3395,10 +3395,14 @@ int monologue_publish(struct call_monologue *ml, GQueue *streams, struct sdp_ng_ /* called with call->master_lock held in W */ __attribute__((nonnull(1, 2, 3, 4))) static int monologue_subscribe_request1(struct call_monologue *src_ml, struct call_monologue *dst_ml, - struct sdp_ng_flags *flags, unsigned int *index) + struct sdp_ng_flags *flags, unsigned int *index, bool print_extra_sess_attrs) { unsigned int idx_diff = 0, rev_idx_diff = 0; + /* additional attributes to be carried for `sdp_create()` */ + if (print_extra_sess_attrs) + sdp_copy_session_attributes(src_ml, dst_ml); + for (GList *l = src_ml->last_in_sdp_streams.head; l; l = l->next) { struct stream_params *sp = l->data; @@ -3469,7 +3473,7 @@ static int monologue_subscribe_request1(struct call_monologue *src_ml, struct ca /* called with call->master_lock held in W */ __attribute__((nonnull(1, 2, 3))) int monologue_subscribe_request(const GQueue *srms, struct call_monologue *dst_ml, - struct sdp_ng_flags *flags) + struct sdp_ng_flags *flags, bool print_extra_sess_attrs) { unsigned int index = 1; /* running counter for output/dst medias */ @@ -3485,7 +3489,7 @@ int monologue_subscribe_request(const GQueue *srms, struct call_monologue *dst_m continue; if (!g_queue_find(&mls, src_ml)) { - int ret = monologue_subscribe_request1(src_ml, dst_ml, flags, &index); + int ret = monologue_subscribe_request1(src_ml, dst_ml, flags, &index, print_extra_sess_attrs); g_queue_push_tail(&mls, src_ml); if (ret) return -1; @@ -3496,8 +3500,11 @@ int monologue_subscribe_request(const GQueue *srms, struct call_monologue *dst_m /* called with call->master_lock held in W */ __attribute__((nonnull(1, 2, 3))) -int monologue_subscribe_answer(struct call_monologue *dst_ml, struct sdp_ng_flags *flags, GQueue *streams) { +int monologue_subscribe_answer(struct call_monologue *dst_ml, struct sdp_ng_flags *flags, GQueue *streams, + bool print_extra_sess_attrs) +{ struct media_subscription *rev_ms = NULL; + AUTO_CLEANUP(GQueue attr_mls, g_queue_clear) = G_QUEUE_INIT; /* to avoid duplications */ for (GList * l = streams->head; l; l = l->next) { @@ -3512,6 +3519,12 @@ int monologue_subscribe_answer(struct call_monologue *dst_ml, struct sdp_ng_flag if (!dst_media || !src_media) continue; + /* additional attributes to be carried for `sdp_create()` */ + if (print_extra_sess_attrs && !g_queue_find(&attr_mls, ms->monologue)) { + sdp_copy_session_attributes(ms->monologue, dst_ml); + g_queue_push_tail(&attr_mls, ms->monologue); + } + rev_ms = call_get_media_subscription(src_media->media_subscribers_ht, dst_media); if (rev_ms) rev_ms->attrs.transcoding = 0; @@ -3987,6 +4000,7 @@ void __monologue_free(struct call_monologue *m) { g_string_free(m->last_out_sdp, TRUE); str_free_dup(&m->last_in_sdp); sdp_free(&m->last_in_sdp_parsed); + g_queue_clear_full(&m->sdp_attributes, free); sdp_streams_free(&m->last_in_sdp_streams); g_hash_table_destroy(m->subscribers_ht); g_hash_table_destroy(m->subscriptions_ht); diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 5c690e3ce..a58395032 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -3415,7 +3415,7 @@ const char *call_publish_ng(struct ng_buffer *ngbuf, bencode_item_t *input, benc if (ret) ilog(LOG_ERR, "Publish error"); // XXX close call? handle errors? - ret = sdp_create(&sdp_out, ml, &flags, true); + ret = sdp_create(&sdp_out, ml, &flags, false, true); if (!ret) { save_last_sdp(ml, &sdp_in, &parsed, &streams); bencode_buffer_destroy_add(output->buffer, g_free, sdp_out.s); @@ -3486,7 +3486,7 @@ const char *call_subscribe_request_ng(bencode_item_t *input, bencode_item_t *out bencode_buffer_destroy_add(output->buffer, (free_func_t) sdp_chopper_destroy, chopper); } - int ret = monologue_subscribe_request(&srms, dest_ml, &flags); + int ret = monologue_subscribe_request(&srms, dest_ml, &flags, false); if (ret) return "Failed to request subscription"; @@ -3496,7 +3496,7 @@ const char *call_subscribe_request_ng(bencode_item_t *input, bencode_item_t *out return "Failed to rewrite SDP"; } else { /* create new SDP */ - ret = sdp_create(&sdp_out, dest_ml, &flags, false); + ret = sdp_create(&sdp_out, dest_ml, &flags, false, false); } /* place return output SDP */ @@ -3600,7 +3600,7 @@ const char *call_subscribe_answer_ng(struct ng_buffer *ngbuf, bencode_item_t *in if (sdp_streams(&parsed, &streams, &flags)) return "Incomplete SDP specification"; - int ret = monologue_subscribe_answer(dest_ml, &flags, &streams); + int ret = monologue_subscribe_answer(dest_ml, &flags, &streams, false); if (ret) return "Failed to process subscription answer"; diff --git a/daemon/janus.c b/daemon/janus.c index b3cb92878..59146c5ff 100644 --- a/daemon/janus.c +++ b/daemon/janus.c @@ -625,12 +625,12 @@ static const char *janus_videoroom_join(struct websocket_message *wm, struct jan flags.rtcp_mux_demux = 1; } - int ret = monologue_subscribe_request(&srms, dest_ml, &flags); + int ret = monologue_subscribe_request(&srms, dest_ml, &flags, true); if (ret) return "Subscribe error"; /* create SDP */ - ret = sdp_create(jsep_sdp_out, dest_ml, &flags, true); + ret = sdp_create(jsep_sdp_out, dest_ml, &flags, true, true); if (!dest_ml->janus_session) dest_ml->janus_session = obj_get(session); @@ -865,7 +865,7 @@ static const char *janus_videoroom_configure(struct websocket_message *wm, struc // XXX check there's only one audio and one video stream? AUTO_CLEANUP(str sdp_out, str_free_dup) = STR_NULL; - ret = sdp_create(&sdp_out, ml, &flags, true); + ret = sdp_create(&sdp_out, ml, &flags, true, true); if (ret) return "Publish error"; @@ -985,7 +985,7 @@ static const char *janus_videoroom_start(struct websocket_message *wm, struct ja if (!dest_ml) return "Subscriber not found"; - int ret = monologue_subscribe_answer(dest_ml, &flags, &streams); + int ret = monologue_subscribe_answer(dest_ml, &flags, &streams, true); if (ret) return "Failed to process subscription answer"; diff --git a/daemon/sdp.c b/daemon/sdp.c index c2f0c82d3..5b743256a 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -1747,6 +1747,7 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *fl const char *errstr; unsigned int num = 0; struct sdp_attribute *attr; + GQueue *attrs; for (l = sessions->head; l; l = l->next) { session = l->data; @@ -1793,7 +1794,7 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *fl goto error; /* a=crypto */ - GQueue *attrs = attr_list_get_by_id(&media->attributes, ATTR_CRYPTO); + attrs = attr_list_get_by_id(&media->attributes, ATTR_CRYPTO); for (GList *ll = attrs ? attrs->head : NULL; ll; ll = ll->next) { attr = ll->data; struct crypto_params_sdes *cps = g_slice_alloc0(sizeof(*cps)); @@ -3051,6 +3052,18 @@ struct packet_stream *print_rtcp(GString *s, struct call_media *media, GList *rt return ps_rtcp; } +/* copy sdp session attributes to the correlated monologue, as a plain text objects (str) */ +void sdp_copy_session_attributes(struct call_monologue * src, struct call_monologue * dst) { + struct sdp_attribute *attr; + struct sdp_session *src_session = src->last_in_sdp_parsed.head->data; + GQueue *src_attributes = attr_list_get_by_id(&src_session->attributes, ATTR_OTHER); + g_queue_clear_full(&dst->sdp_attributes, free); + for (GList *ll = src_attributes ? src_attributes->head : NULL; ll; ll = ll->next) { + attr = ll->data; + str * ret = str_dup(&attr->line_value); + g_queue_push_tail(&dst->sdp_attributes, ret); + } +} static void print_sdp_session_section(GString *s, struct sdp_ng_flags *flags, struct call_media *call_media) { @@ -3406,10 +3419,11 @@ error: } int sdp_create(str *out, struct call_monologue *monologue, struct sdp_ng_flags *flags, - bool print_other_attrs) + bool print_other_sess_attrs, bool print_other_media_attrs) { const char *err = NULL; GString *s = NULL; + GQueue * extra_sdp_attributes = &monologue->sdp_attributes; err = "Need at least one media"; if (!monologue->medias->len) @@ -3440,6 +3454,15 @@ int sdp_create(str *out, struct call_monologue *monologue, struct sdp_ng_flags * g_string_append_printf(s, "s=%s\r\n", rtpe_config.software_id); g_string_append(s, "t=0 0\r\n"); + if (print_other_sess_attrs) { + /* `sdp_session`, if `->attributes` given, print on the session level */ + for (GList *l = extra_sdp_attributes->head; l; l = l->next) + { + str * attr_value = l->data; + append_attr_to_gstring(s, attr_value->s, NULL, flags, MT_UNKNOWN); + } + } + for (unsigned int i = 0; i < monologue->medias->len; i++) { media = monologue->medias->pdata[i]; err = "Empty media stream"; @@ -3470,7 +3493,7 @@ int sdp_create(str *out, struct call_monologue *monologue, struct sdp_ng_flags * g_string_append_printf(s, "\r\nc=IN %s %s\r\n", rtp_ps->selected_sfd->local_intf->advertised_address.addr.family->rfc_name, sockaddr_print_buf(&rtp_ps->selected_sfd->local_intf->advertised_address.addr)); - print_sdp_media_section(s, media, NULL, flags, rtp_ps_link, true, false, print_other_attrs); + print_sdp_media_section(s, media, NULL, flags, rtp_ps_link, true, false, print_other_media_attrs); } out->len = s->len; diff --git a/include/call.h b/include/call.h index 1a9316d46..71075a723 100644 --- a/include/call.h +++ b/include/call.h @@ -559,8 +559,8 @@ struct call_monologue { unsigned long long sdp_session_id; unsigned long long sdp_version; str last_in_sdp; - GQueue last_in_sdp_parsed; - GQueue last_in_sdp_streams; + GQueue last_in_sdp_parsed; /* last parsed `sdp_session` */ + GQueue last_in_sdp_streams; /* last parsed `stream_params` */ GString *last_out_sdp; char *sdp_username; char *sdp_session_name; @@ -582,6 +582,9 @@ struct call_monologue { unsigned int block_dtmf_trigger_end_ms; unsigned int dtmf_delay; + /* carry `sdp_session` attributes into resulting call monologue SDP */ + GQueue sdp_attributes; + volatile unsigned int ml_flags; }; @@ -765,9 +768,10 @@ void codecs_offer_answer(struct call_media *media, struct call_media *other_medi struct stream_params *sp, struct sdp_ng_flags *flags); int monologue_publish(struct call_monologue *ml, GQueue *streams, struct sdp_ng_flags *flags); -int monologue_subscribe_request(const GQueue *srms, struct call_monologue *dst, struct sdp_ng_flags *); -int monologue_subscribe_answer(struct call_monologue *dst, struct sdp_ng_flags *, - GQueue *); +int monologue_subscribe_request(const GQueue *srms, struct call_monologue *dst, struct sdp_ng_flags *flags, + bool print_extra_sess_attrs); +int monologue_subscribe_answer(struct call_monologue *dst, struct sdp_ng_flags *flags, + GQueue *streams, bool print_extra_sess_attrs); int monologue_unsubscribe(struct call_monologue *dst, struct sdp_ng_flags *); void monologue_destroy(struct call_monologue *ml); int call_delete_branch_by_id(const str *callid, const str *branch, diff --git a/include/sdp.h b/include/sdp.h index 0a3b188e3..700c94bc1 100644 --- a/include/sdp.h +++ b/include/sdp.h @@ -37,7 +37,7 @@ int sdp_replace(struct sdp_chopper *, GQueue *, struct call_monologue *, struct bool print_other_attrs); int sdp_is_duplicate(GQueue *sessions); int sdp_create(str *out, struct call_monologue *, struct sdp_ng_flags *flags, - bool print_other_attrs); + bool print_other_sess_attrs, bool print_other_media_attrs); const char *sdp_get_sendrecv(struct call_media *media); int sdp_parse_candidate(struct ice_candidate *cand, const str *s); // returns -1, 0, 1 @@ -46,6 +46,8 @@ struct sdp_chopper *sdp_chopper_new(str *input); void sdp_chopper_destroy(struct sdp_chopper *chop); void sdp_chopper_destroy_ret(struct sdp_chopper *chop, str *ret); +void sdp_copy_session_attributes(struct call_monologue * src, struct call_monologue * dst); + INLINE int is_trickle_ice_address(const struct endpoint *ep) { if (is_addr_unspecified(&ep->address) && ep->port == 9) return 1; diff --git a/t/auto-daemon-tests-websocket.py b/t/auto-daemon-tests-websocket.py index 639408a6b..3121baac0 100644 --- a/t/auto-daemon-tests-websocket.py +++ b/t/auto-daemon-tests-websocket.py @@ -1719,6 +1719,8 @@ class TestVideoroom(unittest.TestCase): "o=- \d+ \d+ IN IP4 203.0.113.1\r\n" "s=rtpengine.*?\r\n" "t=0 0\r\n" + "a=extmap-allow-mixed\r\n" + "a=msid-semantic: WMS hJifdaJwqEqHxSG0pVbs1DrLAwiHqz7fKlqC\r\n" "m=audio \d+ UDP/TLS/RTP/SAVPF 111\r\n" "c=IN IP4 203.0.113.1\r\n" "a=mid:0\r\n"