diff options
Diffstat (limited to 'tinyMEDIA/src/tmedia_session.c')
-rwxr-xr-x | tinyMEDIA/src/tmedia_session.c | 3705 |
1 files changed, 1886 insertions, 1819 deletions
diff --git a/tinyMEDIA/src/tmedia_session.c b/tinyMEDIA/src/tmedia_session.c index 08348d3..72c924a 100755 --- a/tinyMEDIA/src/tmedia_session.c +++ b/tinyMEDIA/src/tmedia_session.c @@ -82,52 +82,56 @@ int tmedia_session_set_ro(tmedia_session_t* self, const tsdp_header_M_t* m); /*== Predicate function to find session object by media */ static int __pred_find_session_by_media(const tsk_list_item_t *item, const void *media) { - if (item && item->data){ - return tsk_stricmp(tmedia_session_get_media((const tmedia_session_t *)item->data), (const char*)media); - } - return -1; + if (item && item->data) { + return tsk_stricmp(tmedia_session_get_media((const tmedia_session_t *)item->data), (const char*)media); + } + return -1; } /*== Predicate function to find session object by type */ static int __pred_find_session_by_type(const tsk_list_item_t *item, const void *type) { - if (item && item->data){ - return ((const tmedia_session_t *)item->data)->type - *((tmedia_type_t*)type); - } - return -1; + if (item && item->data) { + return ((const tmedia_session_t *)item->data)->type - *((tmedia_type_t*)type); + } + return -1; } /*== Predicate function to find codec object by format */ static int __pred_find_codec_by_format(const tsk_list_item_t *item, const void *codec) { - if (item && item->data && codec){ - return tsk_stricmp(((const tmedia_codec_t*)item->data)->format, ((const tmedia_codec_t*)codec)->format); - } - return -1; + if (item && item->data && codec) { + return tsk_stricmp(((const tmedia_codec_t*)item->data)->format, ((const tmedia_codec_t*)codec)->format); + } + return -1; } /*== Predicate function to find codec object by id */ static int __pred_find_codec_by_id(const tsk_list_item_t *item, const void *id) { - if (item && item->data && id){ - if (((const tmedia_codec_t*)item->data)->id == *((const tmedia_codec_id_t*)id)){ - return 0; - } - } - return -1; + if (item && item->data && id) { + if (((const tmedia_codec_t*)item->data)->id == *((const tmedia_codec_id_t*)id)) { + return 0; + } + } + return -1; } -static tsk_size_t __flags_sum(const tsk_bool_t *flags, tsk_size_t count) { - tsk_size_t sum = 0, i; - for (i = 0; i < count; ++i) { - if (flags[i] == tsk_true) ++sum; - } - return sum; +static tsk_size_t __flags_sum(const tsk_bool_t *flags, tsk_size_t count) +{ + tsk_size_t sum = 0, i; + for (i = 0; i < count; ++i) { + if (flags[i] == tsk_true) { + ++sum; + } + } + return sum; } -uint64_t tmedia_session_get_unique_id(){ - static uint64_t __UniqueId = 1; // MUST not be equal to zero - return __UniqueId++; +uint64_t tmedia_session_get_unique_id() +{ + static uint64_t __UniqueId = 1; // MUST not be equal to zero + return __UniqueId++; } /**@ingroup tmedia_session_group @@ -138,135 +142,152 @@ uint64_t tmedia_session_get_unique_id(){ */ int tmedia_session_init(tmedia_session_t* self, tmedia_type_t type) { - int ret = 0; - - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - if (!self->initialized){ - /* set values */ - if (!self->id){ - self->id = tmedia_session_get_unique_id(); - } - self->type = type; - self->initialized = tsk_true; - self->bl = tmedia_defaults_get_bl(); - self->codecs_allowed = tmedia_codec_id_all; - self->bypass_encoding = tmedia_defaults_get_bypass_encoding(); - self->bypass_decoding = tmedia_defaults_get_bypass_decoding(); - /* SSL certificates */{ - const char* priv_path = tsk_null, *pub_path = tsk_null, *ca_path = tsk_null; - tsk_bool_t verify = tsk_false; - if ((ret = tmedia_defaults_get_ssl_certs(&priv_path, &pub_path, &ca_path, &verify))) { - return ret; - } - self->dtls.file_pvk = tsk_strdup(priv_path); - self->dtls.file_pbk = tsk_strdup(pub_path); - self->dtls.file_ca = tsk_strdup(ca_path); - self->dtls.verify = verify; - } - /* load associated codecs */ - ret = _tmedia_session_load_codecs(self); - } - - return 0; + int ret = 0; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if (!self->initialized) { + /* set values */ + if (!self->id) { + self->id = tmedia_session_get_unique_id(); + } + self->type = type; + self->initialized = tsk_true; + self->bl = tmedia_defaults_get_bl(); + self->codecs_allowed = tmedia_codec_id_all; + self->bypass_encoding = tmedia_defaults_get_bypass_encoding(); + self->bypass_decoding = tmedia_defaults_get_bypass_decoding(); + /* SSL certificates */{ + const char* priv_path = tsk_null, *pub_path = tsk_null, *ca_path = tsk_null; + tsk_bool_t verify = tsk_false; + if ((ret = tmedia_defaults_get_ssl_certs(&priv_path, &pub_path, &ca_path, &verify))) { + return ret; + } + self->dtls.file_pvk = tsk_strdup(priv_path); + self->dtls.file_pbk = tsk_strdup(pub_path); + self->dtls.file_ca = tsk_strdup(ca_path); + self->dtls.verify = verify; + } + /* QoS metrics */ + self->qos_metrics.q1 = 0.f; + self->qos_metrics.q2 = 0.f; + self->qos_metrics.q3 = 0.f; + self->qos_metrics.q4 = 0.f; + self->qos_metrics.qvag = 1.f; + self->qos_metrics.last_update_time = 0; + self->qos_metrics.bw_up_est_kbps = 0; + self->qos_metrics.bw_down_est_kbps = 0; + self->qos_metrics.video_out_width = 0; + self->qos_metrics.video_out_height = 0; + self->qos_metrics.video_in_width = 0; + self->qos_metrics.video_in_height = 0; + self->qos_metrics.video_in_avg_fps = 0; + self->qos_metrics.video_dec_avg_time = 0; + self->qos_metrics.video_enc_avg_time = 0; + + /* load associated codecs */ + ret = _tmedia_session_load_codecs(self); + } + + return 0; } int tmedia_session_set(tmedia_session_t* self, ...) { - va_list ap; - tmedia_params_L_t* params; - - if (!self || !self->plugin || !self->plugin->set){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - va_start(ap, self); - if ((params = tmedia_params_create_2(&ap))){ - const tsk_list_item_t *item; - const tmedia_param_t* param; - tsk_list_foreach(item, params){ - if (!(param = item->data)){ - continue; - } - if ((self->type & param->media_type)){ - self->plugin->set(self, param); - } - } - TSK_OBJECT_SAFE_FREE(params); - } - va_end(ap); - return 0; + va_list ap; + tmedia_params_L_t* params; + + if (!self || !self->plugin || !self->plugin->set) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + va_start(ap, self); + if ((params = tmedia_params_create_2(&ap))) { + const tsk_list_item_t *item; + const tmedia_param_t* param; + tsk_list_foreach(item, params) { + if (!(param = item->data)) { + continue; + } + if ((self->type & param->media_type)) { + self->plugin->set(self, param); + } + } + TSK_OBJECT_SAFE_FREE(params); + } + va_end(ap); + return 0; } tsk_bool_t tmedia_session_set_2(tmedia_session_t* self, const tmedia_param_t* param) { - if (!self || !param){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_false; - } - - if (param->plugin_type == tmedia_ppt_session){ - if (param->value_type == tmedia_pvt_int32){ - if (tsk_striequals(param->key, "codecs-supported")){ - //if(self->M.lo){ - // TSK_DEBUG_WARN("Cannot change codec values at this stage"); - //} - //else{ - int32_t codecs_allowed = *((int32_t*)param->value); - if (self->codecs_allowed != codecs_allowed){ - self->codecs_allowed = codecs_allowed; - return (_tmedia_session_load_codecs(self) == 0); - } - return 0; - //} - return tsk_true; - } - else if (tsk_striequals(param->key, "bypass-encoding")){ - self->bypass_encoding = *((int32_t*)param->value); - return tsk_true; - } - else if (tsk_striequals(param->key, "bypass-decoding")){ - self->bypass_decoding = *((int32_t*)param->value); - return tsk_true; - } - else if (tsk_striequals(param->key, "dtls-cert-verify")){ - self->dtls.verify = *((int32_t*)param->value) ? tsk_true : tsk_false; - return tsk_true; - } - } - else if (param->value_type == tmedia_pvt_pchar){ - if (tsk_striequals(param->key, "dtls-file-ca")){ - tsk_strupdate(&self->dtls.file_ca, param->value); - return tsk_true; - } - else if (tsk_striequals(param->key, "dtls-file-pbk")){ - tsk_strupdate(&self->dtls.file_pbk, param->value); - return tsk_true; - } - else if (tsk_striequals(param->key, "dtls-file-pvk")){ - tsk_strupdate(&self->dtls.file_pvk, param->value); - return tsk_true; - } - } - } - - return tsk_false; + if (!self || !param) { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_false; + } + + if (param->plugin_type == tmedia_ppt_session) { + if (param->value_type == tmedia_pvt_int32) { + if (tsk_striequals(param->key, "codecs-supported")) { + //if(self->M.lo){ + // TSK_DEBUG_WARN("Cannot change codec values at this stage"); + //} + //else{ + int32_t codecs_allowed = *((int32_t*)param->value); + if (self->codecs_allowed != codecs_allowed) { + self->codecs_allowed = codecs_allowed; + return (_tmedia_session_load_codecs(self) == 0); + } + return 0; + //} + return tsk_true; + } + else if (tsk_striequals(param->key, "bypass-encoding")) { + self->bypass_encoding = *((int32_t*)param->value); + return tsk_true; + } + else if (tsk_striequals(param->key, "bypass-decoding")) { + self->bypass_decoding = *((int32_t*)param->value); + return tsk_true; + } + else if (tsk_striequals(param->key, "dtls-cert-verify")) { + self->dtls.verify = *((int32_t*)param->value) ? tsk_true : tsk_false; + return tsk_true; + } + } + else if (param->value_type == tmedia_pvt_pchar) { + if (tsk_striequals(param->key, "dtls-file-ca")) { + tsk_strupdate(&self->dtls.file_ca, param->value); + return tsk_true; + } + else if (tsk_striequals(param->key, "dtls-file-pbk")) { + tsk_strupdate(&self->dtls.file_pbk, param->value); + return tsk_true; + } + else if (tsk_striequals(param->key, "dtls-file-pvk")) { + tsk_strupdate(&self->dtls.file_pvk, param->value); + return tsk_true; + } + } + } + + return tsk_false; } int tmedia_session_get(tmedia_session_t* self, tmedia_param_t* param) { - if (!self || !param) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - if (self->plugin && self->plugin->get) { - return self->plugin->get(self, param); - } - return -2; + if (!self || !param) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if (self->plugin && self->plugin->get) { + return self->plugin->get(self, param); + } + return -2; } /**@ingroup tmedia_session_group @@ -280,9 +301,9 @@ int tmedia_session_get(tmedia_session_t* self, tmedia_param_t* param) */ int tmedia_session_cmp(const tsk_object_t* sess1, const tsk_object_t* sess2) { - int ret; - tsk_subsat_int32_ptr(sess1, sess2, &ret); - return ret; + int ret; + tsk_subsat_int32_ptr(sess1, sess2, &ret); + return ret; } /**@ingroup tmedia_session_group @@ -293,22 +314,22 @@ int tmedia_session_cmp(const tsk_object_t* sess1, const tsk_object_t* sess2) */ int tmedia_session_plugin_register(const tmedia_session_plugin_def_t* plugin) { - tsk_size_t i; - if (!plugin){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + tsk_size_t i; + if (!plugin) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - /* add or replace the plugin */ - for (i = 0; i < TMED_SESSION_MAX_PLUGINS; i++){ - if (!__tmedia_session_plugins[i] || (__tmedia_session_plugins[i] == plugin)){ - __tmedia_session_plugins[i] = plugin; - return 0; - } - } + /* add or replace the plugin */ + for (i = 0; i < TMED_SESSION_MAX_PLUGINS; i++) { + if (!__tmedia_session_plugins[i] || (__tmedia_session_plugins[i] == plugin)) { + __tmedia_session_plugins[i] = plugin; + return 0; + } + } - TSK_DEBUG_ERROR("There are already %d plugins.", TMED_SESSION_MAX_PLUGINS); - return -2; + TSK_DEBUG_ERROR("There are already %d plugins.", TMED_SESSION_MAX_PLUGINS); + return -2; } /**@ingroup tmedia_session_group @@ -316,20 +337,20 @@ int tmedia_session_plugin_register(const tmedia_session_plugin_def_t* plugin) */ const tmedia_session_plugin_def_t* tmedia_session_plugin_find_by_media(const char* media) { - tsk_size_t i = 0; - if (tsk_strnullORempty(media)){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } + tsk_size_t i = 0; + if (tsk_strnullORempty(media)) { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } - /* add or replace the plugin */ - while ((i < TMED_SESSION_MAX_PLUGINS) && (__tmedia_session_plugins[i])){ - if (tsk_striequals(__tmedia_session_plugins[i]->media, media)){ - return __tmedia_session_plugins[i]; - } - i++; - } - return tsk_null; + /* add or replace the plugin */ + while ((i < TMED_SESSION_MAX_PLUGINS) && (__tmedia_session_plugins[i])) { + if (tsk_striequals(__tmedia_session_plugins[i]->media, media)) { + return __tmedia_session_plugins[i]; + } + i++; + } + return tsk_null; } /**@ingroup tmedia_session_group @@ -339,35 +360,35 @@ const tmedia_session_plugin_def_t* tmedia_session_plugin_find_by_media(const cha */ int tmedia_session_plugin_unregister(const tmedia_session_plugin_def_t* plugin) { - tsk_size_t i; - tsk_bool_t found = tsk_false; - if (!plugin){ - TSK_DEBUG_ERROR("Invalid Parameter"); - return -1; - } - - /* find the plugin to unregister */ - for (i = 0; i < TMED_SESSION_MAX_PLUGINS && __tmedia_session_plugins[i]; i++){ - if (__tmedia_session_plugins[i] == plugin){ - __tmedia_session_plugins[i] = tsk_null; - found = tsk_true; - break; - } - } - - /* compact */ - if (found){ - for (; i < (TMED_SESSION_MAX_PLUGINS - 1); i++){ - if (__tmedia_session_plugins[i + 1]){ - __tmedia_session_plugins[i] = __tmedia_session_plugins[i + 1]; - } - else{ - break; - } - } - __tmedia_session_plugins[i] = tsk_null; - } - return (found ? 0 : -2); + tsk_size_t i; + tsk_bool_t found = tsk_false; + if (!plugin) { + TSK_DEBUG_ERROR("Invalid Parameter"); + return -1; + } + + /* find the plugin to unregister */ + for (i = 0; i < TMED_SESSION_MAX_PLUGINS && __tmedia_session_plugins[i]; i++) { + if (__tmedia_session_plugins[i] == plugin) { + __tmedia_session_plugins[i] = tsk_null; + found = tsk_true; + break; + } + } + + /* compact */ + if (found) { + for (; i < (TMED_SESSION_MAX_PLUGINS - 1); i++) { + if (__tmedia_session_plugins[i + 1]) { + __tmedia_session_plugins[i] = __tmedia_session_plugins[i + 1]; + } + else { + break; + } + } + __tmedia_session_plugins[i] = tsk_null; + } + return (found ? 0 : -2); } /**@ingroup tmedia_session_group @@ -377,252 +398,268 @@ int tmedia_session_plugin_unregister(const tmedia_session_plugin_def_t* plugin) */ tmedia_session_t* tmedia_session_create(tmedia_type_t type) { - tmedia_session_t* session = tsk_null; - const tmedia_session_plugin_def_t* plugin; - tsk_size_t i = 0; + tmedia_session_t* session = tsk_null; + const tmedia_session_plugin_def_t* plugin; + tsk_size_t i = 0; - while ((i < TMED_SESSION_MAX_PLUGINS) && (plugin = __tmedia_session_plugins[i++])){ - if (plugin->objdef && (plugin->type == type)){ - if ((session = tsk_object_new(plugin->objdef))){ - if (!session->initialized){ - tmedia_session_init(session, type); - } - session->plugin = plugin; - } - break; - } - } - return session; + while ((i < TMED_SESSION_MAX_PLUGINS) && (plugin = __tmedia_session_plugins[i++])) { + if (plugin->objdef && (plugin->type == type)) { + if ((session = tsk_object_new(plugin->objdef))) { + if (!session->initialized) { + tmedia_session_init(session, type); + } + session->plugin = plugin; + } + break; + } + } + return session; } /* internal funtion: prepare lo */ static int _tmedia_session_prepare(tmedia_session_t* self) { - int ret; - if (!self || !self->plugin || !self->plugin->prepare){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - if (self->prepared){ - TSK_DEBUG_WARN("Session already prepared"); - return 0; - } - if ((ret = self->plugin->prepare(self))){ - TSK_DEBUG_ERROR("Failed to prepare the session"); - } - else{ - self->prepared = tsk_true; - } - return ret; + int ret; + if (!self || !self->plugin || !self->plugin->prepare) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if (self->prepared) { + TSK_DEBUG_WARN("Session already prepared"); + return 0; + } + if ((ret = self->plugin->prepare(self))) { + TSK_DEBUG_ERROR("Failed to prepare the session"); + } + else { + self->prepared = tsk_true; + } + return ret; } /* internal function used to set remote offer */ int _tmedia_session_set_ro(tmedia_session_t* self, const tsdp_header_M_t* m) { - int ret; - if (!self || !self->plugin || !self->plugin->set_remote_offer){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - if (!(ret = self->plugin->set_remote_offer(self, m))){ - self->ro_changed = tsk_true; - self->ro_held = tsdp_header_M_is_held(m, tsk_false); - } - return ret; + int ret; + if (!self || !self->plugin || !self->plugin->set_remote_offer) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if (!(ret = self->plugin->set_remote_offer(self, m))) { + self->ro_changed = tsk_true; + self->ro_held = tsdp_header_M_is_held(m, tsk_false); + } + return ret; } /* internal function: get media */ const char* tmedia_session_get_media(const tmedia_session_t* self) { - if (!self || !self->plugin){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } + if (!self || !self->plugin) { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } - /* ghost? */ - if (self->plugin == tmedia_session_ghost_plugin_def_t){ - return ((const tmedia_session_ghost_t*)self)->media; - } - else{ - return self->plugin->media; - } + /* ghost? */ + if (self->plugin == tmedia_session_ghost_plugin_def_t) { + return ((const tmedia_session_ghost_t*)self)->media; + } + else { + return self->plugin->media; + } } /* internal function: get local offer */ const tsdp_header_M_t* tmedia_session_get_lo(tmedia_session_t* self) { - const tsdp_header_M_t* m; + const tsdp_header_M_t* m; - if (!self || !self->plugin || !self->plugin->get_local_offer){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } + if (!self || !self->plugin || !self->plugin->get_local_offer) { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } - if ((m = self->plugin->get_local_offer(self))){ - self->ro_changed = tsk_false; /* we should have a fresh local offer (based on the latest ro) */ - } - return m; + if ((m = self->plugin->get_local_offer(self))) { + self->ro_changed = tsk_false; /* we should have a fresh local offer (based on the latest ro) */ + } + return m; } /* Match a codec */ tmedia_codecs_L_t* tmedia_session_match_codec(tmedia_session_t* self, const tsdp_header_M_t* M) { - const tmedia_codec_t *codec; - char *rtpmap = tsk_null, *fmtp = tsk_null, *image_attr = tsk_null, *name = tsk_null; - const tsdp_fmt_t* fmt; - const tsk_list_item_t *it1, *it2; - tsk_bool_t found = tsk_false; - tmedia_codecs_L_t* matchingCodecs = tsk_null; - - if (!self || !M){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } - - - /* foreach format */ - tsk_list_foreach(it1, M->FMTs){ - fmt = it1->data; - - /* foreach codec */ - tsk_list_foreach(it2, self->codecs){ - /* 'tmedia_codec_id_none' is used for fake codecs (e.g. dtmf or msrp) and should not be filtered beacuse of backward compatibility*/ - if (!(codec = it2->data) || !codec->plugin || !(codec->id == tmedia_codec_id_none || (codec->id & self->codecs_allowed))){ - continue; - } - - // Guard to avoid matching a codec more than once - // For example, H.264 codecs without profiles (Jitsi, Tiscali PC client) to distinguish them could match more than once - if (matchingCodecs && tsk_list_find_object_by_pred(matchingCodecs, __pred_find_codec_by_format, codec)){ - continue; - } - - // Dyn. payload type - if (codec->dyn && (rtpmap = tsdp_header_M_get_rtpmap(M, fmt->value))){ - int32_t rate, channels; - /* parse rtpmap */ - if (tmedia_parse_rtpmap(rtpmap, &name, &rate, &channels)){ - goto next; - } - - /* compare name and rate... what about channels? */ - if (tsk_striequals(name, codec->name) && (!rate || !codec->plugin->rate || (codec->plugin->rate == rate))){ - goto compare_fmtp; - } - } - // Fixed payload type - else{ - if (tsk_striequals(fmt->value, codec->format)){ - goto compare_fmtp; - } - } - - /* rtpmap do not match: free strings and try next codec */ - goto next; - - compare_fmtp: - if ((fmtp = tsdp_header_M_get_fmtp(M, fmt->value))){ /* remote have fmtp? */ - if (tmedia_codec_sdp_att_match(codec, "fmtp", fmtp)){ /* fmtp matches? */ - if (codec->type & tmedia_video) goto compare_imageattr; - else found = tsk_true; - } - else goto next; - } - else{ /* no fmtp -> always match */ - if (codec->type & tmedia_video) goto compare_imageattr; - else found = tsk_true; - } - - compare_imageattr: - if (codec->type & tmedia_video){ - if ((image_attr = tsdp_header_M_get_imageattr(M, fmt->value))){ - if (tmedia_codec_sdp_att_match(codec, "imageattr", image_attr)) found = tsk_true; - } - else found = tsk_true; - } - - // update neg. format - if (found) tsk_strupdate((char**)&codec->neg_format, fmt->value); - - next: - TSK_FREE(name); - TSK_FREE(fmtp); - TSK_FREE(rtpmap); - TSK_FREE(image_attr); - if (found){ - tmedia_codec_t * copy; - if (!matchingCodecs){ - matchingCodecs = tsk_list_create(); - } - copy = tsk_object_ref((void*)codec); - tsk_list_push_back_data(matchingCodecs, (void**)©); - - found = tsk_false; - break; - } - } - } - - - return matchingCodecs; + const tmedia_codec_t *codec; + char *rtpmap = tsk_null, *fmtp = tsk_null, *image_attr = tsk_null, *name = tsk_null; + const tsdp_fmt_t* fmt; + const tsk_list_item_t *it1, *it2; + tsk_bool_t found = tsk_false; + tmedia_codecs_L_t* matchingCodecs = tsk_null; + + if (!self || !M) { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + + /* foreach format */ + tsk_list_foreach(it1, M->FMTs) { + fmt = it1->data; + + /* foreach codec */ + tsk_list_foreach(it2, self->codecs) { + /* 'tmedia_codec_id_none' is used for fake codecs (e.g. dtmf or msrp) and should not be filtered beacuse of backward compatibility*/ + if (!(codec = it2->data) || !codec->plugin || !(codec->id == tmedia_codec_id_none || (codec->id & self->codecs_allowed))) { + continue; + } + + // Guard to avoid matching a codec more than once + // For example, H.264 codecs without profiles (Jitsi, Tiscali PC client) to distinguish them could match more than once + if (matchingCodecs && tsk_list_find_object_by_pred(matchingCodecs, __pred_find_codec_by_format, codec)) { + continue; + } + + // Dyn. payload type + if (codec->dyn && (rtpmap = tsdp_header_M_get_rtpmap(M, fmt->value))) { + int32_t rate, channels; + /* parse rtpmap */ + if (tmedia_parse_rtpmap(rtpmap, &name, &rate, &channels)) { + goto next; + } + + /* compare name and rate... what about channels? */ + if (tsk_striequals(name, codec->name) && (!rate || !codec->plugin->rate || (codec->plugin->rate == rate))) { + goto compare_fmtp; + } + } + // Fixed payload type + else { + if (tsk_striequals(fmt->value, codec->format)) { + goto compare_fmtp; + } + } + + /* rtpmap do not match: free strings and try next codec */ + goto next; + +compare_fmtp: + if ((fmtp = tsdp_header_M_get_fmtp(M, fmt->value))) { /* remote have fmtp? */ + if (tmedia_codec_sdp_att_match(codec, "fmtp", fmtp)) { /* fmtp matches? */ + if (codec->type & tmedia_video) { + goto compare_imageattr; + } + else { + found = tsk_true; + } + } + else { + goto next; + } + } + else { /* no fmtp -> always match */ + if (codec->type & tmedia_video) { + goto compare_imageattr; + } + else { + found = tsk_true; + } + } + +compare_imageattr: + if (codec->type & tmedia_video) { + if ((image_attr = tsdp_header_M_get_imageattr(M, fmt->value))) { + if (tmedia_codec_sdp_att_match(codec, "imageattr", image_attr)) { + found = tsk_true; + } + } + else { + found = tsk_true; + } + } + + // update neg. format + if (found) { + tsk_strupdate((char**)&codec->neg_format, fmt->value); + } + +next: + TSK_FREE(name); + TSK_FREE(fmtp); + TSK_FREE(rtpmap); + TSK_FREE(image_attr); + if (found) { + tmedia_codec_t * copy; + if (!matchingCodecs) { + matchingCodecs = tsk_list_create(); + } + copy = tsk_object_ref((void*)codec); + tsk_list_push_back_data(matchingCodecs, (void**)©); + + found = tsk_false; + break; + } + } + } + + + return matchingCodecs; } int tmedia_session_set_onrtcp_cbfn(tmedia_session_t* self, const void* context, tmedia_session_rtcp_onevent_cb_f func) { - if (self && self->plugin && self->plugin->rtcp.set_onevent_cbfn){ - return self->plugin->rtcp.set_onevent_cbfn(self, context, func); - } - return -1; + if (self && self->plugin && self->plugin->rtcp.set_onevent_cbfn) { + return self->plugin->rtcp.set_onevent_cbfn(self, context, func); + } + return -1; } int tmedia_session_send_rtcp_event(tmedia_session_t* self, tmedia_rtcp_event_type_t event_type, uint32_t ssrc_media) { - if (self && self->plugin && self->plugin->rtcp.send_event){ - return self->plugin->rtcp.send_event(self, event_type, ssrc_media); - } - TSK_DEBUG_INFO("Not sending RTCP event with SSRC = %u because no callback function found", ssrc_media); - return -1; + if (self && self->plugin && self->plugin->rtcp.send_event) { + return self->plugin->rtcp.send_event(self, event_type, ssrc_media); + } + TSK_DEBUG_INFO("Not sending RTCP event with SSRC = %u because no callback function found", ssrc_media); + return -1; } int tmedia_session_recv_rtcp_event(tmedia_session_t* self, tmedia_rtcp_event_type_t event_type, uint32_t ssrc_media) { - if (self && self->plugin && self->plugin->rtcp.recv_event){ - return self->plugin->rtcp.recv_event(self, event_type, ssrc_media); - } - TSK_DEBUG_INFO("Not receiving RTCP event with SSRC = %u because no callback function found", ssrc_media); - return -1; + if (self && self->plugin && self->plugin->rtcp.recv_event) { + return self->plugin->rtcp.recv_event(self, event_type, ssrc_media); + } + TSK_DEBUG_INFO("Not receiving RTCP event with SSRC = %u because no callback function found", ssrc_media); + return -1; } int tmedia_session_set_onerror_cbfn(tmedia_session_t* self, const void* usrdata, tmedia_session_onerror_cb_f fun) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - self->onerror_cb.fun = fun; - self->onerror_cb.usrdata = usrdata; - return 0; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->onerror_cb.fun = fun; + self->onerror_cb.usrdata = usrdata; + return 0; } int tmedia_session_set_rfc5168_cbfn(tmedia_session_t* self, const void* usrdata, tmedia_session_rfc5168_cb_f fun) { - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - self->rfc5168_cb.fun = fun; - self->rfc5168_cb.usrdata = usrdata; - return 0; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->rfc5168_cb.fun = fun; + self->rfc5168_cb.usrdata = usrdata; + return 0; } int tmedia_session_set_bfcp_cbfn(tmedia_session_t* self, const void* usrdata, tmedia_session_bfcp_cb_f fun) { - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - self->bfcp_cb.fun = fun; - self->bfcp_cb.usrdata = usrdata; - return 0; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->bfcp_cb.fun = fun; + self->bfcp_cb.usrdata = usrdata; + return 0; } /**@ingroup tmedia_session_group @@ -632,28 +669,28 @@ int tmedia_session_set_bfcp_cbfn(tmedia_session_t* self, const void* usrdata, tm */ int tmedia_session_deinit(tmedia_session_t* self) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - /* free codecs */ - TSK_OBJECT_SAFE_FREE(self->codecs); - TSK_OBJECT_SAFE_FREE(self->neg_codecs); + /* free codecs */ + TSK_OBJECT_SAFE_FREE(self->codecs); + TSK_OBJECT_SAFE_FREE(self->neg_codecs); - /* free lo, no and ro */ - TSK_OBJECT_SAFE_FREE(self->M.lo); - TSK_OBJECT_SAFE_FREE(self->M.ro); + /* free lo, no and ro */ + TSK_OBJECT_SAFE_FREE(self->M.lo); + TSK_OBJECT_SAFE_FREE(self->M.ro); - /* QoS */ - TSK_OBJECT_SAFE_FREE(self->qos); + /* QoS */ + TSK_OBJECT_SAFE_FREE(self->qos); - /* DTLS */ - TSK_FREE(self->dtls.file_ca); - TSK_FREE(self->dtls.file_pbk); - TSK_FREE(self->dtls.file_pvk); + /* DTLS */ + TSK_FREE(self->dtls.file_ca); + TSK_FREE(self->dtls.file_pbk); + TSK_FREE(self->dtls.file_pvk); - return 0; + return 0; } /**@ingroup tmedia_session_group @@ -664,109 +701,109 @@ int tmedia_session_deinit(tmedia_session_t* self) */ int tmedia_session_audio_send_dtmf(tmedia_session_audio_t* self, uint8_t event) { - if (!self || !TMEDIA_SESSION(self)->plugin || !TMEDIA_SESSION(self)->plugin->audio.send_dtmf){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - return TMEDIA_SESSION(self)->plugin->audio.send_dtmf(TMEDIA_SESSION(self), event); + if (!self || !TMEDIA_SESSION(self)->plugin || !TMEDIA_SESSION(self)->plugin->audio.send_dtmf) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + return TMEDIA_SESSION(self)->plugin->audio.send_dtmf(TMEDIA_SESSION(self), event); } int tmedia_session_t140_set_ondata_cbfn(tmedia_session_t* self, const void* context, tmedia_session_t140_ondata_cb_f func) { - if (self && self->plugin && self->plugin->t140.set_ondata_cbfn){ - return self->plugin->t140.set_ondata_cbfn(self, context, func); - } - return -1; + if (self && self->plugin && self->plugin->t140.set_ondata_cbfn) { + return self->plugin->t140.set_ondata_cbfn(self, context, func); + } + return -1; } int tmedia_session_t140_send_data(tmedia_session_t* self, enum tmedia_t140_data_type_e data_type, const void* data_ptr, unsigned data_size) { - if (self && self->plugin && self->plugin->t140.send_data){ - return self->plugin->t140.send_data(self, data_type, data_ptr, data_size); - } - return -1; + if (self && self->plugin && self->plugin->t140.send_data) { + return self->plugin->t140.send_data(self, data_type, data_ptr, data_size); + } + return -1; } /* internal function used to prepare a session */ int _tmedia_session_load_codecs(tmedia_session_t* self) { - tsk_size_t i = 0; - tmedia_codec_t* codec; - const tmedia_codec_plugin_def_t* plugin; - const tsk_list_item_t* item; - tmedia_type_t type; - - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - if (!self->codecs && !(self->codecs = tsk_list_create())){ - TSK_DEBUG_ERROR("Failed to create new list"); - return -1; - } - - tsk_list_lock(self->codecs); - - /* remove old codecs */ - tsk_list_clear_items(self->codecs); - - type = self->type; - if ((type & tmedia_bfcp_video) == tmedia_bfcp_video) { - type |= tmedia_video; - } - if ((type & tmedia_bfcp_audio) == tmedia_bfcp_audio) { - type |= tmedia_audio; - } - - /* for each registered plugin create a session instance */ - while ((i < TMED_CODEC_MAX_PLUGINS) && (plugin = __tmedia_codec_plugins[i++])){ - /* 'tmedia_codec_id_none' is used for fake codecs (e.g. dtmf, bfcp or msrp) and should not be filtered beacuse of backward compatibility*/ - if ((plugin->type & type) && (plugin->codec_id == tmedia_codec_id_none || (plugin->codec_id & self->codecs_allowed))){ - // do not load bfcp codec for "audiobfcp" and "videobfcp" session - if ((plugin->type == tmedia_bfcp) && (type != tmedia_bfcp)) { - continue; - } - if ((codec = tmedia_codec_create(plugin->format))){ - if (!self->codecs){ - self->codecs = tsk_list_create(); - } - tsk_list_push_back_data(self->codecs, (void**)(&codec)); - } - } - } - - // filter negotiated codecs with the newly loaded codecs - if (1){ // code valid for all use-cases but for now it's not fully tested and not needed for the clients - filter_neg_codecs: - tsk_list_foreach(item, self->neg_codecs){ - if (!(codec = (item->data))){ - continue; - } - if (!(tsk_list_find_item_by_pred(self->codecs, __pred_find_codec_by_id, &codec->id))){ - const char* codec_name = codec->plugin ? codec->plugin->name : "unknown"; - const char* neg_format = codec->neg_format ? codec->neg_format : codec->format; - TSK_DEBUG_INFO("Codec '%s' with format '%s' was negotiated but [supported codecs] updated without it -> removing", codec_name, neg_format); - // update sdp and remove the codec from the list - if (self->M.lo && !TSK_LIST_IS_EMPTY(self->M.lo->FMTs)){ - if (self->M.lo->FMTs->head->next == tsk_null && tsdp_header_M_have_fmt(self->M.lo, neg_format)){ // single item? - // rejecting a media with port equal to zero requires at least one format - TSK_DEBUG_INFO("[supported codecs] updated but do not remove codec with name='%s' and format='%s' because it's the last one", codec_name, neg_format); - self->M.lo->port = 0; - } - else{ - tsdp_header_M_remove_fmt(self->M.lo, neg_format); - } - } - tsk_list_remove_item_by_data(self->neg_codecs, codec); - goto filter_neg_codecs; - } - } - } - - tsk_list_unlock(self->codecs); - - return 0; + tsk_size_t i = 0; + tmedia_codec_t* codec; + const tmedia_codec_plugin_def_t* plugin; + const tsk_list_item_t* item; + tmedia_type_t type; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if (!self->codecs && !(self->codecs = tsk_list_create())) { + TSK_DEBUG_ERROR("Failed to create new list"); + return -1; + } + + tsk_list_lock(self->codecs); + + /* remove old codecs */ + tsk_list_clear_items(self->codecs); + + type = self->type; + if ((type & tmedia_bfcp_video) == tmedia_bfcp_video) { + type |= tmedia_video; + } + if ((type & tmedia_bfcp_audio) == tmedia_bfcp_audio) { + type |= tmedia_audio; + } + + /* for each registered plugin create a session instance */ + while ((i < TMED_CODEC_MAX_PLUGINS) && (plugin = __tmedia_codec_plugins[i++])) { + /* 'tmedia_codec_id_none' is used for fake codecs (e.g. dtmf, bfcp or msrp) and should not be filtered beacuse of backward compatibility*/ + if ((plugin->type & type) && (plugin->codec_id == tmedia_codec_id_none || (plugin->codec_id & self->codecs_allowed))) { + // do not load bfcp codec for "audiobfcp" and "videobfcp" session + if ((plugin->type == tmedia_bfcp) && (type != tmedia_bfcp)) { + continue; + } + if ((codec = tmedia_codec_create(plugin->format))) { + if (!self->codecs) { + self->codecs = tsk_list_create(); + } + tsk_list_push_back_data(self->codecs, (void**)(&codec)); + } + } + } + + // filter negotiated codecs with the newly loaded codecs + if (1) { // code valid for all use-cases but for now it's not fully tested and not needed for the clients +filter_neg_codecs: + tsk_list_foreach(item, self->neg_codecs) { + if (!(codec = (item->data))) { + continue; + } + if (!(tsk_list_find_item_by_pred(self->codecs, __pred_find_codec_by_id, &codec->id))) { + const char* codec_name = codec->plugin ? codec->plugin->name : "unknown"; + const char* neg_format = codec->neg_format ? codec->neg_format : codec->format; + TSK_DEBUG_INFO("Codec '%s' with format '%s' was negotiated but [supported codecs] updated without it -> removing", codec_name, neg_format); + // update sdp and remove the codec from the list + if (self->M.lo && !TSK_LIST_IS_EMPTY(self->M.lo->FMTs)) { + if (self->M.lo->FMTs->head->next == tsk_null && tsdp_header_M_have_fmt(self->M.lo, neg_format)) { // single item? + // rejecting a media with port equal to zero requires at least one format + TSK_DEBUG_INFO("[supported codecs] updated but do not remove codec with name='%s' and format='%s' because it's the last one", codec_name, neg_format); + self->M.lo->port = 0; + } + else { + tsdp_header_M_remove_fmt(self->M.lo, neg_format); + } + } + tsk_list_remove_item_by_data(self->neg_codecs, codec); + goto filter_neg_codecs; + } + } + } + + tsk_list_unlock(self->codecs); + + return 0; } @@ -781,155 +818,169 @@ int _tmedia_session_load_codecs(tmedia_session_t* self) */ tmedia_session_mgr_t* tmedia_session_mgr_create(tmedia_type_t type, const char* addr, tsk_bool_t ipv6, tsk_bool_t offerer) { - tmedia_session_mgr_t* mgr; + tmedia_session_mgr_t* mgr; - if (!(mgr = tsk_object_new(tmedia_session_mgr_def_t))){ - TSK_DEBUG_ERROR("Failed to create Media Session manager"); - return tsk_null; - } + if (!(mgr = tsk_object_new(tmedia_session_mgr_def_t))) { + TSK_DEBUG_ERROR("Failed to create Media Session manager"); + return tsk_null; + } - /* init */ - mgr->type = type; - mgr->addr = tsk_strdup(addr); - mgr->ipv6 = ipv6; + /* init */ + mgr->type = type; + mgr->addr = tsk_strdup(addr); + mgr->ipv6 = ipv6; - /* load sessions (will allow us to generate lo) */ - if (offerer){ - mgr->offerer = tsk_true; - //if(_tmedia_session_mgr_load_sessions(mgr)){ - /* Do nothing */ - // TSK_DEBUG_ERROR("Failed to load sessions"); - //} - } + /* load sessions (will allow us to generate lo) */ + if (offerer) { + mgr->offerer = tsk_true; + //if(_tmedia_session_mgr_load_sessions(mgr)){ + /* Do nothing */ + // TSK_DEBUG_ERROR("Failed to load sessions"); + //} + } - return mgr; + return mgr; } /**@ingroup tmedia_session_group */ int tmedia_session_mgr_set_media_type(tmedia_session_mgr_t* self, tmedia_type_t type) { - static tsk_bool_t __force_set = tsk_false; - return tmedia_session_mgr_set_media_type_2(self, type, __force_set); + static tsk_bool_t __force_set = tsk_false; + return tmedia_session_mgr_set_media_type_2(self, type, __force_set); } /**@ingroup tmedia_session_group */ int tmedia_session_mgr_set_media_type_2(tmedia_session_mgr_t* self, tmedia_type_t type, tsk_bool_t force) { - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - if (force || self->type != type) { - self->mediaType_changed = tsk_true; - self->type = type; - } - return 0; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if (force || self->type != type) { + self->mediaType_changed = tsk_true; + self->type = type; + } + return 0; } // special set() case int tmedia_session_mgr_set_codecs_supported(tmedia_session_mgr_t* self, tmedia_codec_id_t codecs_supported) { - int ret = 0; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - // calling set() could create zombies (media sessions with port equal to zero) - ret = tmedia_session_mgr_set(self, - TMEDIA_SESSION_SET_INT32(self->type, "codecs-supported", codecs_supported), - tsk_null); - if (ret == 0 && self->sdp.lo){ - // update type (will discard zombies) - tmedia_type_t new_type = tmedia_type_from_sdp(self->sdp.lo); - if (new_type != self->type){ - TSK_DEBUG_INFO("codecs-supported updated and media type changed from %d to %d", self->type, new_type); - self->type = new_type; - } - } - return ret; + int ret = 0; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + // calling set() could create zombies (media sessions with port equal to zero) + ret = tmedia_session_mgr_set(self, + TMEDIA_SESSION_SET_INT32(self->type, "codecs-supported", codecs_supported), + tsk_null); + if (ret == 0 && self->sdp.lo) { + // update type (will discard zombies) + tmedia_type_t new_type = tmedia_type_from_sdp(self->sdp.lo); + if (new_type != self->type) { + TSK_DEBUG_INFO("codecs-supported updated and media type changed from %d to %d", self->type, new_type); + self->type = new_type; + } + } + return ret; } /**@ingroup tmedia_session_group */ tmedia_session_t* tmedia_session_mgr_find(tmedia_session_mgr_t* self, tmedia_type_t type) { - tmedia_session_t* session; + tmedia_session_t* session; - tsk_list_lock(self->sessions); - session = (tmedia_session_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &type); - tsk_list_unlock(self->sessions); + tsk_list_lock(self->sessions); + session = (tmedia_session_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &type); + tsk_list_unlock(self->sessions); - return tsk_object_ref(session); + return tsk_object_ref(session); } /**@ingroup tmedia_session_group */ int tmedia_session_mgr_set_natt_ctx(tmedia_session_mgr_t* self, struct tnet_nat_ctx_s* natt_ctx, const char* public_addr) { - if (!self || !natt_ctx){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - TSK_OBJECT_SAFE_FREE(self->natt_ctx); - self->natt_ctx = tsk_object_ref(natt_ctx); - tsk_strupdate(&self->public_addr, public_addr); + if (!self || !natt_ctx) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + TSK_OBJECT_SAFE_FREE(self->natt_ctx); + self->natt_ctx = tsk_object_ref(natt_ctx); + tsk_strupdate(&self->public_addr, public_addr); - tmedia_session_mgr_set(self, - TMEDIA_SESSION_SET_POBJECT(self->type, "natt-ctx", self->natt_ctx), - TMEDIA_SESSION_SET_NULL()); - return 0; + tmedia_session_mgr_set(self, + TMEDIA_SESSION_SET_POBJECT(self->type, "natt-ctx", self->natt_ctx), + TMEDIA_SESSION_SET_NULL()); + return 0; } // @deprecated int tmedia_session_mgr_set_ice_ctx(tmedia_session_mgr_t* self, struct tnet_ice_ctx_s* ctx_audio, struct tnet_ice_ctx_s* ctx_video) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - TSK_OBJECT_SAFE_FREE(self->ice.ctx_audio); // backward compatibility - TSK_OBJECT_SAFE_FREE(self->ice.ctx_video); // backward compatibility - if (self->type & tmedia_audio) { - tmedia_session_mgr_set_ice_ctx_2(self, tmedia_audio, ctx_audio); - } - if (self->type & tmedia_video) { - tmedia_session_mgr_set_ice_ctx_2(self, tmedia_video, ctx_video); - } - return 0; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + TSK_OBJECT_SAFE_FREE(self->ice.ctx_audio); // backward compatibility + TSK_OBJECT_SAFE_FREE(self->ice.ctx_video); // backward compatibility + if (self->type & tmedia_audio) { + tmedia_session_mgr_set_ice_ctx_2(self, tmedia_audio, ctx_audio); + } + if (self->type & tmedia_video) { + tmedia_session_mgr_set_ice_ctx_2(self, tmedia_video, ctx_video); + } + return 0; } int tmedia_session_mgr_set_ice_ctx_2(tmedia_session_mgr_t* self, tmedia_type_t type, struct tnet_ice_ctx_s* ctx) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - if ((self->type & type) == type) { - struct tnet_ice_ctx_s **_ctx = tsk_null; - switch (type) { - case tmedia_audio: _ctx = &self->ice.ctx_audio; break; - case tmedia_video: _ctx = &self->ice.ctx_video; break; - case tmedia_bfcp_video: _ctx = &self->ice.ctx_bfcpvid; break; - default: TSK_DEBUG_ERROR("Media type(%d) not supported by this session manager", type); return -2; - } - TSK_OBJECT_SAFE_FREE((*_ctx)); - *_ctx = tsk_object_ref(ctx); - return tmedia_session_mgr_set(self, - TMEDIA_SESSION_SET_POBJECT(type, "ice-ctx", ctx), - TMEDIA_SESSION_SET_NULL()); - } - else if (!ctx) { //cleanup - switch (type) { - case tmedia_audio: TSK_OBJECT_SAFE_FREE(self->ice.ctx_audio); return 0; - case tmedia_video: TSK_OBJECT_SAFE_FREE(self->ice.ctx_video); return 0; - case tmedia_bfcp_video: TSK_OBJECT_SAFE_FREE(self->ice.ctx_bfcpvid); return 0; - } - } - TSK_DEBUG_ERROR("Ignoring ICE context definition for media type %d", type); - return -2; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if ((self->type & type) == type) { + struct tnet_ice_ctx_s **_ctx = tsk_null; + switch (type) { + case tmedia_audio: + _ctx = &self->ice.ctx_audio; + break; + case tmedia_video: + _ctx = &self->ice.ctx_video; + break; + case tmedia_bfcp_video: + _ctx = &self->ice.ctx_bfcpvid; + break; + default: + TSK_DEBUG_ERROR("Media type(%d) not supported by this session manager", type); + return -2; + } + TSK_OBJECT_SAFE_FREE((*_ctx)); + *_ctx = tsk_object_ref(ctx); + return tmedia_session_mgr_set(self, + TMEDIA_SESSION_SET_POBJECT(type, "ice-ctx", ctx), + TMEDIA_SESSION_SET_NULL()); + } + else if (!ctx) { //cleanup + switch (type) { + case tmedia_audio: + TSK_OBJECT_SAFE_FREE(self->ice.ctx_audio); + return 0; + case tmedia_video: + TSK_OBJECT_SAFE_FREE(self->ice.ctx_video); + return 0; + case tmedia_bfcp_video: + TSK_OBJECT_SAFE_FREE(self->ice.ctx_bfcpvid); + return 0; + } + } + TSK_DEBUG_ERROR("Ignoring ICE context definition for media type %d", type); + return -2; } /**@ingroup tmedia_session_group @@ -942,7 +993,7 @@ int tmedia_session_mgr_set_ice_ctx_2(tmedia_session_mgr_t* self, tmedia_type_t t */ int tmedia_session_mgr_start(tmedia_session_mgr_t* self) { - return _tmedia_session_mgr_start(self, kSessionIndexAll); + return _tmedia_session_mgr_start(self, kSessionIndexAll); } /**@ingroup tmedia_session_group @@ -953,19 +1004,19 @@ int tmedia_session_mgr_start(tmedia_session_mgr_t* self) */ int tmedia_session_mgr_set(tmedia_session_mgr_t* self, ...) { - va_list ap; - int ret; + va_list ap; + int ret; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - va_start(ap, self); - ret = tmedia_session_mgr_set_2(self, &ap); - va_end(ap); + va_start(ap, self); + ret = tmedia_session_mgr_set_2(self, &ap); + va_end(ap); - return ret; + return ret; } /**@ingroup tmedia_session_group @@ -976,29 +1027,29 @@ int tmedia_session_mgr_set(tmedia_session_mgr_t* self, ...) */ int tmedia_session_mgr_set_2(tmedia_session_mgr_t* self, va_list *app) { - tmedia_params_L_t* params; + tmedia_params_L_t* params; - if (!self || !app){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self || !app) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - if ((params = tmedia_params_create_2(app))){ - if (!self->params){ - self->params = tsk_object_ref(params); - } - else{ - tsk_list_pushback_list(self->params, params); - } - TSK_OBJECT_SAFE_FREE(params); - } + if ((params = tmedia_params_create_2(app))) { + if (!self->params) { + self->params = tsk_object_ref(params); + } + else { + tsk_list_pushback_list(self->params, params); + } + TSK_OBJECT_SAFE_FREE(params); + } - /* load params if we already have sessions */ - if (!TSK_LIST_IS_EMPTY(self->sessions)){ - _tmedia_session_mgr_apply_params(self); - } + /* load params if we already have sessions */ + if (!TSK_LIST_IS_EMPTY(self->sessions)) { + _tmedia_session_mgr_apply_params(self); + } - return 0; + return 0; } /**@ingroup tmedia_session_group @@ -1009,59 +1060,59 @@ int tmedia_session_mgr_set_2(tmedia_session_mgr_t* self, va_list *app) */ int tmedia_session_mgr_set_3(tmedia_session_mgr_t* self, const tmedia_params_L_t* params) { - if (!self || !params){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self || !params) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - if (!self->params){ - self->params = tsk_list_create(); - } - tsk_list_pushback_list(self->params, params); + if (!self->params) { + self->params = tsk_list_create(); + } + tsk_list_pushback_list(self->params, params); - /* load params if we already have sessions */ - if (!TSK_LIST_IS_EMPTY(self->sessions)){ - _tmedia_session_mgr_apply_params(self); - } + /* load params if we already have sessions */ + if (!TSK_LIST_IS_EMPTY(self->sessions)) { + _tmedia_session_mgr_apply_params(self); + } - return 0; + return 0; } int tmedia_session_mgr_get(tmedia_session_mgr_t* self, ...) { - va_list ap; - int ret = 0; - tmedia_params_L_t* params; - const tsk_list_item_t *item1, *item2; - - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - va_start(ap, self); - - if ((params = tmedia_params_create_2(&ap))){ - tmedia_session_t* session; - tmedia_param_t* param; - tsk_list_foreach(item2, params){ - if ((param = item2->data)){ - tsk_list_foreach(item1, self->sessions){ - if (!(session = (tmedia_session_t*)item1->data) || !session->plugin){ - continue; - } - if ((session->type & param->media_type) == session->type && session->plugin->set){ - ret = session->plugin->get(session, param); - } - } - } - } - TSK_OBJECT_SAFE_FREE(params); - } - - va_end(ap); - - return ret; + va_list ap; + int ret = 0; + tmedia_params_L_t* params; + const tsk_list_item_t *item1, *item2; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + va_start(ap, self); + + if ((params = tmedia_params_create_2(&ap))) { + tmedia_session_t* session; + tmedia_param_t* param; + tsk_list_foreach(item2, params) { + if ((param = item2->data)) { + tsk_list_foreach(item1, self->sessions) { + if (!(session = (tmedia_session_t*)item1->data) || !session->plugin) { + continue; + } + if ((session->type & param->media_type) == session->type && session->plugin->set) { + ret = session->plugin->get(session, param); + } + } + } + } + TSK_OBJECT_SAFE_FREE(params); + } + + va_end(ap); + + return ret; } /**@ingroup tmedia_session_group @@ -1073,7 +1124,7 @@ int tmedia_session_mgr_get(tmedia_session_mgr_t* self, ...) */ int tmedia_session_mgr_stop(tmedia_session_mgr_t* self) { - return _tmedia_session_mgr_stop(self, kSessionIndexAll); + return _tmedia_session_mgr_stop(self, kSessionIndexAll); } /**@ingroup tmedia_session_group @@ -1081,7 +1132,7 @@ int tmedia_session_mgr_stop(tmedia_session_mgr_t* self) */ const tsdp_message_t* tmedia_session_mgr_get_lo(tmedia_session_mgr_t* self) { - return _tmedia_session_mgr_get_lo(self, kSkipSessionLoadFalse, kForceUpdateLOFalse); + return _tmedia_session_mgr_get_lo(self, kSkipSessionLoadFalse, kForceUpdateLOFalse); } @@ -1090,486 +1141,503 @@ const tsdp_message_t* tmedia_session_mgr_get_lo(tmedia_session_mgr_t* self) */ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t* sdp, tmedia_ro_type_t ro_type) { - const tmedia_session_t* ms; - const tsdp_header_M_t* M; - const tsdp_header_C_t* C; /* global "c=" line */ - const tsdp_header_O_t* O; - tsk_size_t index = 0; - tsk_size_t active_sessions_count = 0, m_lines_count = 0; - int ret = 0; - tsk_bool_t found; - tsk_bool_t stopped_to_reconf[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; - tsk_bool_t is_ro_codecs_changed[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; - tsk_bool_t is_local_encoder_still_ok[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; // decoder is dynamically mapped for each incoming rtp frame -> no need to check it - tsk_bool_t is_ro_network_info_changed[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; - tsk_bool_t is_ro_hold_resume_changed[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; - tsk_bool_t is_ro_loopback_address[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; - tsk_bool_t is_ice_enabled[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; - tsk_bool_t is_ice_restart[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; - tsk_bool_t is_dtls_fingerprint_changed[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; + const tmedia_session_t* ms; + const tsdp_header_M_t* M; + const tsdp_header_C_t* C; /* global "c=" line */ + const tsdp_header_O_t* O; + tsk_size_t index = 0; + tsk_size_t active_sessions_count = 0, m_lines_count = 0; + int ret = 0; + tsk_bool_t found; + tsk_bool_t stopped_to_reconf[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; + tsk_bool_t is_ro_codecs_changed[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; + tsk_bool_t is_local_encoder_still_ok[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; // decoder is dynamically mapped for each incoming rtp frame -> no need to check it + tsk_bool_t is_ro_network_info_changed[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; + tsk_bool_t is_ro_hold_resume_changed[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; + tsk_bool_t is_ro_loopback_address[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; + tsk_bool_t is_ice_enabled[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; + tsk_bool_t is_ice_restart[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; + tsk_bool_t is_dtls_fingerprint_changed[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; tsk_bool_t is_sdes_crypto_changed[TMEDIA_SESSION_MAX_LINES] = { tsk_false }; - tmedia_type_t media_types[TMEDIA_SESSION_MAX_LINES] = { tmedia_none }; - tsk_bool_t is_media_type_changed = tsk_false; - tsk_bool_t is_ro_media_lines_changed = tsk_false; - tsk_bool_t had_ro_sdp, had_lo_sdp, had_ro_provisional, is_ro_provisional_final_matching = tsk_false; - tsk_bool_t is_new_mediatype_striped = tsk_false; - tmedia_qos_stype_t qos_type = tmedia_qos_stype_none; - tmedia_type_t new_mediatype = tmedia_none; - tmedia_sessions_L_t *list_tmp_sessions; - - if (!self || !sdp) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - if (!(list_tmp_sessions = tsk_list_create())) { - TSK_DEBUG_ERROR("Failed to create tmp list"); - return -2; - } - - tsk_safeobj_lock(self); - tsk_list_lock(self->sessions); - - new_mediatype = tmedia_type_from_sdp(sdp); - had_ro_sdp = (self->sdp.ro != tsk_null); - had_lo_sdp = (self->sdp.lo != tsk_null); - had_ro_provisional = (had_ro_sdp && self->ro_provisional); - - // Remove BFCP offer if not locally enabled. Only the client can init BFCP session. - if ((ro_type & tmedia_ro_type_offer)) { - if (!(self->type & tmedia_bfcp_video)) { - is_new_mediatype_striped |= (new_mediatype & tmedia_bfcp_video); - new_mediatype &= ~tmedia_bfcp_video; - } - if (!(self->type & tmedia_bfcp_audio)) { - is_new_mediatype_striped |= (new_mediatype & tmedia_bfcp_video); - new_mediatype &= ~tmedia_bfcp_audio; - } - if (!(self->type & tmedia_bfcp)) { - is_new_mediatype_striped |= (new_mediatype & tmedia_bfcp_video); - new_mediatype &= ~tmedia_bfcp; - } - } - - /* RFC 3264 subcaluse 8 - When issuing an offer that modifies the session, the "o=" line of the new SDP MUST be identical to that in the previous SDP, - except that the version in the origin field MUST increment by one from the previous SDP. If the version in the origin line - does not increment, the SDP MUST be identical to the SDP with that version number. The answerer MUST be prepared to receive - an offer that contains SDP with a version that has not changed; this is effectively a no-op. - */ - if ((O = (const tsdp_header_O_t*)tsdp_message_get_header(sdp, tsdp_htype_O))){ - tsk_bool_t is_ro_provisional; - if (self->sdp.ro_ver == (int32_t)O->sess_version){ - TSK_DEBUG_INFO("Remote offer has not changed"); - ret = 0; - goto bail; - } - // Last provisional and new final sdp messages match only if: - // - session version diff is == 1 - // - previous sdp was provisional and new one is final - // - the new final sdp is inside an answer - is_ro_provisional = ((ro_type & tmedia_ro_type_provisional) == tmedia_ro_type_provisional); - is_ro_provisional_final_matching = ((had_ro_provisional && !is_ro_provisional) && ((self->sdp.ro_ver + 1) == O->sess_version) && ((ro_type & tmedia_ro_type_answer) == tmedia_ro_type_answer)); - self->sdp.ro_ver = (int32_t)O->sess_version; - } - else{ - TSK_DEBUG_ERROR("o= line is missing"); - ret = -2; - goto bail; - } - - /* SDP comparison */ - if ((sdp && self->sdp.ro)){ - const tsdp_header_M_t *M0, *M1; - const tsdp_header_C_t *C0, *C1; - const tsdp_header_A_t *A0, *A1; - const tsdp_header_A_t *A0_sess_fp, *A1_sess_fp; // session-level fingerprints - tsdp_header_M_diff_t med_level_diff; // media-level diff - index = 0; - A0_sess_fp = tsdp_message_get_headerA(self->sdp.ro, "fingerprint"); - A1_sess_fp = tsdp_message_get_headerA(sdp, "fingerprint"); - while ((M0 = (const tsdp_header_M_t*)tsdp_message_get_headerAt(self->sdp.ro, tsdp_htype_M, index))){ - ++m_lines_count; - if (m_lines_count >= TMEDIA_SESSION_MAX_LINES) { - TSK_DEBUG_ERROR("Too many m-lines %u>%u", (unsigned)m_lines_count, (unsigned)TMEDIA_SESSION_MAX_LINES); - ret = -2; - goto bail; - } - M1 = (const tsdp_header_M_t*)tsdp_message_get_headerAt(sdp, tsdp_htype_M, index); - // media-level diffs - - if ((ret = tsdp_header_M_diff(M0, M1, &med_level_diff)) != 0) { - goto bail; - } - if (med_level_diff & tsdp_header_M_diff_hold_resume) is_ro_hold_resume_changed[index] = tsk_true; - if (med_level_diff & tsdp_header_M_diff_index) is_ro_media_lines_changed = tsk_true; - if (med_level_diff & tsdp_header_M_diff_codecs) is_ro_codecs_changed[index] = tsk_true; - if (med_level_diff & tsdp_header_M_diff_network_info) is_ro_network_info_changed[index] = tsk_true; - if (tmedia_defaults_get_ice_enabled() && (med_level_diff & tsdp_header_M_diff_ice_enabled)) is_ice_enabled[index] = tsk_true; - if (tmedia_defaults_get_ice_enabled() && (med_level_diff & tsdp_header_M_diff_ice_restart)) is_ice_restart[index] = tsk_true; - if (med_level_diff & tsdp_header_M_diff_dtls_fingerprint) is_dtls_fingerprint_changed[index] = tsk_true; - if (med_level_diff & tsdp_header_M_diff_sdes_crypto) is_sdes_crypto_changed[index] = tsk_true; - if (med_level_diff & tsdp_header_M_diff_media_type); // cannot happen as media must keep same index - - // dtls fingerprint (session-level) - if (!is_dtls_fingerprint_changed[index]) { - A0 = tsdp_header_M_findA_at(M0, "fingerprint", 0); - A1 = M1 ? tsdp_header_M_findA_at(M1, "fingerprint", 0) : tsk_null; - is_dtls_fingerprint_changed[index] = (A0 && A1 && !tsk_striequals(A0->value, A1->value)); - } - // network info (session-level) - if (!is_ro_network_info_changed[index]) { - C0 = (const tsdp_header_C_t*)tsdp_message_get_headerAt(self->sdp.ro, tsdp_htype_C, index); - C1 = (const tsdp_header_C_t*)tsdp_message_get_headerAt(sdp, tsdp_htype_C, index); - // Connection informations must be both "null" or "not-null" - if (!(is_ro_network_info_changed[index] = !((C0 && C1) || (!C0 && !C1)))){ - if (C0) { - is_ro_network_info_changed[index] = (!tsk_strequals(C1->addr, C0->addr) || !tsk_strequals(C1->nettype, C0->nettype) || !tsk_strequals(C1->addrtype, C0->addrtype)); - } - } - } + tmedia_type_t media_types[TMEDIA_SESSION_MAX_LINES] = { tmedia_none }; + tsk_bool_t is_media_type_changed = tsk_false; + tsk_bool_t is_ro_media_lines_changed = tsk_false; + tsk_bool_t had_ro_sdp, had_lo_sdp, had_ro_provisional, is_ro_provisional_final_matching = tsk_false; + tsk_bool_t is_new_mediatype_striped = tsk_false; + tmedia_qos_stype_t qos_type = tmedia_qos_stype_none; + tmedia_type_t new_mediatype = tmedia_none; + tmedia_sessions_L_t *list_tmp_sessions; + + if (!self || !sdp) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if (!(list_tmp_sessions = tsk_list_create())) { + TSK_DEBUG_ERROR("Failed to create tmp list"); + return -2; + } + + tsk_safeobj_lock(self); + tsk_list_lock(self->sessions); + + new_mediatype = tmedia_type_from_sdp(sdp); + had_ro_sdp = (self->sdp.ro != tsk_null); + had_lo_sdp = (self->sdp.lo != tsk_null); + had_ro_provisional = (had_ro_sdp && self->ro_provisional); + + // Remove BFCP offer if not locally enabled. Only the client can init BFCP session. + if ((ro_type & tmedia_ro_type_offer)) { + if (!(self->type & tmedia_bfcp_video)) { + is_new_mediatype_striped |= (new_mediatype & tmedia_bfcp_video); + new_mediatype &= ~tmedia_bfcp_video; + } + if (!(self->type & tmedia_bfcp_audio)) { + is_new_mediatype_striped |= (new_mediatype & tmedia_bfcp_video); + new_mediatype &= ~tmedia_bfcp_audio; + } + if (!(self->type & tmedia_bfcp)) { + is_new_mediatype_striped |= (new_mediatype & tmedia_bfcp_video); + new_mediatype &= ~tmedia_bfcp; + } + } + + /* RFC 3264 subcaluse 8 + When issuing an offer that modifies the session, the "o=" line of the new SDP MUST be identical to that in the previous SDP, + except that the version in the origin field MUST increment by one from the previous SDP. If the version in the origin line + does not increment, the SDP MUST be identical to the SDP with that version number. The answerer MUST be prepared to receive + an offer that contains SDP with a version that has not changed; this is effectively a no-op. + */ + if ((O = (const tsdp_header_O_t*)tsdp_message_get_header(sdp, tsdp_htype_O))) { + tsk_bool_t is_ro_provisional; + if (self->sdp.ro_ver == (int32_t)O->sess_version) { + TSK_DEBUG_INFO("Remote offer has not changed"); + ret = 0; + goto bail; + } + // Last provisional and new final sdp messages match only if: + // - session version diff is == 1 + // - previous sdp was provisional and new one is final + // - the new final sdp is inside an answer + is_ro_provisional = ((ro_type & tmedia_ro_type_provisional) == tmedia_ro_type_provisional); + is_ro_provisional_final_matching = ((had_ro_provisional && !is_ro_provisional) && ((self->sdp.ro_ver + 1) == O->sess_version) && ((ro_type & tmedia_ro_type_answer) == tmedia_ro_type_answer)); + self->sdp.ro_ver = (int32_t)O->sess_version; + } + else { + TSK_DEBUG_ERROR("o= line is missing"); + ret = -2; + goto bail; + } + + /* SDP comparison */ + if ((sdp && self->sdp.ro)) { + const tsdp_header_M_t *M0, *M1; + const tsdp_header_C_t *C0, *C1; + const tsdp_header_A_t *A0, *A1; + const tsdp_header_A_t *A0_sess_fp, *A1_sess_fp; // session-level fingerprints + tsdp_header_M_diff_t med_level_diff; // media-level diff + index = 0; + A0_sess_fp = tsdp_message_get_headerA(self->sdp.ro, "fingerprint"); + A1_sess_fp = tsdp_message_get_headerA(sdp, "fingerprint"); + while ((M0 = (const tsdp_header_M_t*)tsdp_message_get_headerAt(self->sdp.ro, tsdp_htype_M, index))) { + ++m_lines_count; + if (m_lines_count >= TMEDIA_SESSION_MAX_LINES) { + TSK_DEBUG_ERROR("Too many m-lines %u>%u", (unsigned)m_lines_count, (unsigned)TMEDIA_SESSION_MAX_LINES); + ret = -2; + goto bail; + } + M1 = (const tsdp_header_M_t*)tsdp_message_get_headerAt(sdp, tsdp_htype_M, index); + // media-level diffs + + if ((ret = tsdp_header_M_diff(M0, M1, &med_level_diff)) != 0) { + goto bail; + } + if (med_level_diff & tsdp_header_M_diff_hold_resume) { + is_ro_hold_resume_changed[index] = tsk_true; + } + if (med_level_diff & tsdp_header_M_diff_index) { + is_ro_media_lines_changed = tsk_true; + } + if (med_level_diff & tsdp_header_M_diff_codecs) { + is_ro_codecs_changed[index] = tsk_true; + } + if (med_level_diff & tsdp_header_M_diff_network_info) { + is_ro_network_info_changed[index] = tsk_true; + } + if (tmedia_defaults_get_ice_enabled() && (med_level_diff & tsdp_header_M_diff_ice_enabled)) { + is_ice_enabled[index] = tsk_true; + } + if (tmedia_defaults_get_ice_enabled() && (med_level_diff & tsdp_header_M_diff_ice_restart)) { + is_ice_restart[index] = tsk_true; + } + if (med_level_diff & tsdp_header_M_diff_dtls_fingerprint) { + is_dtls_fingerprint_changed[index] = tsk_true; + } + if (med_level_diff & tsdp_header_M_diff_sdes_crypto) { + is_sdes_crypto_changed[index] = tsk_true; + } + if (med_level_diff & tsdp_header_M_diff_media_type); // cannot happen as media must keep same index + + // dtls fingerprint (session-level) + if (!is_dtls_fingerprint_changed[index]) { + A0 = tsdp_header_M_findA_at(M0, "fingerprint", 0); + A1 = M1 ? tsdp_header_M_findA_at(M1, "fingerprint", 0) : tsk_null; + is_dtls_fingerprint_changed[index] = (A0 && A1 && !tsk_striequals(A0->value, A1->value)); + } + // network info (session-level) + if (!is_ro_network_info_changed[index]) { + C0 = (const tsdp_header_C_t*)tsdp_message_get_headerAt(self->sdp.ro, tsdp_htype_C, index); + C1 = (const tsdp_header_C_t*)tsdp_message_get_headerAt(sdp, tsdp_htype_C, index); + // Connection informations must be both "null" or "not-null" + if (!(is_ro_network_info_changed[index] = !((C0 && C1) || (!C0 && !C1)))) { + if (C0) { + is_ro_network_info_changed[index] = (!tsk_strequals(C1->addr, C0->addr) || !tsk_strequals(C1->nettype, C0->nettype) || !tsk_strequals(C1->addrtype, C0->addrtype)); + } + } + } // TODO: sdes crypo lines (session-level) - // media type - media_types[index] = tmedia_type_from_sdp_headerM(M1); - - // ice (session-level) - if (tmedia_defaults_get_ice_enabled()) { - is_ice_enabled[index] |= tsdp_message_is_ice_enabled(sdp, index); - is_ice_restart[index] |= tsdp_message_is_ice_restart(sdp, index); - } - - // ro_codecs - if (had_lo_sdp && is_ro_codecs_changed[index]) { - // we already have a local sdp (means codecs already negotiated) and the remote is changing the codecs - tmedia_session_t* ms = (tmedia_session_t*)tsk_object_ref(TSK_OBJECT(_tmedia_session_mgr_find_session_at_index(self->sessions, index))); - if (ms && ms->prepared) { - tmedia_codec_t* encoder = tsk_null; - tmedia_param_t* param_get_codec = tmedia_param_create_get_session(media_types[index], tmedia_pvt_pobject, "codec-encoder", &encoder); - if (param_get_codec) { - if (tmedia_session_get(ms, param_get_codec) == 0) { - if (encoder) { - const char* codec_name = encoder->plugin ? encoder->plugin->name : "unknown"; - const char* neg_format = encoder->neg_format ? encoder->neg_format : encoder->format; - if (tsdp_header_M_have_fmt(M1, neg_format) == tsk_true) { // new ro has the old encoder? - // same rtpmap would produce same encoder -> change nothing - char* old_rtpmap = tsdp_header_M_get_rtpmap(M0, neg_format); - char* new_rtpmap = tsdp_header_M_get_rtpmap(M1, neg_format); - is_local_encoder_still_ok[index] = tsk_striequals(old_rtpmap, new_rtpmap); - TSK_FREE(old_rtpmap); TSK_FREE(new_rtpmap); - if (is_local_encoder_still_ok[index]) { - // TODO: add more checks - } - } - TSK_OBJECT_SAFE_FREE(encoder); // destroying "param_get_codec" won't release "encoder" - } - } - TSK_OBJECT_SAFE_FREE(param_get_codec); - } - } - TSK_OBJECT_SAFE_FREE(ms); - } - - ++index; - } - // the index was used against current ro which means at this step there is no longer any media at "index" - // to be sure that new and old sdp have same number of media lines, we just check that there is no media in the new sdp at "index" - is_ro_media_lines_changed |= (tsdp_message_get_headerAt(sdp, tsdp_htype_M, index) != tsk_null); - } - - /* - * Make sure that the provisional response is an preview of the final as explained rfc6337 section 3.1.1. We only check the most important part (IP addr and ports). - * It's useless to check codecs or any other caps (SRTP, ICE, DTLS...) as our offer haven't changed - * If the preview is different than the final response than this is a bug on the remote party: - * As per rfc6337 section 3.1.1.: - * - [RFC3261] requires all SDP in the responses to the INVITE request to be identical. - * - After the UAS has sent the answer in a reliable provisional - response to the INVITE, the UAS should not include any SDPs in - subsequent responses to the INVITE. - * If the remote party is buggy, then the newly generated local SDP will be sent in the ACK request - */ - is_ro_provisional_final_matching &= !(is_ro_media_lines_changed || __flags_sum((const tsk_bool_t*)&is_ro_network_info_changed, m_lines_count)); - - /* This is to hack fake forking from ZTE => ignore SDP with loopback address in order to not start/stop the camera several - * times which leads to more than ten seconds for session connection. - * Gets the global connection line: "c=" - * Loopback address is only invalid on - */ - if ((C = (const tsdp_header_C_t*)tsdp_message_get_header(sdp, tsdp_htype_C)) && C->addr){ - tsk_bool_t _is_ro_loopback_address = (tsk_striequals("IP4", C->addrtype) && tsk_striequals("127.0.0.1", C->addr)) - || (tsk_striequals("IP6", C->addrtype) && tsk_striequals("::1", C->addr)); - for (index = 0; index < m_lines_count; ++index) { - is_ro_loopback_address[index] = _is_ro_loopback_address; - } - } - - /* Check if media type has changed or not - * For initial offer we don't need to check anything - */ - if (self->sdp.lo) { - if ((is_media_type_changed = (new_mediatype != self->type)) || is_new_mediatype_striped) { - tsk_bool_t force = !!is_new_mediatype_striped; - tmedia_session_mgr_set_media_type_2(self, new_mediatype, force); - TSK_DEBUG_INFO("media type has changed"); - } - } - - TSK_DEBUG_INFO( - "m_lines_count=%u,\n" - "is_dtls_fingerprint_changed=%u,\n" + // media type + media_types[index] = tmedia_type_from_sdp_headerM(M1); + + // ice (session-level) + if (tmedia_defaults_get_ice_enabled()) { + is_ice_enabled[index] |= tsdp_message_is_ice_enabled(sdp, index); + is_ice_restart[index] |= tsdp_message_is_ice_restart(sdp, index); + } + + // ro_codecs + if (had_lo_sdp && is_ro_codecs_changed[index]) { + // we already have a local sdp (means codecs already negotiated) and the remote is changing the codecs + tmedia_session_t* ms = (tmedia_session_t*)tsk_object_ref(TSK_OBJECT(_tmedia_session_mgr_find_session_at_index(self->sessions, index))); + if (ms && ms->prepared) { + tmedia_codec_t* encoder = tsk_null; + tmedia_param_t* param_get_codec = tmedia_param_create_get_session(media_types[index], tmedia_pvt_pobject, "codec-encoder", &encoder); + if (param_get_codec) { + if (tmedia_session_get(ms, param_get_codec) == 0) { + if (encoder) { + const char* codec_name = encoder->plugin ? encoder->plugin->name : "unknown"; + const char* neg_format = encoder->neg_format ? encoder->neg_format : encoder->format; + if (tsdp_header_M_have_fmt(M1, neg_format) == tsk_true) { // new ro has the old encoder? + // same rtpmap would produce same encoder -> change nothing + char* old_rtpmap = tsdp_header_M_get_rtpmap(M0, neg_format); + char* new_rtpmap = tsdp_header_M_get_rtpmap(M1, neg_format); + is_local_encoder_still_ok[index] = tsk_striequals(old_rtpmap, new_rtpmap); + TSK_FREE(old_rtpmap); + TSK_FREE(new_rtpmap); + if (is_local_encoder_still_ok[index]) { + // TODO: add more checks + } + } + TSK_OBJECT_SAFE_FREE(encoder); // destroying "param_get_codec" won't release "encoder" + } + } + TSK_OBJECT_SAFE_FREE(param_get_codec); + } + } + TSK_OBJECT_SAFE_FREE(ms); + } + + ++index; + } + // the index was used against current ro which means at this step there is no longer any media at "index" + // to be sure that new and old sdp have same number of media lines, we just check that there is no media in the new sdp at "index" + is_ro_media_lines_changed |= (tsdp_message_get_headerAt(sdp, tsdp_htype_M, index) != tsk_null); + } + + /* + * Make sure that the provisional response is an preview of the final as explained rfc6337 section 3.1.1. We only check the most important part (IP addr and ports). + * It's useless to check codecs or any other caps (SRTP, ICE, DTLS...) as our offer haven't changed + * If the preview is different than the final response than this is a bug on the remote party: + * As per rfc6337 section 3.1.1.: + * - [RFC3261] requires all SDP in the responses to the INVITE request to be identical. + * - After the UAS has sent the answer in a reliable provisional + response to the INVITE, the UAS should not include any SDPs in + subsequent responses to the INVITE. + * If the remote party is buggy, then the newly generated local SDP will be sent in the ACK request + */ + is_ro_provisional_final_matching &= !(is_ro_media_lines_changed || __flags_sum((const tsk_bool_t*)&is_ro_network_info_changed, m_lines_count)); + + /* This is to hack fake forking from ZTE => ignore SDP with loopback address in order to not start/stop the camera several + * times which leads to more than ten seconds for session connection. + * Gets the global connection line: "c=" + * Loopback address is only invalid on + */ + if ((C = (const tsdp_header_C_t*)tsdp_message_get_header(sdp, tsdp_htype_C)) && C->addr) { + tsk_bool_t _is_ro_loopback_address = (tsk_striequals("IP4", C->addrtype) && tsk_striequals("127.0.0.1", C->addr)) + || (tsk_striequals("IP6", C->addrtype) && tsk_striequals("::1", C->addr)); + for (index = 0; index < m_lines_count; ++index) { + is_ro_loopback_address[index] = _is_ro_loopback_address; + } + } + + /* Check if media type has changed or not + * For initial offer we don't need to check anything + */ + if (self->sdp.lo) { + if ((is_media_type_changed = (new_mediatype != self->type)) || is_new_mediatype_striped) { + tsk_bool_t force = !!is_new_mediatype_striped; + tmedia_session_mgr_set_media_type_2(self, new_mediatype, force); + TSK_DEBUG_INFO("media type has changed"); + } + } + + TSK_DEBUG_INFO( + "m_lines_count=%u,\n" + "is_dtls_fingerprint_changed=%u,\n" "is_sdes_crypto_changed=%u,\n" - "is_ice_enabled=%u,\n" - "is_ice_restart=%u,\n" - "is_ro_hold_resume_changed=%u,\n" - "is_ro_provisional_final_matching=%d,\n" - "is_ro_media_lines_changed=%d,\n" - "is_ro_network_info_changed=%u,\n" - "is_ro_loopback_address=%u,\n" - "is_media_type_changed=%d,\n" - "is_ro_codecs_changed=%u\n" - "is_local_encoder_still_ok=%u\n", - (unsigned)m_lines_count, - (unsigned)__flags_sum((const tsk_bool_t*)&is_dtls_fingerprint_changed, m_lines_count), + "is_ice_enabled=%u,\n" + "is_ice_restart=%u,\n" + "is_ro_hold_resume_changed=%u,\n" + "is_ro_provisional_final_matching=%d,\n" + "is_ro_media_lines_changed=%d,\n" + "is_ro_network_info_changed=%u,\n" + "is_ro_loopback_address=%u,\n" + "is_media_type_changed=%d,\n" + "is_ro_codecs_changed=%u\n" + "is_local_encoder_still_ok=%u\n", + (unsigned)m_lines_count, + (unsigned)__flags_sum((const tsk_bool_t*)&is_dtls_fingerprint_changed, m_lines_count), (unsigned)__flags_sum((const tsk_bool_t*)&is_sdes_crypto_changed, m_lines_count), - (unsigned)__flags_sum((const tsk_bool_t*)&is_ice_enabled, m_lines_count), - (unsigned)__flags_sum((const tsk_bool_t*)&is_ice_restart, m_lines_count), - (unsigned)__flags_sum((const tsk_bool_t*)&is_ro_hold_resume_changed, m_lines_count), - is_ro_provisional_final_matching, - is_ro_media_lines_changed, - (unsigned)__flags_sum((const tsk_bool_t*)&is_ro_network_info_changed, m_lines_count), - (unsigned)__flags_sum((const tsk_bool_t*)&is_ro_loopback_address, m_lines_count), - is_media_type_changed, - (unsigned)__flags_sum((const tsk_bool_t*)&is_ro_codecs_changed, m_lines_count), - (unsigned)__flags_sum((const tsk_bool_t*)&is_local_encoder_still_ok, m_lines_count) - ); - - /* - * It's almost impossible to update the codecs, the connection information etc etc while the sessions are running - * For example, if the video producer is already started then, you probably cannot update its configuration - * without stoping it and restarting it again with the right config. Same for RTP Network config (ip addresses, NAT, ports, IP version, ...) - * - * "is_loopback_address" is used as a guard to avoid reconf for loopback address used for example by ZTE for fake forking. In all case - * loopback address won't work on embedded devices such as iOS and Android. - * - */ - if (self->started) { - for (index = 0; index < m_lines_count; ++index) { - if (/* && (!is_ro_loopback_address[index]) && */ ((is_ro_codecs_changed[index] && !is_local_encoder_still_ok[index]) || is_ro_network_info_changed[index] || is_dtls_fingerprint_changed[index] || is_sdes_crypto_changed[index])) { - TSK_DEBUG_INFO("Stop media index %d to reconf", (int)index); - stopped_to_reconf[index] = tsk_true; - tmedia_session_mgr_set(self, - TMEDIA_SESSION_SET_INT32(media_types[index], "stop-to-reconf", stopped_to_reconf[index]), - TMEDIA_SESSION_SET_NULL()); - if ((ret = _tmedia_session_mgr_stop(self, (int)index))){ - TSK_DEBUG_ERROR("Failed to stop session manager"); - goto bail; - } - } - } - } - - /* update remote offer */ - TSK_OBJECT_SAFE_FREE(self->sdp.ro); - self->sdp.ro = tsk_object_ref((void*)sdp); - - /* - if the session is running this means no session update is required unless some important changes - - this check must be done after the "ro" update - - "is_ro_hold_resume_changed" do not restart the session but updates the SDP - */ - if (self->started && !(__flags_sum((const tsk_bool_t*)&is_ro_hold_resume_changed, m_lines_count) || __flags_sum((const tsk_bool_t*)&stopped_to_reconf, m_lines_count) || is_ro_media_lines_changed)) { - goto end_of_sessions_update; - } - - /* prepare the session manager if not already done (create all sessions with their codecs) - * if network-initiated: think about tmedia_type_from_sdp() before creating the manager */ - if (_tmedia_session_mgr_load_sessions(self)){ - TSK_DEBUG_ERROR("Failed to prepare the session manager"); - ret = -3; - goto bail; - } - // update media line counts - index = m_lines_count; // save old "m_lines_count" before loading sessions - m_lines_count = tsk_list_count_all(self->sessions); // "m_lines_count" after loading sessions - TSK_DEBUG_INFO("new m_lines_count = %u -> %u", (unsigned)index, (unsigned)m_lines_count); - if (index != m_lines_count) { // start new sessions - for (; index < m_lines_count; ++index) { // for(session in new_sessions) - stopped_to_reconf[index] = self->started; // start new session if mgr started - if (tmedia_defaults_get_ice_enabled()) { - is_ice_enabled[index] |= tsdp_message_is_ice_enabled(sdp, index); - is_ice_restart[index] |= tsdp_message_is_ice_restart(sdp, index); - } - } - } - - /* get global connection line (common to all sessions) - * Each session should override this info if it has a different one in its "m=" line - * /!\ "remote-ip" is deprecated by "remote-sdp-message" and pending before complete remove - */ - if (C && C->addr){ - tmedia_session_mgr_set(self, - TMEDIA_SESSION_SET_STR(self->type, "remote-ip", C->addr), - TMEDIA_SESSION_SET_NULL()); - } - - /* pass complete remote sdp to the sessions to allow them to use session-level attributes - */ - tmedia_session_mgr_set(self, - TMEDIA_SESSION_SET_POBJECT(self->type, "remote-sdp-message", self->sdp.ro), - TMEDIA_SESSION_SET_NULL()); - - /* foreach "m=" line in the remote offer create/prepare a session (requires the session to be stopped)*/ - index = 0; - tsk_list_pushback_list(list_tmp_sessions, self->sessions); - tsk_list_clear_items(self->sessions); - while ((M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(sdp, tsdp_htype_M, index++))) { - found = tsk_false; + (unsigned)__flags_sum((const tsk_bool_t*)&is_ice_enabled, m_lines_count), + (unsigned)__flags_sum((const tsk_bool_t*)&is_ice_restart, m_lines_count), + (unsigned)__flags_sum((const tsk_bool_t*)&is_ro_hold_resume_changed, m_lines_count), + is_ro_provisional_final_matching, + is_ro_media_lines_changed, + (unsigned)__flags_sum((const tsk_bool_t*)&is_ro_network_info_changed, m_lines_count), + (unsigned)__flags_sum((const tsk_bool_t*)&is_ro_loopback_address, m_lines_count), + is_media_type_changed, + (unsigned)__flags_sum((const tsk_bool_t*)&is_ro_codecs_changed, m_lines_count), + (unsigned)__flags_sum((const tsk_bool_t*)&is_local_encoder_still_ok, m_lines_count) + ); + + /* + * It's almost impossible to update the codecs, the connection information etc etc while the sessions are running + * For example, if the video producer is already started then, you probably cannot update its configuration + * without stoping it and restarting it again with the right config. Same for RTP Network config (ip addresses, NAT, ports, IP version, ...) + * + * "is_loopback_address" is used as a guard to avoid reconf for loopback address used for example by ZTE for fake forking. In all case + * loopback address won't work on embedded devices such as iOS and Android. + * + */ + if (self->started) { + for (index = 0; index < m_lines_count; ++index) { + if (/* && (!is_ro_loopback_address[index]) && */ ((is_ro_codecs_changed[index] && !is_local_encoder_still_ok[index]) || is_ro_network_info_changed[index] || is_dtls_fingerprint_changed[index] || is_sdes_crypto_changed[index])) { + TSK_DEBUG_INFO("Stop media index %d to reconf", (int)index); + stopped_to_reconf[index] = tsk_true; + tmedia_session_mgr_set(self, + TMEDIA_SESSION_SET_INT32(media_types[index], "stop-to-reconf", stopped_to_reconf[index]), + TMEDIA_SESSION_SET_NULL()); + if ((ret = _tmedia_session_mgr_stop(self, (int)index))) { + TSK_DEBUG_ERROR("Failed to stop session manager"); + goto bail; + } + } + } + } + + /* update remote offer */ + TSK_OBJECT_SAFE_FREE(self->sdp.ro); + self->sdp.ro = tsk_object_ref((void*)sdp); + + /* - if the session is running this means no session update is required unless some important changes + - this check must be done after the "ro" update + - "is_ro_hold_resume_changed" do not restart the session but updates the SDP + */ + if (self->started && !(__flags_sum((const tsk_bool_t*)&is_ro_hold_resume_changed, m_lines_count) || __flags_sum((const tsk_bool_t*)&stopped_to_reconf, m_lines_count) || is_ro_media_lines_changed)) { + goto end_of_sessions_update; + } + + /* prepare the session manager if not already done (create all sessions with their codecs) + * if network-initiated: think about tmedia_type_from_sdp() before creating the manager */ + if (_tmedia_session_mgr_load_sessions(self)) { + TSK_DEBUG_ERROR("Failed to prepare the session manager"); + ret = -3; + goto bail; + } + // update media line counts + index = m_lines_count; // save old "m_lines_count" before loading sessions + m_lines_count = tsk_list_count_all(self->sessions); // "m_lines_count" after loading sessions + TSK_DEBUG_INFO("new m_lines_count = %u -> %u", (unsigned)index, (unsigned)m_lines_count); + if (index != m_lines_count) { // start new sessions + for (; index < m_lines_count; ++index) { // for(session in new_sessions) + stopped_to_reconf[index] = self->started; // start new session if mgr started + if (tmedia_defaults_get_ice_enabled()) { + is_ice_enabled[index] |= tsdp_message_is_ice_enabled(sdp, index); + is_ice_restart[index] |= tsdp_message_is_ice_restart(sdp, index); + } + } + } + + /* get global connection line (common to all sessions) + * Each session should override this info if it has a different one in its "m=" line + * /!\ "remote-ip" is deprecated by "remote-sdp-message" and pending before complete remove + */ + if (C && C->addr) { + tmedia_session_mgr_set(self, + TMEDIA_SESSION_SET_STR(self->type, "remote-ip", C->addr), + TMEDIA_SESSION_SET_NULL()); + } + + /* pass complete remote sdp to the sessions to allow them to use session-level attributes + */ + tmedia_session_mgr_set(self, + TMEDIA_SESSION_SET_POBJECT(self->type, "remote-sdp-message", self->sdp.ro), + TMEDIA_SESSION_SET_NULL()); + + /* foreach "m=" line in the remote offer create/prepare a session (requires the session to be stopped)*/ + index = 0; + tsk_list_pushback_list(list_tmp_sessions, self->sessions); + tsk_list_clear_items(self->sessions); + while ((M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(sdp, tsdp_htype_M, index++))) { + found = tsk_false; #if 1 - // rfc3264 - 8 Modifying the Session - // if the previous SDP had N "m=" lines, the new SDP MUST have at least N "m=" lines - // Deleted media streams from a previous SDP MUST NOT be removed in a new SDP - if (had_lo_sdp) { - ms = _tmedia_session_mgr_find_session_at_index(list_tmp_sessions, (index - 1 - active_sessions_count)); - } - else { - // Initial Offer - tmedia_type_t M_media_type = tmedia_type_from_sdp_headerM(M); - ms = tsk_list_find_object_by_pred(list_tmp_sessions, __pred_find_session_by_type, &M_media_type); - } + // rfc3264 - 8 Modifying the Session + // if the previous SDP had N "m=" lines, the new SDP MUST have at least N "m=" lines + // Deleted media streams from a previous SDP MUST NOT be removed in a new SDP + if (had_lo_sdp) { + ms = _tmedia_session_mgr_find_session_at_index(list_tmp_sessions, (index - 1 - active_sessions_count)); + } + else { + // Initial Offer + tmedia_type_t M_media_type = tmedia_type_from_sdp_headerM(M); + ms = tsk_list_find_object_by_pred(list_tmp_sessions, __pred_find_session_by_type, &M_media_type); + } #else - if (ro_type & tmedia_ro_type_answer) { - // Answer -> match by index - ms = _tmedia_session_mgr_find_session_at_index(list_tmp_sessions, (index - 1 - active_sessions_count)); - } - else { - // Request -> match by type - tmedia_type_t M_media_type = tmedia_type_from_sdp_headerM(M); - ms = tsk_list_find_object_by_pred(list_tmp_sessions, __pred_find_session_by_type, &M_media_type); - } + if (ro_type & tmedia_ro_type_answer) { + // Answer -> match by index + ms = _tmedia_session_mgr_find_session_at_index(list_tmp_sessions, (index - 1 - active_sessions_count)); + } + else { + // Request -> match by type + tmedia_type_t M_media_type = tmedia_type_from_sdp_headerM(M); + ms = tsk_list_find_object_by_pred(list_tmp_sessions, __pred_find_session_by_type, &M_media_type); + } #endif - if (ms && (tsk_striequals(tmedia_session_get_media(ms), M->media))) { - /* prepare the media session */ - if (!ms->prepared && M->port && (_tmedia_session_prepare(TMEDIA_SESSION(ms)))){ - TSK_DEBUG_ERROR("Failed to prepare session"); /* should never happen */ - goto bail; - } - /* set remote ro at session-level unless media is disabled (port=0) */ - if (M->port == 0 || (ret = _tmedia_session_set_ro(TMEDIA_SESSION(ms), M)) == 0) { - tmedia_session_t* _ms = tsk_object_ref(TSK_OBJECT(ms)); - found = tsk_true; - ++active_sessions_count; - tsk_list_push_back_data(self->sessions, (void**)&_ms); // add at the same index - tsk_list_remove_item_by_data(list_tmp_sessions, ms); // make sure not to use the session more than once - } - else { - TSK_DEBUG_WARN("_tmedia_session_set_ro() failed"); - ret = 0; // add ghost for this session. Do not goto bail because set_ro() is allowed to fail (e.g. codec mismatch). - } - /* set QoS type (only if we are not the offerer) */ - if (/*!self->offerer ==> we suppose that the remote party respected our demand &&*/ qos_type == tmedia_qos_stype_none) { - tmedia_qos_tline_t* tline = tmedia_qos_tline_from_sdp(M); - if (tline) { - qos_type = tline->type; - TSK_OBJECT_SAFE_FREE(tline); - } - } - } - - if (!found /*&& (self->sdp.lo == tsk_null)*/){ - /* Session not supported and we are not the initial offerer ==> add ghost session */ - /* - An offered stream MAY be rejected in the answer, for any reason. If - a stream is rejected, the offerer and answerer MUST NOT generate - media (or RTCP packets) for that stream. To reject an offered - stream, the port number in the corresponding stream in the answer - MUST be set to zero. Any media formats listed are ignored. AT LEAST - ONE MUST BE PRESENT, AS SPECIFIED BY SDP. - */ - tmedia_session_ghost_t* ghost; - if ((ghost = (tmedia_session_ghost_t*)tmedia_session_create(tmedia_ghost))){ - tsk_strupdate(&ghost->media, M->media); /* copy media */ - tsk_strupdate(&ghost->proto, M->proto); /* copy proto */ - if (!TSK_LIST_IS_EMPTY(M->FMTs)){ - tsk_strupdate(&ghost->first_format, ((const tsdp_fmt_t*)TSK_LIST_FIRST_DATA(M->FMTs))->value); /* copy format */ - } - tsk_list_push_back_data(self->sessions, (void**)&ghost); - } - else { - TSK_DEBUG_ERROR("Failed to create ghost session"); - continue; - } - } - } + if (ms && (tsk_striequals(tmedia_session_get_media(ms), M->media))) { + /* prepare the media session */ + if (!ms->prepared && M->port && (_tmedia_session_prepare(TMEDIA_SESSION(ms)))) { + TSK_DEBUG_ERROR("Failed to prepare session"); /* should never happen */ + goto bail; + } + /* set remote ro at session-level unless media is disabled (port=0) */ + if (M->port == 0 || (ret = _tmedia_session_set_ro(TMEDIA_SESSION(ms), M)) == 0) { + tmedia_session_t* _ms = tsk_object_ref(TSK_OBJECT(ms)); + found = tsk_true; + ++active_sessions_count; + tsk_list_push_back_data(self->sessions, (void**)&_ms); // add at the same index + tsk_list_remove_item_by_data(list_tmp_sessions, ms); // make sure not to use the session more than once + } + else { + TSK_DEBUG_WARN("_tmedia_session_set_ro() failed"); + ret = 0; // add ghost for this session. Do not goto bail because set_ro() is allowed to fail (e.g. codec mismatch). + } + /* set QoS type (only if we are not the offerer) */ + if (/*!self->offerer ==> we suppose that the remote party respected our demand &&*/ qos_type == tmedia_qos_stype_none) { + tmedia_qos_tline_t* tline = tmedia_qos_tline_from_sdp(M); + if (tline) { + qos_type = tline->type; + TSK_OBJECT_SAFE_FREE(tline); + } + } + } + + if (!found /*&& (self->sdp.lo == tsk_null)*/) { + /* Session not supported and we are not the initial offerer ==> add ghost session */ + /* + An offered stream MAY be rejected in the answer, for any reason. If + a stream is rejected, the offerer and answerer MUST NOT generate + media (or RTCP packets) for that stream. To reject an offered + stream, the port number in the corresponding stream in the answer + MUST be set to zero. Any media formats listed are ignored. AT LEAST + ONE MUST BE PRESENT, AS SPECIFIED BY SDP. + */ + tmedia_session_ghost_t* ghost; + if ((ghost = (tmedia_session_ghost_t*)tmedia_session_create(tmedia_ghost))) { + tsk_strupdate(&ghost->media, M->media); /* copy media */ + tsk_strupdate(&ghost->proto, M->proto); /* copy proto */ + if (!TSK_LIST_IS_EMPTY(M->FMTs)) { + tsk_strupdate(&ghost->first_format, ((const tsdp_fmt_t*)TSK_LIST_FIRST_DATA(M->FMTs))->value); /* copy format */ + } + tsk_list_push_back_data(self->sessions, (void**)&ghost); + } + else { + TSK_DEBUG_ERROR("Failed to create ghost session"); + continue; + } + } + } end_of_sessions_update: - /* update QoS type */ - if (!self->offerer && (qos_type != tmedia_qos_stype_none)){ - self->qos.type = qos_type; - } - - /* signal that ro has changed (will be used to update lo) unless there was no ro_sdp */ - self->ro_changed = (had_ro_sdp && (__flags_sum((const tsk_bool_t*)&is_ro_hold_resume_changed, m_lines_count) || __flags_sum((const tsk_bool_t*)&is_ro_network_info_changed, m_lines_count) || is_ro_media_lines_changed || __flags_sum((const tsk_bool_t*)&is_ro_codecs_changed, m_lines_count) /*|| is_media_type_changed || is_new_mediatype_striped*/)); - - /* update "provisional" info */ - self->ro_provisional = ((ro_type & tmedia_ro_type_provisional) == tmedia_ro_type_provisional); - - if (self->ro_changed) { - /* update local offer before restarting the session manager otherwise neg_codecs won't match if new codecs - have been added or removed. No need to load sessions again. */ - (_tmedia_session_mgr_get_lo(self, kSkipSessionLoadTrue, kForceUpdateLOFalse)); - } - /* manager was started and we stopped it in order to reconfigure it (codecs, network, ....) - * When ICE is active ("is_ice_active" = yes), the media session will be explicitly restarted when conncheck succeed or fail. - */ - for (index = 0; index < m_lines_count; ++index) { - if (stopped_to_reconf[index] && !is_ice_enabled[index]) { - if ((ret = _tmedia_session_mgr_start(self, (int)index))) { - TSK_DEBUG_ERROR("Failed to re-start session at index = %d", (int)index); - goto bail; - } - } - } - - // will send [488 Not Acceptable] / [BYE] if no active session - ret = (self->ro_changed && active_sessions_count <= 0) ? -0xFF : 0; + /* update QoS type */ + if (!self->offerer && (qos_type != tmedia_qos_stype_none)) { + self->qos.type = qos_type; + } + + /* signal that ro has changed (will be used to update lo) unless there was no ro_sdp */ + self->ro_changed = (had_ro_sdp && (__flags_sum((const tsk_bool_t*)&is_ro_hold_resume_changed, m_lines_count) || __flags_sum((const tsk_bool_t*)&is_ro_network_info_changed, m_lines_count) || is_ro_media_lines_changed || __flags_sum((const tsk_bool_t*)&is_ro_codecs_changed, m_lines_count) /*|| is_media_type_changed || is_new_mediatype_striped*/)); + + /* update "provisional" info */ + self->ro_provisional = ((ro_type & tmedia_ro_type_provisional) == tmedia_ro_type_provisional); + + if (self->ro_changed) { + /* update local offer before restarting the session manager otherwise neg_codecs won't match if new codecs + have been added or removed. No need to load sessions again. */ + (_tmedia_session_mgr_get_lo(self, kSkipSessionLoadTrue, kForceUpdateLOFalse)); + } + /* manager was started and we stopped it in order to reconfigure it (codecs, network, ....) + * When ICE is active ("is_ice_active" = yes), the media session will be explicitly restarted when conncheck succeed or fail. + */ + for (index = 0; index < m_lines_count; ++index) { + if (stopped_to_reconf[index] && !is_ice_enabled[index]) { + if ((ret = _tmedia_session_mgr_start(self, (int)index))) { + TSK_DEBUG_ERROR("Failed to re-start session at index = %d", (int)index); + goto bail; + } + } + } + + // will send [488 Not Acceptable] / [BYE] if no active session + ret = (self->ro_changed && active_sessions_count <= 0) ? -0xFF : 0; bail: - TSK_OBJECT_SAFE_FREE(list_tmp_sessions); + TSK_OBJECT_SAFE_FREE(list_tmp_sessions); - tsk_safeobj_unlock(self); - tsk_list_unlock(self->sessions); - return ret; + tsk_safeobj_unlock(self); + tsk_list_unlock(self->sessions); + return ret; } const tsdp_message_t* tmedia_session_mgr_get_ro(tmedia_session_mgr_t* self) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } - return self->sdp.ro; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + return self->sdp.ro; } tsk_bool_t tmedia_session_mgr_is_new_ro(tmedia_session_mgr_t* self, const tsdp_message_t* sdp) { - tsk_bool_t is_new = tsk_true; - const tsdp_header_O_t* O; + tsk_bool_t is_new = tsk_true; + const tsdp_header_O_t* O; - if (!self || !sdp){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self || !sdp) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - tsk_safeobj_lock(self); + tsk_safeobj_lock(self); - if ((O = (const tsdp_header_O_t*)tsdp_message_get_header(sdp, tsdp_htype_O))) { - is_new = (self->sdp.ro_ver != (int32_t)O->sess_version); - } - else { - TSK_DEBUG_ERROR("o= line is missing"); - } + if ((O = (const tsdp_header_O_t*)tsdp_message_get_header(sdp, tsdp_htype_O))) { + is_new = (self->sdp.ro_ver != (int32_t)O->sess_version); + } + else { + TSK_DEBUG_ERROR("o= line is missing"); + } - tsk_safeobj_unlock(self); - return is_new; + tsk_safeobj_unlock(self); + return is_new; } /**@ingroup tmedia_session_group @@ -1581,23 +1649,23 @@ tsk_bool_t tmedia_session_mgr_is_new_ro(tmedia_session_mgr_t* self, const tsdp_m */ int tmedia_session_mgr_hold(tmedia_session_mgr_t* self, tmedia_type_t type) { - const tsk_list_item_t* item; + const tsk_list_item_t* item; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - tsk_list_foreach(item, self->sessions){ - tmedia_session_t* session = TMEDIA_SESSION(item->data); - if (((session->type & type) == session->type) && session->M.lo){ - if (tsdp_header_M_hold(session->M.lo, tsk_true) == 0){ - self->state_changed = tsk_true; - session->lo_held = tsk_true; - } - } - } - return 0; + tsk_list_foreach(item, self->sessions) { + tmedia_session_t* session = TMEDIA_SESSION(item->data); + if (((session->type & type) == session->type) && session->M.lo) { + if (tsdp_header_M_hold(session->M.lo, tsk_true) == 0) { + self->state_changed = tsk_true; + session->lo_held = tsk_true; + } + } + } + return 0; } /**@ingroup tmedia_session_group @@ -1608,33 +1676,33 @@ int tmedia_session_mgr_hold(tmedia_session_mgr_t* self, tmedia_type_t type) */ tsk_bool_t tmedia_session_mgr_is_held(tmedia_session_mgr_t* self, tmedia_type_t type, tsk_bool_t local) { - const tsk_list_item_t* item; - tsk_bool_t have_these_sessions = tsk_false; - - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_false; - } - - tsk_list_foreach(item, self->sessions){ - tmedia_session_t* session = TMEDIA_SESSION(item->data); - if ((session->type & type) == session->type){ - if (local && session->M.lo){ - have_these_sessions = tsk_true; - if (!tsdp_header_M_is_held(session->M.lo, tsk_true)){ - return tsk_false; - } - } - else if (!local && session->M.ro){ - have_these_sessions = tsk_true; - if (!tsdp_header_M_is_held(session->M.ro, tsk_false)){ - return tsk_false; - } - } - } - } - /* none is held */ - return have_these_sessions ? tsk_true : tsk_false; + const tsk_list_item_t* item; + tsk_bool_t have_these_sessions = tsk_false; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_false; + } + + tsk_list_foreach(item, self->sessions) { + tmedia_session_t* session = TMEDIA_SESSION(item->data); + if ((session->type & type) == session->type) { + if (local && session->M.lo) { + have_these_sessions = tsk_true; + if (!tsdp_header_M_is_held(session->M.lo, tsk_true)) { + return tsk_false; + } + } + else if (!local && session->M.ro) { + have_these_sessions = tsk_true; + if (!tsdp_header_M_is_held(session->M.ro, tsk_false)) { + return tsk_false; + } + } + } + } + /* none is held */ + return have_these_sessions ? tsk_true : tsk_false; } /**@ingroup tmedia_session_group @@ -1647,28 +1715,28 @@ tsk_bool_t tmedia_session_mgr_is_held(tmedia_session_mgr_t* self, tmedia_type_t */ int tmedia_session_mgr_resume(tmedia_session_mgr_t* self, tmedia_type_t type, tsk_bool_t local) { - const tsk_list_item_t* item; - int ret = 0; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - tsk_list_foreach(item, self->sessions){ - tmedia_session_t* session = TMEDIA_SESSION(item->data); - if (((session->type & type) == session->type) && session->M.lo){ - if ((ret = tsdp_header_M_resume(session->M.lo, local)) == 0){ - self->state_changed = tsk_true; - if (local){ - session->lo_held = tsk_false; - } - else{ - session->ro_held = tsk_false; - } - } - } - } - return ret; + const tsk_list_item_t* item; + int ret = 0; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + tsk_list_foreach(item, self->sessions) { + tmedia_session_t* session = TMEDIA_SESSION(item->data); + if (((session->type & type) == session->type) && session->M.lo) { + if ((ret = tsdp_header_M_resume(session->M.lo, local)) == 0) { + self->state_changed = tsk_true; + if (local) { + session->lo_held = tsk_false; + } + else { + session->ro_held = tsk_false; + } + } + } + } + return ret; } /**@ingroup tmedia_session_group @@ -1680,47 +1748,47 @@ int tmedia_session_mgr_resume(tmedia_session_mgr_t* self, tmedia_type_t type, ts */ int tmedia_session_mgr_add_media(tmedia_session_mgr_t* self, tmedia_type_t type) { - tsk_size_t i = 0; - tmedia_session_t* session; - const tmedia_session_plugin_def_t* plugin; - - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - /* for each registered plugin match with the supplied type */ - while ((i < TMED_SESSION_MAX_PLUGINS) && (plugin = __tmedia_session_plugins[i++])){ - if ((plugin->type & type) == plugin->type){ - /* check whether we already support this media */ - if ((session = (tmedia_session_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &plugin->type)) && session->plugin){ - if (session->prepared){ - TSK_DEBUG_WARN("[%s] already active", plugin->media); - } - else{ - /* exist but unprepared(port=0) */ - _tmedia_session_prepare(session); - if (self->started && session->plugin->start){ - session->plugin->start(session); - } - self->state_changed = tsk_true; - } - } - else{ - /* session not supported */ - self->state_changed = tsk_true; - if ((session = tmedia_session_create(plugin->type))){ - if (self->started && session->plugin->start){ - session->plugin->start(session); - } - tsk_list_push_back_data(self->sessions, (void**)(&session)); - self->state_changed = tsk_true; - } - } - } - } - - return self->state_changed ? 0 : -2; + tsk_size_t i = 0; + tmedia_session_t* session; + const tmedia_session_plugin_def_t* plugin; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + /* for each registered plugin match with the supplied type */ + while ((i < TMED_SESSION_MAX_PLUGINS) && (plugin = __tmedia_session_plugins[i++])) { + if ((plugin->type & type) == plugin->type) { + /* check whether we already support this media */ + if ((session = (tmedia_session_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &plugin->type)) && session->plugin) { + if (session->prepared) { + TSK_DEBUG_WARN("[%s] already active", plugin->media); + } + else { + /* exist but unprepared(port=0) */ + _tmedia_session_prepare(session); + if (self->started && session->plugin->start) { + session->plugin->start(session); + } + self->state_changed = tsk_true; + } + } + else { + /* session not supported */ + self->state_changed = tsk_true; + if ((session = tmedia_session_create(plugin->type))) { + if (self->started && session->plugin->start) { + session->plugin->start(session); + } + tsk_list_push_back_data(self->sessions, (void**)(&session)); + self->state_changed = tsk_true; + } + } + } + } + + return self->state_changed ? 0 : -2; } /**@ingroup tmedia_session_group @@ -1731,22 +1799,22 @@ int tmedia_session_mgr_add_media(tmedia_session_mgr_t* self, tmedia_type_t type) */ int tmedia_session_mgr_remove_media(tmedia_session_mgr_t* self, tmedia_type_t type) { - const tsk_list_item_t* item; + const tsk_list_item_t* item; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - tsk_list_foreach(item, self->sessions){ - tmedia_session_t* session = TMEDIA_SESSION(item->data); - if (((session->type & type) == session->type) && session->plugin->stop){ - if (!session->plugin->stop(session)){ - self->state_changed = tsk_true; - } - } - } - return 0; + tsk_list_foreach(item, self->sessions) { + tmedia_session_t* session = TMEDIA_SESSION(item->data); + if (((session->type & type) == session->type) && session->plugin->stop) { + if (!session->plugin->stop(session)) { + self->state_changed = tsk_true; + } + } + } + return 0; } /**@ingroup tmedia_session_group @@ -1758,14 +1826,14 @@ int tmedia_session_mgr_remove_media(tmedia_session_mgr_t* self, tmedia_type_t ty */ int tmedia_session_mgr_set_qos(tmedia_session_mgr_t* self, tmedia_qos_stype_t qos_type, tmedia_qos_strength_t qos_strength) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - self->qos.type = qos_type; - self->qos.strength = qos_strength; - return 0; + self->qos.type = qos_type; + self->qos.strength = qos_strength; + return 0; } /**@ingroup tmedia_session_group @@ -1775,20 +1843,20 @@ int tmedia_session_mgr_set_qos(tmedia_session_mgr_t* self, tmedia_qos_stype_t qo */ tsk_bool_t tmedia_session_mgr_canresume(tmedia_session_mgr_t* self) { - const tsk_list_item_t* item; + const tsk_list_item_t* item; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_true; - } + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_true; + } - tsk_list_foreach(item, self->sessions){ - tmedia_session_t* session = TMEDIA_SESSION(item->data); - if (session && session->qos && !tmedia_qos_tline_canresume(session->qos)){ - return tsk_false; - } - } - return tsk_true; + tsk_list_foreach(item, self->sessions) { + tmedia_session_t* session = TMEDIA_SESSION(item->data); + if (session && session->qos && !tmedia_qos_tline_canresume(session->qos)) { + return tsk_false; + } + } + return tsk_true; } @@ -1797,673 +1865,673 @@ tsk_bool_t tmedia_session_mgr_canresume(tmedia_session_mgr_t* self) */ tsk_bool_t tmedia_session_mgr_has_active_session(tmedia_session_mgr_t* self) { - const tsk_list_item_t* item; + const tsk_list_item_t* item; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_false; - } + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_false; + } - tsk_list_foreach(item, self->sessions){ - tmedia_session_t* session = TMEDIA_SESSION(item->data); - if (session && session->M.lo && session->M.lo->port){ - return tsk_true; - } - } - return tsk_false; + tsk_list_foreach(item, self->sessions) { + tmedia_session_t* session = TMEDIA_SESSION(item->data); + if (session && session->M.lo && session->M.lo->port) { + return tsk_true; + } + } + return tsk_false; } int tmedia_session_mgr_send_dtmf(tmedia_session_mgr_t* self, uint8_t event) { - tmedia_session_audio_t* session; - static const tmedia_type_t audio_type = tmedia_audio; - int ret = -3; + tmedia_session_audio_t* session; + static const tmedia_type_t audio_type = tmedia_audio; + int ret = -3; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - session = (tmedia_session_audio_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &audio_type); - if (session){ - session = tsk_object_ref(session); - ret = tmedia_session_audio_send_dtmf(TMEDIA_SESSION_AUDIO(session), event); - TSK_OBJECT_SAFE_FREE(session); - } - else{ - TSK_DEBUG_ERROR("No audio session associated to this manager"); - } + session = (tmedia_session_audio_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &audio_type); + if (session) { + session = tsk_object_ref(session); + ret = tmedia_session_audio_send_dtmf(TMEDIA_SESSION_AUDIO(session), event); + TSK_OBJECT_SAFE_FREE(session); + } + else { + TSK_DEBUG_ERROR("No audio session associated to this manager"); + } - return ret; + return ret; } int tmedia_session_mgr_set_t140_ondata_cbfn(tmedia_session_mgr_t* self, const void* context, tmedia_session_t140_ondata_cb_f func) { - tmedia_session_t* session; - int ret = -1; - if ((session = tmedia_session_mgr_find(self, tmedia_t140))){ - ret = tmedia_session_t140_set_ondata_cbfn(session, context, func); - TSK_OBJECT_SAFE_FREE(session); - } - return ret; + tmedia_session_t* session; + int ret = -1; + if ((session = tmedia_session_mgr_find(self, tmedia_t140))) { + ret = tmedia_session_t140_set_ondata_cbfn(session, context, func); + TSK_OBJECT_SAFE_FREE(session); + } + return ret; } int tmedia_session_mgr_send_t140_data(tmedia_session_mgr_t* self, enum tmedia_t140_data_type_e data_type, const void* data_ptr, unsigned data_size) { - tmedia_session_t* session; - int ret = -1; - if ((session = tmedia_session_mgr_find(self, tmedia_t140))){ - ret = tmedia_session_t140_send_data(session, data_type, data_ptr, data_size); - TSK_OBJECT_SAFE_FREE(session); - } - return ret; + tmedia_session_t* session; + int ret = -1; + if ((session = tmedia_session_mgr_find(self, tmedia_t140))) { + ret = tmedia_session_t140_send_data(session, data_type, data_ptr, data_size); + TSK_OBJECT_SAFE_FREE(session); + } + return ret; } int tmedia_session_mgr_set_onrtcp_cbfn(tmedia_session_mgr_t* self, tmedia_type_t media_type, const void* context, tmedia_session_rtcp_onevent_cb_f fun) { - tmedia_session_t* session; - tsk_list_item_t *item; + tmedia_session_t* session; + tsk_list_item_t *item; - if (!self){ - TSK_DEBUG_ERROR("Invlid parameter"); - return -1; - } + if (!self) { + TSK_DEBUG_ERROR("Invlid parameter"); + return -1; + } - tsk_list_lock(self->sessions); - tsk_list_foreach(item, self->sessions){ - if (!(session = item->data) || !(session->type & media_type)){ - continue; - } - tmedia_session_set_onrtcp_cbfn(session, context, fun); - } - tsk_list_unlock(self->sessions); + tsk_list_lock(self->sessions); + tsk_list_foreach(item, self->sessions) { + if (!(session = item->data) || !(session->type & media_type)) { + continue; + } + tmedia_session_set_onrtcp_cbfn(session, context, fun); + } + tsk_list_unlock(self->sessions); - return 0; + return 0; } int tmedia_session_mgr_send_rtcp_event(tmedia_session_mgr_t* self, tmedia_type_t media_type, tmedia_rtcp_event_type_t event_type, uint32_t ssrc_media) { - tmedia_session_t* session; - tsk_list_item_t *item; + tmedia_session_t* session; + tsk_list_item_t *item; - if (!self){ - TSK_DEBUG_ERROR("Invlid parameter"); - return -1; - } + if (!self) { + TSK_DEBUG_ERROR("Invlid parameter"); + return -1; + } - tsk_list_lock(self->sessions); - tsk_list_foreach(item, self->sessions){ - if (!(session = item->data) || !(session->type & media_type)){ - continue; - } - tmedia_session_send_rtcp_event(session, event_type, ssrc_media); - } - tsk_list_unlock(self->sessions); + tsk_list_lock(self->sessions); + tsk_list_foreach(item, self->sessions) { + if (!(session = item->data) || !(session->type & media_type)) { + continue; + } + tmedia_session_send_rtcp_event(session, event_type, ssrc_media); + } + tsk_list_unlock(self->sessions); - return 0; + return 0; } int tmedia_session_mgr_recv_rtcp_event(tmedia_session_mgr_t* self, tmedia_type_t media_type, tmedia_rtcp_event_type_t event_type, uint32_t ssrc_media) { - static const uint64_t __fake_session_id = 0; - return _tmedia_session_mgr_recv_rtcp_event(self, media_type, event_type, ssrc_media, __fake_session_id); + static const uint64_t __fake_session_id = 0; + return _tmedia_session_mgr_recv_rtcp_event(self, media_type, event_type, ssrc_media, __fake_session_id); } int tmedia_session_mgr_recv_rtcp_event_2(tmedia_session_mgr_t* self, tmedia_rtcp_event_type_t event_type, uint64_t session_id) { - static const uint32_t __fake_ssrc_media = 0; - static const tmedia_type_t __fake_media_type = tmedia_none; - return _tmedia_session_mgr_recv_rtcp_event(self, __fake_media_type, event_type, __fake_ssrc_media, session_id); + static const uint32_t __fake_ssrc_media = 0; + static const tmedia_type_t __fake_media_type = tmedia_none; + return _tmedia_session_mgr_recv_rtcp_event(self, __fake_media_type, event_type, __fake_ssrc_media, session_id); } int tmedia_session_mgr_send_file(tmedia_session_mgr_t* self, const char* path, ...) { - tmedia_session_msrp_t* session; - tmedia_type_t msrp_type = tmedia_msrp; - int ret = -3; + tmedia_session_msrp_t* session; + tmedia_type_t msrp_type = tmedia_msrp; + int ret = -3; - if (!self || !path){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self || !path) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - session = (tmedia_session_msrp_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &msrp_type); - if (session && session->send_file){ - va_list ap; - va_start(ap, path); - session = tsk_object_ref(session); - ret = session->send_file(TMEDIA_SESSION_MSRP(session), path, &ap); - TSK_OBJECT_SAFE_FREE(session); - va_end(ap); - } - else{ - TSK_DEBUG_ERROR("No MSRP session associated to this manager or session does not support file transfer"); - } + session = (tmedia_session_msrp_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &msrp_type); + if (session && session->send_file) { + va_list ap; + va_start(ap, path); + session = tsk_object_ref(session); + ret = session->send_file(TMEDIA_SESSION_MSRP(session), path, &ap); + TSK_OBJECT_SAFE_FREE(session); + va_end(ap); + } + else { + TSK_DEBUG_ERROR("No MSRP session associated to this manager or session does not support file transfer"); + } - return ret; + return ret; } int tmedia_session_mgr_send_message(tmedia_session_mgr_t* self, const void* data, tsk_size_t size, const tmedia_params_L_t *params) { - tmedia_session_msrp_t* session; - tmedia_type_t msrp_type = tmedia_msrp; - int ret = -3; + tmedia_session_msrp_t* session; + tmedia_type_t msrp_type = tmedia_msrp; + int ret = -3; - if (!self || !size || !data){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self || !size || !data) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - session = (tmedia_session_msrp_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &msrp_type); - if (session && session->send_message){ - session = tsk_object_ref(session); - ret = session->send_message(TMEDIA_SESSION_MSRP(session), data, size, params); - TSK_OBJECT_SAFE_FREE(session); - } - else{ - TSK_DEBUG_ERROR("No MSRP session associated to this manager or session does not support file transfer"); - } + session = (tmedia_session_msrp_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &msrp_type); + if (session && session->send_message) { + session = tsk_object_ref(session); + ret = session->send_message(TMEDIA_SESSION_MSRP(session), data, size, params); + TSK_OBJECT_SAFE_FREE(session); + } + else { + TSK_DEBUG_ERROR("No MSRP session associated to this manager or session does not support file transfer"); + } - return ret; + return ret; } int tmedia_session_mgr_set_msrp_cb(tmedia_session_mgr_t* self, const void* callback_data, tmedia_session_msrp_cb_f func) { - tmedia_session_msrp_t* session; - tmedia_type_t msrp_type = tmedia_msrp; + tmedia_session_msrp_t* session; + tmedia_type_t msrp_type = tmedia_msrp; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - if ((session = (tmedia_session_msrp_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &msrp_type))){ - session->callback.data = callback_data; - session->callback.func = func; - return 0; - } - else{ - TSK_DEBUG_ERROR("No MSRP session associated to this manager or session does not support file transfer"); - return -2; - } + if ((session = (tmedia_session_msrp_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &msrp_type))) { + session->callback.data = callback_data; + session->callback.func = func; + return 0; + } + else { + TSK_DEBUG_ERROR("No MSRP session associated to this manager or session does not support file transfer"); + return -2; + } } int tmedia_session_mgr_set_onerror_cbfn(tmedia_session_mgr_t* self, const void* usrdata, tmedia_session_onerror_cb_f fun) { - tmedia_session_t* session; - tsk_list_item_t *item; + tmedia_session_t* session; + tsk_list_item_t *item; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } // save for sessions created later - self->onerror_cb.fun = fun; - self->onerror_cb.usrdata = usrdata; + self->onerror_cb.fun = fun; + self->onerror_cb.usrdata = usrdata; - tsk_list_lock(self->sessions); - tsk_list_foreach(item, self->sessions){ - if (!(session = item->data)){ - continue; - } - tmedia_session_set_onerror_cbfn(session, usrdata, fun); - } - tsk_list_unlock(self->sessions); + tsk_list_lock(self->sessions); + tsk_list_foreach(item, self->sessions) { + if (!(session = item->data)) { + continue; + } + tmedia_session_set_onerror_cbfn(session, usrdata, fun); + } + tsk_list_unlock(self->sessions); - return 0; + return 0; } int tmedia_session_mgr_set_rfc5168_cbfn(tmedia_session_mgr_t* self, const void* usrdata, tmedia_session_rfc5168_cb_f fun) { - tmedia_session_t* session; - tsk_list_item_t *item; + tmedia_session_t* session; + tsk_list_item_t *item; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } // save for functions created later - self->rfc5168_cb.fun = fun; - self->rfc5168_cb.usrdata = usrdata; + self->rfc5168_cb.fun = fun; + self->rfc5168_cb.usrdata = usrdata; - tsk_list_lock(self->sessions); - tsk_list_foreach(item, self->sessions){ - if (!(session = item->data)){ - continue; - } - tmedia_session_set_rfc5168_cbfn(session, usrdata, fun); - } - tsk_list_unlock(self->sessions); + tsk_list_lock(self->sessions); + tsk_list_foreach(item, self->sessions) { + if (!(session = item->data)) { + continue; + } + tmedia_session_set_rfc5168_cbfn(session, usrdata, fun); + } + tsk_list_unlock(self->sessions); - return 0; + return 0; } int tmedia_session_mgr_set_bfcp_cbfn(tmedia_session_mgr_t* self, const void* usrdata, tmedia_session_bfcp_cb_f fun) { - tmedia_session_t* session; - tsk_list_item_t *item; + tmedia_session_t* session; + tsk_list_item_t *item; - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - tsk_list_lock(self->sessions); - tsk_list_foreach(item, self->sessions) { - if (!(session = item->data)) { - continue; - } - tmedia_session_set_bfcp_cbfn(session, usrdata, fun); - } - tsk_list_unlock(self->sessions); + tsk_list_lock(self->sessions); + tsk_list_foreach(item, self->sessions) { + if (!(session = item->data)) { + continue; + } + tmedia_session_set_bfcp_cbfn(session, usrdata, fun); + } + tsk_list_unlock(self->sessions); - return 0; + return 0; } int tmedia_session_mgr_lo_apply_changes(tmedia_session_mgr_t* self) { - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - _tmedia_session_mgr_get_lo(self, kSkipSessionLoadTrue, kForceUpdateLOTrue); - return 0; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + _tmedia_session_mgr_get_lo(self, kSkipSessionLoadTrue, kForceUpdateLOTrue); + return 0; } static int _tmedia_session_mgr_recv_rtcp_event(tmedia_session_mgr_t* self, tmedia_type_t media_type, tmedia_rtcp_event_type_t event_type, uint32_t ssrc_media, uint64_t session_id) { - tmedia_session_t* session; - tsk_list_item_t *item; + tmedia_session_t* session; + tsk_list_item_t *item; - if (!self){ - TSK_DEBUG_ERROR("Invlid parameter"); - return -1; - } + if (!self) { + TSK_DEBUG_ERROR("Invlid parameter"); + return -1; + } - tsk_list_lock(self->sessions); - tsk_list_foreach(item, self->sessions) { - if (!(session = item->data) || !((session->type & media_type) || (session->id == session_id))) { - continue; - } - tmedia_session_recv_rtcp_event(session, event_type, ssrc_media); - } - tsk_list_unlock(self->sessions); + tsk_list_lock(self->sessions); + tsk_list_foreach(item, self->sessions) { + if (!(session = item->data) || !((session->type & media_type) || (session->id == session_id))) { + continue; + } + tmedia_session_recv_rtcp_event(session, event_type, ssrc_media); + } + tsk_list_unlock(self->sessions); - return 0; + return 0; } /** internal function used to load sessions */ static int _tmedia_session_mgr_load_sessions(tmedia_session_mgr_t* self) { - tsk_size_t i = 0; - tmedia_session_t* session; - const tmedia_session_plugin_def_t* plugin; + tsk_size_t i = 0; + tmedia_session_t* session; + const tmedia_session_plugin_def_t* plugin; - tsk_list_lock(self->sessions); + tsk_list_lock(self->sessions); #define has_media(media_type) (tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &(media_type))) - if (TSK_LIST_IS_EMPTY(self->sessions) || self->mediaType_changed) { - //static tmedia_type_t __ghost_media_type = tmedia_ghost; - //static tmedia_type_t __none_media_type = tmedia_none; - // Remove ghost sessions. Up to the - //while (tsk_list_remove_item_by_pred(self->sessions, __pred_find_session_by_type, &__ghost_media_type)) ; - //while (tsk_list_remove_item_by_pred(self->sessions, __pred_find_session_by_type, &__none_media_type)) ; - /* for each registered plugin create a session instance */ - while ((i < TMED_SESSION_MAX_PLUGINS) && (plugin = __tmedia_session_plugins[i++])) { - if ((plugin->type & self->type) == plugin->type) { /* media_type *IS* enabled */ - if (has_media(plugin->type)) { - // we already have a matching media session -> enable it if not already done - _tmedia_session_mgr_enable_session_with_type(self, plugin->type); - } - else { - // we don't have a matching media session yet - if ((session = tmedia_session_create(plugin->type))) { - /* do not call "tmedia_session_mgr_set()" here to avoid applying parms before the creation of all session */ - - /* set other default values */ - - /* push session */ - tsk_list_push_back_data(self->sessions, (void**)(&session)); - } - } - } - else { /* media_type *IS NOT* enabled */ - if (has_media(plugin->type)) { - // do not remove to make sure media indexes match -> see rfc 3264 section 8 - // tsk_list_remove_item_by_pred(self->sessions, __pred_find_session_by_type, &(plugin->type)); - _tmedia_session_mgr_disable_session_with_type(self, plugin->type); - } - } - } - /* set default values and apply params*/ - tmedia_session_mgr_set(self, - TMEDIA_SESSION_SET_POBJECT(tmedia_audio, "ice-ctx", self->ice.ctx_audio), - TMEDIA_SESSION_SET_POBJECT(tmedia_video, "ice-ctx", self->ice.ctx_video), - TMEDIA_SESSION_SET_POBJECT(tmedia_bfcp_video, "ice-ctx", self->ice.ctx_bfcpvid), - - TMEDIA_SESSION_SET_STR(self->type, "local-ip", self->addr), - TMEDIA_SESSION_SET_STR(self->type, "local-ipver", self->ipv6 ? "ipv6" : "ipv4"), - TMEDIA_SESSION_SET_INT32(self->type, "bandwidth-level", self->bl), - TMEDIA_SESSION_SET_NULL()); + if (TSK_LIST_IS_EMPTY(self->sessions) || self->mediaType_changed) { + //static tmedia_type_t __ghost_media_type = tmedia_ghost; + //static tmedia_type_t __none_media_type = tmedia_none; + // Remove ghost sessions. Up to the + //while (tsk_list_remove_item_by_pred(self->sessions, __pred_find_session_by_type, &__ghost_media_type)) ; + //while (tsk_list_remove_item_by_pred(self->sessions, __pred_find_session_by_type, &__none_media_type)) ; + /* for each registered plugin create a session instance */ + while ((i < TMED_SESSION_MAX_PLUGINS) && (plugin = __tmedia_session_plugins[i++])) { + if ((plugin->type & self->type) == plugin->type) { /* media_type *IS* enabled */ + if (has_media(plugin->type)) { + // we already have a matching media session -> enable it if not already done + _tmedia_session_mgr_enable_session_with_type(self, plugin->type); + } + else { + // we don't have a matching media session yet + if ((session = tmedia_session_create(plugin->type))) { + /* do not call "tmedia_session_mgr_set()" here to avoid applying parms before the creation of all session */ + + /* set other default values */ + + /* push session */ + tsk_list_push_back_data(self->sessions, (void**)(&session)); + } + } + } + else { /* media_type *IS NOT* enabled */ + if (has_media(plugin->type)) { + // do not remove to make sure media indexes match -> see rfc 3264 section 8 + // tsk_list_remove_item_by_pred(self->sessions, __pred_find_session_by_type, &(plugin->type)); + _tmedia_session_mgr_disable_session_with_type(self, plugin->type); + } + } + } + /* set default values and apply params*/ + tmedia_session_mgr_set(self, + TMEDIA_SESSION_SET_POBJECT(tmedia_audio, "ice-ctx", self->ice.ctx_audio), + TMEDIA_SESSION_SET_POBJECT(tmedia_video, "ice-ctx", self->ice.ctx_video), + TMEDIA_SESSION_SET_POBJECT(tmedia_bfcp_video, "ice-ctx", self->ice.ctx_bfcpvid), + + TMEDIA_SESSION_SET_STR(self->type, "local-ip", self->addr), + TMEDIA_SESSION_SET_STR(self->type, "local-ipver", self->ipv6 ? "ipv6" : "ipv4"), + TMEDIA_SESSION_SET_INT32(self->type, "bandwidth-level", self->bl), + TMEDIA_SESSION_SET_NULL()); // set callback functions tmedia_session_mgr_set_onerror_cbfn(self, self->onerror_cb.usrdata, self->onerror_cb.fun); tmedia_session_mgr_set_rfc5168_cbfn(self, self->rfc5168_cb.usrdata, self->rfc5168_cb.fun); - } + } #undef has_media - tsk_list_unlock(self->sessions); - return 0; + tsk_list_unlock(self->sessions); + return 0; } //!\ not thread-safe static const tmedia_session_t* _tmedia_session_mgr_find_session_at_index(const tmedia_sessions_L_t* list, tsk_size_t index) { - const tsk_list_item_t *item; - tsk_size_t u = 0; - tsk_list_foreach(item, list) { - if (u++ == index) { - return (const tmedia_session_t*)item->data; - } - } - return tsk_null; + const tsk_list_item_t *item; + tsk_size_t u = 0; + tsk_list_foreach(item, list) { + if (u++ == index) { + return (const tmedia_session_t*)item->data; + } + } + return tsk_null; } /* internal function */ static int _tmedia_session_mgr_disable_or_enable_session_with_type(tmedia_session_mgr_t* self, tmedia_type_t media_type, tsk_bool_t enable) { - tsk_list_item_t *item; - tmedia_session_t *session; - tsk_list_lock(self->sessions); - - tsk_list_foreach(item, self->sessions) { - if ((session = (tmedia_session_t*)item->data) && session->plugin && session->plugin->type == media_type) { - if (enable) { - if (session->M.lo && !session->M.lo->port) { - TSK_OBJECT_SAFE_FREE(session->M.ro); - TSK_OBJECT_SAFE_FREE(session->M.lo); - session->prepared = tsk_false; - } - } - else { - if (session->plugin->stop) { - session->plugin->stop(session); - } - if (session->M.lo) { - session->M.lo->port = 0; - } - session->prepared = tsk_false; - } - } - } - - tsk_list_unlock(self->sessions); - return 0; + tsk_list_item_t *item; + tmedia_session_t *session; + tsk_list_lock(self->sessions); + + tsk_list_foreach(item, self->sessions) { + if ((session = (tmedia_session_t*)item->data) && session->plugin && session->plugin->type == media_type) { + if (enable) { + if (session->M.lo && !session->M.lo->port) { + TSK_OBJECT_SAFE_FREE(session->M.ro); + TSK_OBJECT_SAFE_FREE(session->M.lo); + session->prepared = tsk_false; + } + } + else { + if (session->plugin->stop) { + session->plugin->stop(session); + } + if (session->M.lo) { + session->M.lo->port = 0; + } + session->prepared = tsk_false; + } + } + } + + tsk_list_unlock(self->sessions); + return 0; } /* internal function */ static int _tmedia_session_mgr_clear_sessions(tmedia_session_mgr_t* self) { - if (self && self->sessions){ - tsk_list_clear_items(self->sessions); - } - return 0; + if (self && self->sessions) { + tsk_list_clear_items(self->sessions); + } + return 0; } /* internal function */ // force_update_lo: means use same sdp version number but update fields static const tsdp_message_t* _tmedia_session_mgr_get_lo(tmedia_session_mgr_t* self, tsk_bool_t skip_session_load, tsk_bool_t force_update_lo) { - const tsk_list_item_t* item; - const tmedia_session_t* ms; - const tsdp_header_M_t* m; - const tsdp_message_t* ret = tsk_null; - uint32_t new_ver_num; - tsk_bool_t inc_ver = tsk_false; - - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } - - tsk_safeobj_lock(self); - - /* prepare the session manager if not already done (create all sessions) */ - if (TSK_LIST_IS_EMPTY(self->sessions)){ - if (_tmedia_session_mgr_load_sessions(self)){ - TSK_DEBUG_ERROR("Failed to prepare the session manager"); - goto bail; - } - } - - /* creates local sdp if not already done or update it's value (because of set_ro())*/ - if ((self->ro_changed || self->state_changed || self->mediaType_changed) && self->sdp.lo) { - // delete current lo - TSK_OBJECT_SAFE_FREE(self->sdp.lo); - if (self->mediaType_changed && !skip_session_load) { - // reload session with new medias and keep the old one - _tmedia_session_mgr_load_sessions(self); - } - self->ro_changed = tsk_false; - self->ro_provisional = tsk_false; - self->state_changed = tsk_false; - self->mediaType_changed = tsk_false; - } - if (force_update_lo && self->sdp.lo) { - const tsdp_header_O_t* O; - - if ((O = (const tsdp_header_O_t*)tsdp_message_get_header(self->sdp.lo, tsdp_htype_O))) { - tsk_list_item_t *item; - tmedia_session_t *session; - - new_ver_num = O->sess_version; - tsk_list_lock(self->sessions); - tsk_list_foreach(item, self->sessions) { - if ((session = (tmedia_session_t*)item->data)) { - TSK_OBJECT_SAFE_FREE(session->M.lo); - } - } - tsk_list_unlock(self->sessions); - TSK_OBJECT_SAFE_FREE(self->sdp.lo); - } - else { - new_ver_num = (self->sdp.lo_ver + 1); - inc_ver = tsk_true; - } - } - else { - new_ver_num = (self->sdp.lo_ver + 1); - inc_ver = tsk_true; - } - - if (self->sdp.lo) { - ret = self->sdp.lo; - goto bail; - } - else { - if ((self->sdp.lo = tsdp_message_create_empty(self->public_addr ? self->public_addr : self->addr, self->ipv6, new_ver_num))) { - /* Set connection "c=" */ - tsdp_message_add_headers(self->sdp.lo, - TSDP_HEADER_C_VA_ARGS("IN", self->ipv6 ? "IP6" : "IP4", self->public_addr ? self->public_addr : self->addr), - tsk_null); - if (inc_ver) { - ++self->sdp.lo_ver; - } - } - else { - TSK_DEBUG_ERROR("Failed to create empty SDP message"); - goto bail; - } - } - - /* pass complete local sdp to the sessions to allow them to use session-level attributes */ - tmedia_session_mgr_set(self, - TMEDIA_SESSION_SET_POBJECT(self->type, "local-sdp-message", self->sdp.lo), - TMEDIA_SESSION_SET_NULL()); - - /* gets each "m=" line from the sessions and add them to the local sdp */ - tsk_list_foreach(item, self->sessions){ - if (!(ms = item->data) || !ms->plugin){ - TSK_DEBUG_ERROR("Invalid session"); - continue; - } - if ((self->type & ms->plugin->type) || ms->plugin->type == tmedia_ghost) { - /* prepare the media session */ - if (!ms->prepared && (_tmedia_session_prepare(TMEDIA_SESSION(ms)))){ - TSK_DEBUG_ERROR("Failed to prepare session"); /* should never happen */ - continue; - } - - /* Add QoS lines to our local media */ - if ((self->qos.type != tmedia_qos_stype_none) && !TMEDIA_SESSION(ms)->qos){ - TMEDIA_SESSION(ms)->qos = tmedia_qos_tline_create(self->qos.type, self->qos.strength); - } - - /* add "m=" line from the session to the local sdp */ - if ((m = tmedia_session_get_lo(TMEDIA_SESSION(ms)))) { - tsdp_message_add_header(self->sdp.lo, TSDP_HEADER(m)); - } - else { - TSK_DEBUG_ERROR("Failed to get m= line for [%s] media", ms->plugin->media); - } - } - else if (ms->M.lo) { - // media not enabled -> add sdp with port zero - tsdp_message_add_header(self->sdp.lo, TSDP_HEADER(ms->M.lo)); - } - } - - self->type = self->sdp.lo ? tmedia_type_from_sdp(self->sdp.lo) : tmedia_none; - ret = self->sdp.lo; + const tsk_list_item_t* item; + const tmedia_session_t* ms; + const tsdp_header_M_t* m; + const tsdp_message_t* ret = tsk_null; + uint32_t new_ver_num; + tsk_bool_t inc_ver = tsk_false; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + tsk_safeobj_lock(self); + + /* prepare the session manager if not already done (create all sessions) */ + if (TSK_LIST_IS_EMPTY(self->sessions)) { + if (_tmedia_session_mgr_load_sessions(self)) { + TSK_DEBUG_ERROR("Failed to prepare the session manager"); + goto bail; + } + } + + /* creates local sdp if not already done or update it's value (because of set_ro())*/ + if ((self->ro_changed || self->state_changed || self->mediaType_changed) && self->sdp.lo) { + // delete current lo + TSK_OBJECT_SAFE_FREE(self->sdp.lo); + if (self->mediaType_changed && !skip_session_load) { + // reload session with new medias and keep the old one + _tmedia_session_mgr_load_sessions(self); + } + self->ro_changed = tsk_false; + self->ro_provisional = tsk_false; + self->state_changed = tsk_false; + self->mediaType_changed = tsk_false; + } + if (force_update_lo && self->sdp.lo) { + const tsdp_header_O_t* O; + + if ((O = (const tsdp_header_O_t*)tsdp_message_get_header(self->sdp.lo, tsdp_htype_O))) { + tsk_list_item_t *item; + tmedia_session_t *session; + + new_ver_num = O->sess_version; + tsk_list_lock(self->sessions); + tsk_list_foreach(item, self->sessions) { + if ((session = (tmedia_session_t*)item->data)) { + TSK_OBJECT_SAFE_FREE(session->M.lo); + } + } + tsk_list_unlock(self->sessions); + TSK_OBJECT_SAFE_FREE(self->sdp.lo); + } + else { + new_ver_num = (self->sdp.lo_ver + 1); + inc_ver = tsk_true; + } + } + else { + new_ver_num = (self->sdp.lo_ver + 1); + inc_ver = tsk_true; + } + + if (self->sdp.lo) { + ret = self->sdp.lo; + goto bail; + } + else { + if ((self->sdp.lo = tsdp_message_create_empty(self->public_addr ? self->public_addr : self->addr, self->ipv6, new_ver_num))) { + /* Set connection "c=" */ + tsdp_message_add_headers(self->sdp.lo, + TSDP_HEADER_C_VA_ARGS("IN", self->ipv6 ? "IP6" : "IP4", self->public_addr ? self->public_addr : self->addr), + tsk_null); + if (inc_ver) { + ++self->sdp.lo_ver; + } + } + else { + TSK_DEBUG_ERROR("Failed to create empty SDP message"); + goto bail; + } + } + + /* pass complete local sdp to the sessions to allow them to use session-level attributes */ + tmedia_session_mgr_set(self, + TMEDIA_SESSION_SET_POBJECT(self->type, "local-sdp-message", self->sdp.lo), + TMEDIA_SESSION_SET_NULL()); + + /* gets each "m=" line from the sessions and add them to the local sdp */ + tsk_list_foreach(item, self->sessions) { + if (!(ms = item->data) || !ms->plugin) { + TSK_DEBUG_ERROR("Invalid session"); + continue; + } + if ((self->type & ms->plugin->type) || ms->plugin->type == tmedia_ghost) { + /* prepare the media session */ + if (!ms->prepared && (_tmedia_session_prepare(TMEDIA_SESSION(ms)))) { + TSK_DEBUG_ERROR("Failed to prepare session"); /* should never happen */ + continue; + } + + /* Add QoS lines to our local media */ + if ((self->qos.type != tmedia_qos_stype_none) && !TMEDIA_SESSION(ms)->qos) { + TMEDIA_SESSION(ms)->qos = tmedia_qos_tline_create(self->qos.type, self->qos.strength); + } + + /* add "m=" line from the session to the local sdp */ + if ((m = tmedia_session_get_lo(TMEDIA_SESSION(ms)))) { + tsdp_message_add_header(self->sdp.lo, TSDP_HEADER(m)); + } + else { + TSK_DEBUG_ERROR("Failed to get m= line for [%s] media", ms->plugin->media); + } + } + else if (ms->M.lo) { + // media not enabled -> add sdp with port zero + tsdp_message_add_header(self->sdp.lo, TSDP_HEADER(ms->M.lo)); + } + } + + self->type = self->sdp.lo ? tmedia_type_from_sdp(self->sdp.lo) : tmedia_none; + ret = self->sdp.lo; bail: - tsk_safeobj_unlock(self); + tsk_safeobj_unlock(self); - return ret; + return ret; } static int _tmedia_session_mgr_start(tmedia_session_mgr_t* self, int session_index) { - int ret = 0, index = 0; - tsk_list_item_t* item; - tmedia_session_t* session; - - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - tsk_safeobj_lock(self); - - tsk_list_foreach(item, self->sessions) { - if (session_index == kSessionIndexAll || index++ == session_index) { - if (!(session = item->data) || !session->plugin || !session->plugin->start) { - TSK_DEBUG_ERROR("Invalid session"); - ret = -2; - goto bail; - } - if (!session->M.lo || !session->M.lo->port) { - continue; - } - if ((ret = session->plugin->start(session))) { - TSK_DEBUG_ERROR("Failed to start %s session", session->plugin->media); - continue; - } - } - } - if (session_index == kSessionIndexAll) { - self->started = tsk_true; - } + int ret = 0, index = 0; + tsk_list_item_t* item; + tmedia_session_t* session; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + tsk_safeobj_lock(self); + + tsk_list_foreach(item, self->sessions) { + if (session_index == kSessionIndexAll || index++ == session_index) { + if (!(session = item->data) || !session->plugin || !session->plugin->start) { + TSK_DEBUG_ERROR("Invalid session"); + ret = -2; + goto bail; + } + if (!session->M.lo || !session->M.lo->port) { + continue; + } + if ((ret = session->plugin->start(session))) { + TSK_DEBUG_ERROR("Failed to start %s session", session->plugin->media); + continue; + } + } + } + if (session_index == kSessionIndexAll) { + self->started = tsk_true; + } bail: - tsk_safeobj_unlock(self); - return ret; + tsk_safeobj_unlock(self); + return ret; } static int _tmedia_session_mgr_stop(tmedia_session_mgr_t* self, int session_index) { - int ret = 0, index = 0; - tsk_list_item_t* item; - tmedia_session_t* session; - - TSK_DEBUG_INFO("tmedia_session_mgr_stop()"); - - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - tsk_safeobj_lock(self); - - tsk_list_foreach(item, self->sessions) { - if (session_index == kSessionIndexAll || index++ == session_index) { - if (!(session = item->data) || !session->plugin || !session->plugin->stop) { - TSK_DEBUG_ERROR("Invalid session"); - ret = -2; - goto bail; - } - if ((ret = session->plugin->stop(session))) { - TSK_DEBUG_ERROR("Failed to stop session"); - continue; - } - session->prepared = tsk_false; - } - } - if (session_index == kSessionIndexAll) { - self->started = tsk_false; - } + int ret = 0, index = 0; + tsk_list_item_t* item; + tmedia_session_t* session; + + TSK_DEBUG_INFO("tmedia_session_mgr_stop()"); + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + tsk_safeobj_lock(self); + + tsk_list_foreach(item, self->sessions) { + if (session_index == kSessionIndexAll || index++ == session_index) { + if (!(session = item->data) || !session->plugin || !session->plugin->stop) { + TSK_DEBUG_ERROR("Invalid session"); + ret = -2; + goto bail; + } + if ((ret = session->plugin->stop(session))) { + TSK_DEBUG_ERROR("Failed to stop session"); + continue; + } + session->prepared = tsk_false; + } + } + if (session_index == kSessionIndexAll) { + self->started = tsk_false; + } bail: - tsk_safeobj_unlock(self); - return ret; + tsk_safeobj_unlock(self); + return ret; } /* internal function */ static int _tmedia_session_mgr_apply_params(tmedia_session_mgr_t* self) { - tsk_list_item_t *it1, *it2; - tmedia_param_t* param; - tmedia_session_t* session; + tsk_list_item_t *it1, *it2; + tmedia_param_t* param; + tmedia_session_t* session; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } - /* If no parameters ==> do nothing (not error) */ - if (TSK_LIST_IS_EMPTY(self->params)){ - return 0; - } + /* If no parameters ==> do nothing (not error) */ + if (TSK_LIST_IS_EMPTY(self->params)) { + return 0; + } - tsk_list_lock(self->params); + tsk_list_lock(self->params); - tsk_list_foreach(it1, self->params){ - if (!(param = it1->data)){ - continue; - } + tsk_list_foreach(it1, self->params) { + if (!(param = it1->data)) { + continue; + } - /* For us */ - if (param->plugin_type == tmedia_ppt_manager){ - continue; - } + /* For us */ + if (param->plugin_type == tmedia_ppt_manager) { + continue; + } - /* For the session (or consumer or producer or codec) */ - tsk_list_foreach(it2, self->sessions){ - if (!(session = it2->data) || !session->plugin){ - continue; - } - if (session->plugin->set && (session->type & param->media_type) == session->type){ - session->plugin->set(session, param); - } - } - } + /* For the session (or consumer or producer or codec) */ + tsk_list_foreach(it2, self->sessions) { + if (!(session = it2->data) || !session->plugin) { + continue; + } + if (session->plugin->set && (session->type & param->media_type) == session->type) { + session->plugin->set(session, param); + } + } + } - /* Clean up params */ - tsk_list_clear_items(self->params); + /* Clean up params */ + tsk_list_clear_items(self->params); - tsk_list_unlock(self->params); + tsk_list_unlock(self->params); - return 0; + return 0; } //================================================================================================= @@ -2471,54 +2539,53 @@ static int _tmedia_session_mgr_apply_params(tmedia_session_mgr_t* self) // static tsk_object_t* tmedia_session_mgr_ctor(tsk_object_t * self, va_list * app) { - tmedia_session_mgr_t *mgr = self; - if (mgr){ - mgr->sessions = tsk_list_create(); + tmedia_session_mgr_t *mgr = self; + if (mgr) { + mgr->sessions = tsk_list_create(); - mgr->sdp.lo_ver = TSDP_HEADER_O_SESS_VERSION_DEFAULT; - mgr->sdp.ro_ver = -1; + mgr->sdp.lo_ver = TSDP_HEADER_O_SESS_VERSION_DEFAULT; + mgr->sdp.ro_ver = -1; - mgr->qos.type = tmedia_qos_stype_none; - mgr->qos.strength = tmedia_qos_strength_optional; - mgr->bl = tmedia_defaults_get_bl(); + mgr->qos.type = tmedia_qos_stype_none; + mgr->qos.strength = tmedia_qos_strength_optional; + mgr->bl = tmedia_defaults_get_bl(); - tsk_safeobj_init(mgr); - } - return self; + tsk_safeobj_init(mgr); + } + return self; } static tsk_object_t* tmedia_session_mgr_dtor(tsk_object_t * self) { - tmedia_session_mgr_t *mgr = self; - if (mgr){ - TSK_OBJECT_SAFE_FREE(mgr->sessions); + tmedia_session_mgr_t *mgr = self; + if (mgr) { + TSK_OBJECT_SAFE_FREE(mgr->sessions); - TSK_OBJECT_SAFE_FREE(mgr->sdp.lo); - TSK_OBJECT_SAFE_FREE(mgr->sdp.ro); + TSK_OBJECT_SAFE_FREE(mgr->sdp.lo); + TSK_OBJECT_SAFE_FREE(mgr->sdp.ro); - TSK_OBJECT_SAFE_FREE(mgr->params); + TSK_OBJECT_SAFE_FREE(mgr->params); - TSK_OBJECT_SAFE_FREE(mgr->natt_ctx); - TSK_FREE(mgr->public_addr); + TSK_OBJECT_SAFE_FREE(mgr->natt_ctx); + TSK_FREE(mgr->public_addr); - TSK_OBJECT_SAFE_FREE(mgr->ice.ctx_audio); - TSK_OBJECT_SAFE_FREE(mgr->ice.ctx_video); - TSK_OBJECT_SAFE_FREE(mgr->ice.ctx_bfcpvid); + TSK_OBJECT_SAFE_FREE(mgr->ice.ctx_audio); + TSK_OBJECT_SAFE_FREE(mgr->ice.ctx_video); + TSK_OBJECT_SAFE_FREE(mgr->ice.ctx_bfcpvid); - TSK_FREE(mgr->addr); + TSK_FREE(mgr->addr); - tsk_safeobj_deinit(mgr); - } + tsk_safeobj_deinit(mgr); + } - return self; + return self; } -static const tsk_object_def_t tmedia_session_mgr_def_s = -{ - sizeof(tmedia_session_mgr_t), - tmedia_session_mgr_ctor, - tmedia_session_mgr_dtor, - tsk_null, +static const tsk_object_def_t tmedia_session_mgr_def_s = { + sizeof(tmedia_session_mgr_t), + tmedia_session_mgr_ctor, + tmedia_session_mgr_dtor, + tsk_null, }; const tsk_object_def_t *tmedia_session_mgr_def_t = &tmedia_session_mgr_def_s; |