From c2bd6e93442d49855746c7407343301a190909e0 Mon Sep 17 00:00:00 2001 From: Evangelos Ribeiro Tzaras Date: Thu, 8 Apr 2021 16:43:13 +0200 Subject: [PATCH] sip: media: rework codec negotiation introduce `calls_sip_media_manager_get_capabilities ()` which takes a GList of MediaCodecInfo's as input to generate a SDP message. If using in an SDP answer we simply feed it a list of the common codecs as gathered from the SDP offer. --- plugins/sip/calls-sip-call.c | 21 +++++- plugins/sip/calls-sip-call.h | 2 + plugins/sip/calls-sip-media-manager.c | 95 +++++++++++++++++++++++++-- plugins/sip/calls-sip-media-manager.h | 8 +++ plugins/sip/calls-sip-origin.c | 17 ++++- plugins/sip/gst-rfc3551.c | 11 ++++ plugins/sip/gst-rfc3551.h | 1 + 7 files changed, 144 insertions(+), 11 deletions(-) diff --git a/plugins/sip/calls-sip-call.c b/plugins/sip/calls-sip-call.c index 0d15941..df76afe 100644 --- a/plugins/sip/calls-sip-call.c +++ b/plugins/sip/calls-sip-call.c @@ -46,6 +46,7 @@ struct _CallsSipCall CallsSipMediaManager *manager; CallsSipMediaPipeline *pipeline; nua_handle_t *nh; + GList *codecs; }; static void calls_sip_call_message_source_interface_init (CallsMessageSourceInterface *iface); @@ -108,9 +109,10 @@ calls_sip_call_answer (CallsCall *call) /* TODO get free port by creating GSocket and passing that to the pipeline */ calls_sip_call_setup_local_media_connection (self, local_port, local_port + 1); - local_sdp = calls_sip_media_manager_static_capabilities (self->manager, - local_port, - FALSE); + local_sdp = calls_sip_media_manager_get_capabilities (self->manager, + local_port, + FALSE, + self->codecs); g_assert (local_sdp); g_debug ("Setting local SDP to string:\n%s", local_sdp); @@ -210,6 +212,8 @@ calls_sip_call_finalize (GObject *object) calls_sip_media_pipeline_stop (self->pipeline); g_clear_object (&self->pipeline); } + g_clear_pointer (&self->codecs, g_list_free); + G_OBJECT_CLASS (calls_sip_call_parent_class)->finalize (object); } @@ -354,3 +358,14 @@ calls_sip_call_set_state (CallsSipCall *self, old_state); } + +void +calls_sip_call_set_codecs (CallsSipCall *self, + GList *codecs) +{ + g_return_if_fail (CALLS_IS_SIP_CALL (self)); + g_return_if_fail (codecs); + + g_list_free (self->codecs); + self->codecs = codecs; +} diff --git a/plugins/sip/calls-sip-call.h b/plugins/sip/calls-sip-call.h index c7a4348..ea12b04 100644 --- a/plugins/sip/calls-sip-call.h +++ b/plugins/sip/calls-sip-call.h @@ -49,5 +49,7 @@ void calls_sip_call_activate_media (CallsSi gboolean enabled); void calls_sip_call_set_state (CallsSipCall *self, CallsCallState state); +void calls_sip_call_set_codecs (CallsSipCall *self, + GList *codecs); G_END_DECLS diff --git a/plugins/sip/calls-sip-media-manager.c b/plugins/sip/calls-sip-media-manager.c index b3ad183..093a2a2 100644 --- a/plugins/sip/calls-sip-media-manager.c +++ b/plugins/sip/calls-sip-media-manager.c @@ -85,18 +85,21 @@ calls_sip_media_manager_default () } -/* calls_sip_media_manager_static_capabilities: +/* calls_sip_media_manager_get_capabilities: * + * @self: A #CallsSipMediaManager * @port: Should eventually come from the ICE stack * @use_srtp: Whether to use srtp (not really handled) + * @supported_codecs: A #GList of #MediaCodecInfo * - * Returns: (full-control) string describing capabilities + * Returns: (transfer: full) string describing capabilities * to be used in the session description (SDP) */ char * -calls_sip_media_manager_static_capabilities (CallsSipMediaManager *self, - guint port, - gboolean use_srtp) +calls_sip_media_manager_get_capabilities (CallsSipMediaManager *self, + guint port, + gboolean use_srtp, + GList *supported_codecs) { char *payload_type = use_srtp ? "SAVP" : "AVP"; g_autoptr (GString) media_line = NULL; @@ -108,7 +111,7 @@ calls_sip_media_manager_static_capabilities (CallsSipMediaManager *self, media_line = g_string_new (NULL); attribute_lines = g_string_new (NULL); - if (self->supported_codecs == NULL) { + if (supported_codecs == NULL) { g_warning ("No supported codecs found. Can't build meaningful SDP message"); g_string_append_printf (media_line, "m=audio 0 RTP/AVP 0"); goto done; @@ -118,7 +121,7 @@ calls_sip_media_manager_static_capabilities (CallsSipMediaManager *self, g_string_append_printf (media_line, "m=audio %d RTP/%s", port, payload_type); - for (node = self->supported_codecs; node != NULL; node = node->next) { + for (node = supported_codecs; node != NULL; node = node->next) { MediaCodecInfo *codec = node->data; g_string_append_printf (media_line, " %u", codec->payload_id); @@ -141,8 +144,86 @@ calls_sip_media_manager_static_capabilities (CallsSipMediaManager *self, } +/* calls_sip_media_manager_static_capabilities: + * + * @self: A #CallsSipMediaManager + * @port: Should eventually come from the ICE stack + * @use_srtp: Whether to use srtp (not really handled) + * + * Returns: (transfer: full) string describing capabilities + * to be used in the session description (SDP) + */ +char * +calls_sip_media_manager_static_capabilities (CallsSipMediaManager *self, + guint port, + gboolean use_srtp) +{ + g_return_val_if_fail (CALLS_IS_SIP_MEDIA_MANAGER (self), NULL); + + return calls_sip_media_manager_get_capabilities (self, + port, + use_srtp, + self->supported_codecs); +} + + MediaCodecInfo* get_best_codec (CallsSipMediaManager *self) { return media_codec_by_name ("PCMA"); } + + +/* calls_sip_media_manager_codec_candiates: + * + * @self: A #CallsSipMediaManager + * + * Returns: (transfer: none) A #GList of supported + * #MediaCodecInfo + */ +GList * +calls_sip_media_manager_codec_candidates (CallsSipMediaManager *self) +{ + g_return_val_if_fail (CALLS_IS_SIP_MEDIA_MANAGER (self), NULL); + + return self->supported_codecs; +} + + +/* calls_sip_media_manager_get_codecs_from_sdp + * + * @self: A #CallsSipMediaManager + * @sdp: A #sdp_media_t media description + * + * Returns: (transfer: full) A #GList of codecs found in the + * SDP message + */ +GList * +calls_sip_media_manager_get_codecs_from_sdp (CallsSipMediaManager *self, + sdp_media_t *sdp_media) +{ + GList *codecs = NULL; + sdp_rtpmap_t *rtpmap = NULL; + + g_return_val_if_fail (CALLS_IS_SIP_MEDIA_MANAGER (self), NULL); + g_return_val_if_fail (sdp_media, NULL); + + if (sdp_media->m_type != sdp_media_audio) { + g_warning ("Only the 'audio' media type is supported"); + return NULL; + } + + for (rtpmap = sdp_media->m_rtpmaps; rtpmap != NULL; rtpmap = rtpmap->rm_next) { + MediaCodecInfo *codec = media_codec_by_payload_id (rtpmap->rm_pt); + if (codec) + codecs = g_list_append (codecs, codec); + } + + if (sdp_media->m_next != NULL) + g_warning ("Currently only a single media session is supported"); + + if (codecs == NULL) + g_warning ("Did not find any common codecs"); + + return codecs; +} diff --git a/plugins/sip/calls-sip-media-manager.h b/plugins/sip/calls-sip-media-manager.h index d835292..3dfc93e 100644 --- a/plugins/sip/calls-sip-media-manager.h +++ b/plugins/sip/calls-sip-media-manager.h @@ -27,6 +27,7 @@ #include "gst-rfc3551.h" #include +#include #include #define CALLS_TYPE_SIP_MEDIA_MANAGER (calls_sip_media_manager_get_type ()) @@ -35,9 +36,16 @@ G_DECLARE_FINAL_TYPE (CallsSipMediaManager, calls_sip_media_manager, CALLS, SIP_ CallsSipMediaManager* calls_sip_media_manager_default (void); +gchar* calls_sip_media_manager_get_capabilities (CallsSipMediaManager *self, + guint port, + gboolean use_srtp, + GList *supported_codecs); gchar* calls_sip_media_manager_static_capabilities (CallsSipMediaManager *self, guint port, gboolean use_srtp); gboolean calls_sip_media_manager_supports_media (CallsSipMediaManager *self, const char *media_type); MediaCodecInfo* get_best_codec (CallsSipMediaManager *self); +GList * calls_sip_media_manager_codec_candidates (CallsSipMediaManager *self); +GList * calls_sip_media_manager_get_codecs_from_sdp (CallsSipMediaManager *self, + sdp_media_t *sdp_media); diff --git a/plugins/sip/calls-sip-origin.c b/plugins/sip/calls-sip-origin.c index addb89e..1507851 100644 --- a/plugins/sip/calls-sip-origin.c +++ b/plugins/sip/calls-sip-origin.c @@ -218,9 +218,14 @@ sip_i_state (int status, tagi_t tags[]) { const sdp_session_t *r_sdp = NULL; + const sdp_session_t *l_sdp = NULL; gint call_state = nua_callstate_init; CallsCallState state; CallsSipCall *call; + int offer_sent = 0; + int offer_recv = 0; + int answer_sent = 0; + int answer_recv = 0; g_assert (CALLS_IS_SIP_ORIGIN (origin)); @@ -234,8 +239,13 @@ sip_i_state (int status, } tl_gets (tags, + SOATAG_LOCAL_SDP_REF (l_sdp), SOATAG_REMOTE_SDP_REF (r_sdp), NUTAG_CALLSTATE_REF (call_state), + NUTAG_OFFER_SENT_REF (offer_sent), + NUTAG_OFFER_RECV_REF (offer_recv), + NUTAG_ANSWER_SENT_REF (answer_sent), + NUTAG_ANSWER_RECV_REF (answer_recv), TAG_END ()); if (status == 503) { @@ -247,6 +257,11 @@ sip_i_state (int status, * also: rtcp port = rtp port + 1 */ if (r_sdp) { + GList *codecs = + calls_sip_media_manager_get_codecs_from_sdp (origin->media_manager, + r_sdp->sdp_media); + + calls_sip_call_set_codecs (call, codecs); calls_sip_call_setup_remote_media_connection (call, r_sdp->sdp_connection->c_address, r_sdp->sdp_media->m_port, @@ -1161,7 +1176,7 @@ calls_sip_origin_go_online (CallsSipOrigin *self, nua_register (self->oper->register_handle, SIPTAG_EXPIRES_STR ("180"), NUTAG_SUPPORTED ("replaces, outbound, gruu"), - NUTAG_OUTBOUND ("outbound natify gruuize"), // <- janus uses "no-validate" + NUTAG_OUTBOUND ("outbound natify gruuize validate"), // <- janus uses "no-validate" NUTAG_M_USERNAME (self->user), NUTAG_M_DISPLAY ("SIP tester"), NUTAG_M_PARAMS ("user=phone"), diff --git a/plugins/sip/gst-rfc3551.c b/plugins/sip/gst-rfc3551.c index 780873a..3e7df1e 100644 --- a/plugins/sip/gst-rfc3551.c +++ b/plugins/sip/gst-rfc3551.c @@ -55,6 +55,17 @@ media_codec_by_name (const char *name) return NULL; } +MediaCodecInfo * +media_codec_by_payload_id (guint payload_id) +{ + for (guint i = 0; i < G_N_ELEMENTS (gst_codecs); i++) { + if (payload_id == gst_codecs[i].payload_id) + return &gst_codecs[i]; + } + + return NULL; +} + gchar * media_codec_get_gst_capabilities (MediaCodecInfo *codec) { diff --git a/plugins/sip/gst-rfc3551.h b/plugins/sip/gst-rfc3551.h index 4ecde6c..f285293 100644 --- a/plugins/sip/gst-rfc3551.h +++ b/plugins/sip/gst-rfc3551.h @@ -44,5 +44,6 @@ typedef struct { MediaCodecInfo* media_codec_by_name (const char *name); +MediaCodecInfo* media_codec_by_payload_id (uint payload_id); gchar* media_codec_get_gst_capabilities (MediaCodecInfo *codec); GList* media_codecs_get_candidates ();