Make media change detection stronger and smarter

This commit is contained in:
bossiel 2015-06-08 22:14:20 +00:00
parent 7ffe7eba0b
commit 493815f88e
15 changed files with 2110 additions and 1668 deletions

View File

@ -342,27 +342,37 @@ static int tdav_session_audio_get(tmedia_session_t* self, tmedia_param_t* param)
return -1;
}
// try with the base class to see if this option is supported or not
if (tdav_session_av_get(TDAV_SESSION_AV(self), param) == tsk_true){
return 0;
}
// the codec information is held by the session even if the user is authorized to request it for the consumer/producer
if (tsk_striequals("codec", param->key) && param->value_type == tmedia_pvt_pobject){
if (param->plugin_type == tmedia_ppt_consumer){
TSK_DEBUG_ERROR("Not implemented");
return -4;
}
else if (param->plugin_type == tmedia_ppt_producer){
const tmedia_codec_t* codec;
if (!(codec = TDAV_SESSION_AUDIO(self)->encoder.codec)){
codec = tdav_session_av_get_best_neg_codec((const tdav_session_av_t*)self);
else {
// the codec information is held by the session even if the user is authorized to request it for the consumer/producer
if (param->value_type == tmedia_pvt_pobject){
if (param->plugin_type == tmedia_ppt_consumer){
TSK_DEBUG_ERROR("Not implemented");
return -4;
}
else if (param->plugin_type == tmedia_ppt_producer){
if (tsk_striequals("codec", param->key)) {
const tmedia_codec_t* codec;
if (!(codec = TDAV_SESSION_AUDIO(self)->encoder.codec)){
codec = tdav_session_av_get_best_neg_codec((const tdav_session_av_t*)self); // up to the caller to release the object
}
*((tsk_object_t**)param->value) = tsk_object_ref(TSK_OBJECT(codec));
return 0;
}
}
else if (param->plugin_type == tmedia_ppt_session) {
if (tsk_striequals(param->key, "codec-encoder")) {
*((tsk_object_t**)param->value) = tsk_object_ref(TDAV_SESSION_AUDIO(self)->encoder.codec); // up to the caller to release the object
return 0;
}
}
*((tsk_object_t**)param->value) = tsk_object_ref(TSK_OBJECT(codec));
return 0;
}
}
TSK_DEBUG_ERROR("(%s) not expected param key for plugin_type=%d", param->key, param->plugin_type);
TSK_DEBUG_WARN("This session doesn't support get(%s)", param->key);
return -2;
}
@ -398,6 +408,11 @@ static int tdav_session_audio_start(tmedia_session_t* self)
audio = (tdav_session_audio_t*)self;
base = (tdav_session_av_t*)self;
if (audio->is_started) {
TSK_DEBUG_INFO("Audio session already started");
return 0;
}
if (!(codec = tdav_session_av_get_best_neg_codec(base))){
TSK_DEBUG_ERROR("No codec matched");
return -2;

View File

@ -71,6 +71,11 @@ static const tsk_bool_t __have_libsrtp = tsk_false;
#define TDAV_FIXME_MEDIA_LEVEL_DTLS_ATT 0
#endif /* TDAV_FIXME_MEDIA_LEVEL_DTLS_ATT */
// rfc5763 - The endpoint MUST NOT use the connection attribute defined in [RFC4145].
#if !defined(TDAV_DTLS_CONNECTION_ATT)
# define TDAV_DTLS_CONNECTION_ATT 0
#endif
static void* TSK_STDCALL _tdav_session_av_error_async_thread(void* usrdata);
static int _tdav_session_av_raise_error_async(struct tdav_session_av_s* self, tsk_bool_t is_fatal, const char* reason);
#if HAVE_SRTP
@ -401,16 +406,22 @@ tsk_bool_t tdav_session_av_get(tdav_session_av_t* self, tmedia_param_t* param)
return tsk_false;
}
// try with the base class to see if this option is supported or not
if(tmedia_session_get(TMEDIA_SESSION(self), param)){
return tsk_true;
}
if(param->plugin_type == tmedia_ppt_session){
if(param->value_type == tmedia_pvt_int32){
if(tsk_striequals(param->key, "srtp-enabled")){
if (param->plugin_type == tmedia_ppt_session){
if (param->value_type == tmedia_pvt_int32) {
if (tsk_striequals(param->key, "codecs-negotiated")) { // negotiated codecs
tmedia_codecs_L_t* neg_codecs = tsk_object_ref(TMEDIA_SESSION(self)->neg_codecs);
if (neg_codecs) {
const tsk_list_item_t* item;
tsk_list_foreach(item, neg_codecs) {
((int32_t*)param->value)[0] |= TMEDIA_CODEC(item->data)->id;
}
TSK_OBJECT_SAFE_FREE(neg_codecs);
}
return tsk_true;
}
else if (tsk_striequals(param->key, "srtp-enabled")) {
#if HAVE_SRTP
if(self->rtp_manager){
if (self->rtp_manager) {
((int8_t*)param->value)[0] = self->use_srtp ? 1 : 0;
return tsk_true;
}
@ -421,8 +432,8 @@ tsk_bool_t tdav_session_av_get(tdav_session_av_t* self, tmedia_param_t* param)
#endif /* HAVE_SRTP */
}
}
else if (param->value_type == tmedia_pvt_pobject){
if (tsk_striequals(param->key, "producer")){
else if (param->value_type == tmedia_pvt_pobject) {
if (tsk_striequals(param->key, "producer")) {
*((tsk_object_t**)param->value) = tsk_object_ref(self->producer); // up to the caller to release the object
return tsk_true;
}
@ -475,47 +486,48 @@ int tdav_session_av_prepare(tdav_session_av_t* self)
#endif
/* set local port */
if(!self->rtp_manager){
if (!self->rtp_manager){
self->rtp_manager = self->ice_ctx ? trtp_manager_create_2(self->ice_ctx, self->srtp_type, self->srtp_mode)
: trtp_manager_create(self->use_rtcp, self->local_ip, self->use_ipv6, self->srtp_type, self->srtp_mode);
if(self->rtp_manager){
if((ret = trtp_manager_set_port_range(self->rtp_manager, tmedia_defaults_get_rtp_port_range_start(), tmedia_defaults_get_rtp_port_range_stop()))){
return ret;
}
}
if (self->rtp_manager) {
if((ret = trtp_manager_set_port_range(self->rtp_manager, tmedia_defaults_get_rtp_port_range_start(), tmedia_defaults_get_rtp_port_range_stop()))){
return ret;
}
#if HAVE_SRTP
if(tsk_strnullORempty(TMEDIA_SESSION(self)->dtls.file_pbk)){
// DTLS-SRTP requires certificates
if(self->srtp_type & tmedia_srtp_type_dtls){
TSK_DEBUG_WARN("DTLS-SRTP requested but no SSL certificates provided, disabling this option :(");
if(!(self->srtp_type &= ~tmedia_srtp_type_dtls)){
// only DTLS-SRTP was enabled
self->srtp_mode = tmedia_srtp_mode_none;
self->use_srtp = tsk_false;
// update rtpmanager
ret = trtp_manager_set_srtp_type_local(self->rtp_manager, self->srtp_type, self->srtp_mode);
}
if(tsk_strnullORempty(TMEDIA_SESSION(self)->dtls.file_pbk)){
// DTLS-SRTP requires certificates
if(self->srtp_type & tmedia_srtp_type_dtls){
TSK_DEBUG_WARN("DTLS-SRTP requested but no SSL certificates provided, disabling this option :(");
if(!(self->srtp_type &= ~tmedia_srtp_type_dtls)){
// only DTLS-SRTP was enabled
self->srtp_mode = tmedia_srtp_mode_none;
self->use_srtp = tsk_false;
// update rtpmanager
ret = trtp_manager_set_srtp_type_local(self->rtp_manager, self->srtp_type, self->srtp_mode);
}
}
if((self->srtp_type & tmedia_srtp_type_dtls) && (self->srtp_mode == tmedia_srtp_mode_optional || self->srtp_mode == tmedia_srtp_mode_mandatory)){
if((ret = trtp_manager_set_dtls_certs(self->rtp_manager, TMEDIA_SESSION(self)->dtls.file_ca, TMEDIA_SESSION(self)->dtls.file_pbk, TMEDIA_SESSION(self)->dtls.file_pvk, TMEDIA_SESSION(self)->dtls.verify))){
return ret;
}
}
#endif /* HAVE_SRTP */
if((ret = trtp_manager_prepare(self->rtp_manager))){
return ret;
}
if(self->natt_ctx){
if((ret = trtp_manager_set_natt_ctx(self->rtp_manager, self->natt_ctx))){
return ret;
}
}
if(self->rtp_ssrc){
self->rtp_manager->rtp.ssrc.local = self->rtp_ssrc;
}
}
if ((self->srtp_type & tmedia_srtp_type_dtls) && (self->srtp_mode == tmedia_srtp_mode_optional || self->srtp_mode == tmedia_srtp_mode_mandatory)){
if((ret = trtp_manager_set_dtls_certs(self->rtp_manager, TMEDIA_SESSION(self)->dtls.file_ca, TMEDIA_SESSION(self)->dtls.file_pbk, TMEDIA_SESSION(self)->dtls.file_pvk, TMEDIA_SESSION(self)->dtls.verify))){
return ret;
}
}
#endif /* HAVE_SRTP */
if((ret = trtp_manager_prepare(self->rtp_manager))){
return ret;
}
if(self->natt_ctx){
if((ret = trtp_manager_set_natt_ctx(self->rtp_manager, self->natt_ctx))){
return ret;
}
}
if(self->rtp_ssrc){
self->rtp_manager->rtp.ssrc.local = self->rtp_ssrc;
}
}
/* SRTP */
#if HAVE_SRTP
@ -669,7 +681,7 @@ int tdav_session_av_start(tdav_session_av_t* self, const tmedia_codec_t* best_co
return ret;
}
else{
else {
TSK_DEBUG_ERROR("Invalid RTP/RTCP manager");
return -3;
}
@ -914,12 +926,12 @@ const tsdp_header_M_t* tdav_session_av_get_lo(tdav_session_av_t* self, tsk_bool_
/* DTLS-SRTP default values */
if(is_srtp_dtls_enabled){
/* "setup" and "connection" */
// rfc5763: the caller is server by default
//if(self->dtls.local.setup == tnet_dtls_setup_none || self->dtls.local.setup == tnet_dtls_setup_actpass){
if (self->dtls.local.setup == tnet_dtls_setup_none || self->dtls.local.setup == tnet_dtls_setup_actpass) { // if setup already negotiated then, use the same
// rfc5763: the caller is server by default
self->dtls.remote.setup = (!base->M.ro) ? tnet_dtls_setup_active : tnet_dtls_setup_passive;
//}
_tdav_session_av_dtls_set_remote_setup(self, self->dtls.remote.setup, self->dtls.remote.connection_new, (!base->M.ro));
if(self->rtp_manager){
_tdav_session_av_dtls_set_remote_setup(self, self->dtls.remote.setup, self->dtls.remote.connection_new, (!base->M.ro));
}
if (self->rtp_manager) {
trtp_manager_set_dtls_local_setup(self->rtp_manager, self->dtls.local.setup, self->dtls.local.connection_new);
}
}
@ -985,7 +997,9 @@ const tsdp_header_M_t* tdav_session_av_get_lo(tdav_session_av_t* self, tsk_bool_
acap_tag_setup = 1, acap_tag_connection = 2;
_first_media_sprintf(&str, "%d setup:%s", acap_tag_setup, TNET_DTLS_SETUP_NAMES[self->dtls.local.setup]);
_first_media_add_headers(self->local_sdp, TSDP_HEADER_A_VA_ARGS("acap", str), tsk_null);
#if TDAV_DTLS_CONNECTION_ATT
_first_media_sprintf(&str, "%d connection:%s", acap_tag_connection, self->dtls.local.connection_new ? "new" : "existing");
#endif
_first_media_add_headers(self->local_sdp, TSDP_HEADER_A_VA_ARGS("acap", str), tsk_null);
// New Firefox Nightly repspond with SHA-256 when offered SHA-1 -> It's a bug in FF
// Just use SHA-256 as first choice
@ -1157,16 +1171,20 @@ const tsdp_header_M_t* tdav_session_av_get_lo(tdav_session_av_t* self, tsk_bool_
tsdp_header_M_add_headers(base->M.lo, TSDP_HEADER_A_VA_ARGS("fingerprint", str), tsk_null);
#else
_first_media_add_headers(self->local_sdp, TSDP_HEADER_A_VA_ARGS("fingerprint", str), tsk_null);
#endif
#endif /* TDAV_FIXME_MEDIA_LEVEL_DTLS_ATT */
TSK_FREE(str);
}
#if TDAV_FIXME_MEDIA_LEVEL_DTLS_ATT
tsdp_header_M_add_headers(base->M.lo, TSDP_HEADER_A_VA_ARGS("setup", TNET_DTLS_SETUP_NAMES[self->dtls.local.setup]), tsk_null);
#if TDAV_DTLS_CONNECTION_ATT
tsdp_header_M_add_headers(base->M.lo, TSDP_HEADER_A_VA_ARGS("connection", self->dtls.local.connection_new ? "new" : "existing"), tsk_null);
#endif /* TDAV_DTLS_CONNECTION_ATT */
#else
_first_media_add_headers(self->local_sdp, TSDP_HEADER_A_VA_ARGS("setup", TNET_DTLS_SETUP_NAMES[self->dtls.local.setup]), tsk_null);
#if TDAV_FIXME_MEDIA_LEVEL_DTLS_ATT
_first_media_add_headers(self->local_sdp, TSDP_HEADER_A_VA_ARGS("connection", self->dtls.local.connection_new ? "new" : "existing"), tsk_null);
#endif
#endif /* TDAV_FIXME_MEDIA_LEVEL_DTLS_ATT */
#endif /* TDAV_FIXME_MEDIA_LEVEL_DTLS_ATT */
is_srtp_dtls_activated = tsk_true;
}
@ -1347,21 +1365,21 @@ int tdav_session_av_set_ro(tdav_session_av_t* self, const struct tsdp_header_M_s
is_srtp_dtls_local_enabled = (self->srtp_mode != tmedia_srtp_mode_none) && (self->srtp_type & tmedia_srtp_type_dtls);
is_srtp_sdes_local_enabled = (self->srtp_mode != tmedia_srtp_mode_none) && (self->srtp_type & tmedia_srtp_type_sdes);
if(base->M.lo){
if((neg_codecs = tmedia_session_match_codec(base, m))){
if (base->M.lo) {
if ((neg_codecs = tmedia_session_match_codec(base, m))) {
/* update negociated codecs */
TSK_OBJECT_SAFE_FREE(base->neg_codecs);
base->neg_codecs = neg_codecs;
*updated = tsk_true;
}
else{
TSK_DEBUG_ERROR("None Match");
else {
TSK_DEBUG_ERROR("Codecs mismatch");
return -1;
}
/* QoS */
if(base->qos){
if (base->qos) {
tmedia_qos_tline_t* ro_tline;
if(base->M.ro && (ro_tline = tmedia_qos_tline_from_sdp(base->M.ro))){
if (base->M.ro && (ro_tline = tmedia_qos_tline_from_sdp(base->M.ro))) {
tmedia_qos_tline_set_ro(base->qos, ro_tline);
TSK_OBJECT_SAFE_FREE(ro_tline);
}
@ -1623,14 +1641,17 @@ int tdav_session_av_set_ro(tdav_session_av_t* self, const struct tsdp_header_M_s
: (self->dtls.local.setup == tnet_dtls_setup_passive ? "active" : (base->M.lo ? "passive" : "active")));
}
if(connection && setup){
if (connection && setup) {
// update local setup according to remote setup
ret = _tdav_session_av_dtls_set_remote_setup(self,
tnet_dtls_get_setup_from_string(setup),
!tsk_striequals(connection, "existing"),
(!base->M.ro)
);
if(ret == 0){
// do not update if local setup already negotiated
if (tnet_dtls_get_setup_from_string(setup) != tnet_dtls_setup_actpass || (self->dtls.local.setup == tnet_dtls_setup_none || self->dtls.local.setup == tnet_dtls_setup_actpass)) {
ret = _tdav_session_av_dtls_set_remote_setup(self,
tnet_dtls_get_setup_from_string(setup),
!tsk_striequals(connection, "existing"),
(!base->M.ro)
);
}
if (ret == 0) {
// pass new local values to the RTP manager
ret = trtp_manager_set_dtls_local_setup(self->rtp_manager, self->dtls.local.setup, self->dtls.local.connection_new);
srtp_dtls_neg_ok = (ret == 0);
@ -1821,10 +1842,12 @@ int _tdav_session_av_dtls_set_remote_setup(struct tdav_session_av_s* self, tnet_
self->dtls.local.connection_new = tsk_true;
break;
case tnet_dtls_setup_actpass:
self->dtls.local.setup = (self->dtls.local.setup == tnet_dtls_setup_actpass || self->dtls.local.setup == tnet_dtls_setup_active)
? tnet_dtls_setup_active
: tnet_dtls_setup_passive;
self->dtls.local.connection_new = tsk_true;
if (self->dtls.local.setup == tnet_dtls_setup_actpass || self->dtls.local.setup == tnet_dtls_setup_none) { // change local setup only if actpass or none
self->dtls.local.setup = (self->dtls.local.setup == tnet_dtls_setup_actpass || self->dtls.local.setup == tnet_dtls_setup_active)
? tnet_dtls_setup_active
: tnet_dtls_setup_passive;
self->dtls.local.connection_new = tsk_true;
}
break;
}
}

View File

@ -949,7 +949,8 @@ static int tdav_session_video_set(tmedia_session_t* self, const tmedia_param_t*
return -1;
}
if(tdav_session_av_set(TDAV_SESSION_AV(self), param) == tsk_true){
// try with the base class to see if this option is supported or not
if (tdav_session_av_set(TDAV_SESSION_AV(self), param) == tsk_true) {
return 0;
}
@ -998,10 +999,10 @@ static int tdav_session_video_set(tmedia_session_t* self, const tmedia_param_t*
ret = tmedia_producer_set(base->producer, param);
}
else{
if(param->value_type == tmedia_pvt_int32){
if(tsk_striequals(param->key, "bandwidth-level")){
if (param->value_type == tmedia_pvt_int32){
if (tsk_striequals(param->key, "bandwidth-level")){
tsk_list_item_t* item;
self->bl = (tmedia_bandwidth_level_t) TSK_TO_INT32((uint8_t*)param->value);
self->bl = (tmedia_bandwidth_level_t)TSK_TO_INT32((uint8_t*)param->value);
self->codecs = tsk_object_ref(self->codecs);
tsk_list_foreach(item, self->codecs){
((tmedia_codec_t*)item->data)->bl = self->bl;
@ -1016,16 +1017,27 @@ static int tdav_session_video_set(tmedia_session_t* self, const tmedia_param_t*
static int tdav_session_video_get(tmedia_session_t* self, tmedia_param_t* param)
{
if(!self){
if (!self || !param) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
if(tdav_session_av_get(TDAV_SESSION_AV(self), param) == tsk_true){
// try with the base class to see if this option is supported or not
if (tdav_session_av_get(TDAV_SESSION_AV(self), param) == tsk_true) {
return 0;
}
else {
if (param->plugin_type == tmedia_ppt_session) {
if (param->value_type == tmedia_pvt_pobject) {
if (tsk_striequals(param->key, "codec-encoder")) {
*((tsk_object_t**)param->value) = tsk_object_ref(TDAV_SESSION_VIDEO(self)->encoder.codec); // up to the caller to release the object
return 0;
}
}
}
}
TSK_DEBUG_ERROR("Not expected");
TSK_DEBUG_WARN("This session doesn't support get(%s)", param->key);
return -2;
}
@ -1062,6 +1074,11 @@ static int tdav_session_video_start(tmedia_session_t* self)
video = (tdav_session_video_t*)self;
base = (tdav_session_av_t*)self;
if (video->started) {
TSK_DEBUG_INFO("Video session already started");
return 0;
}
// ENCODER codec
if (!(codec = tdav_session_av_get_best_neg_codec(base))) {
TSK_DEBUG_ERROR("No codec matched");

View File

@ -249,6 +249,7 @@ typedef struct tmedia_codec_s
const struct tmedia_codec_plugin_def_s* plugin;
}
tmedia_codec_t;
#define TMEDIA_CODEC(self) ((tmedia_codec_t*)(self))
/** Virtual table used to define a codec plugin */
typedef struct tmedia_codec_plugin_def_s

View File

@ -97,6 +97,10 @@ TINYMEDIA_API tmedia_param_t* tmedia_param_create(tmedia_param_access_type_t acc
tmedia_param_value_type_t value_type,
const char* key,
void* value);
#define tmedia_param_create_get(media_type, plugin_type, value_type, key, value) tmedia_param_create(tmedia_pat_get, (media_type), (plugin_type), (value_type), (key), (value))
#define tmedia_param_create_get_session(media_type, value_type, key, value) tmedia_param_create_get((media_type), tmedia_ppt_session, (value_type), (key), (value))
#define tmedia_param_create_get_codec(media_type, value_type, key, value) tmedia_param_create_get((media_type), tmedia_ppt_codec, (value_type), (key), (value))
#define tmedia_param_create_set(media_type, plugin_type, value_type, key, value) tmedia_param_create(tmedia_pat_set, (media_type), (plugin_type), (value_type), (value))
TINYMEDIA_API tmedia_params_L_t* tmedia_params_create_2(va_list *app);

View File

@ -198,7 +198,7 @@ TINYMEDIA_API uint64_t tmedia_session_get_unique_id();
TINYMEDIA_API int tmedia_session_init(tmedia_session_t* self, tmedia_type_t type);
TINYMEDIA_API int tmedia_session_set(tmedia_session_t* self, ...);
TINYMEDIA_API tsk_bool_t tmedia_session_set_2(tmedia_session_t* self, const tmedia_param_t* param);
TINYMEDIA_API tsk_bool_t tmedia_session_get(tmedia_session_t* self, tmedia_param_t* param);
TINYMEDIA_API int tmedia_session_get(tmedia_session_t* self, tmedia_param_t* param);
TINYMEDIA_API int tmedia_session_cmp(const tsk_object_t* sess1, const tsk_object_t* sess2);
TINYMEDIA_API int tmedia_session_plugin_register(const tmedia_session_plugin_def_t* plugin);
TINYMEDIA_API const tmedia_session_plugin_def_t* tmedia_session_plugin_find_by_media(const char* media);
@ -568,6 +568,7 @@ TINYMEDIA_API int tmedia_session_mgr_set_msrp_cb(tmedia_session_mgr_t* self, con
TINYMEDIA_API int tmedia_session_mgr_set_onerror_cbfn(tmedia_session_mgr_t* self, const void* usrdata, tmedia_session_onerror_cb_f fun);
TINYMEDIA_API int tmedia_session_mgr_set_rfc5168_cbfn(tmedia_session_mgr_t* self, const void* usrdata, tmedia_session_rfc5168_cb_f fun);
TINYMEDIA_API int tmedia_session_mgr_set_bfcp_cbfn(tmedia_session_mgr_t* self, const void* usrdata, tmedia_session_bfcp_cb_f fun);
TINYMEDIA_API int tmedia_session_mgr_lo_apply_changes(tmedia_session_mgr_t* self);
TINYMEDIA_GEXTERN const tsk_object_def_t *tmedia_session_mgr_def_t;

View File

@ -44,12 +44,20 @@
#define kSkipSessionLoadTrue tsk_true
#define kSkipSessionLoadFalse tsk_false
#define kForceUpdateLOTrue tsk_true
#define kForceUpdateLOFalse tsk_false
#define kSessionIndexAll -1
extern const tmedia_codec_plugin_def_t* __tmedia_codec_plugins[TMED_CODEC_MAX_PLUGINS];
/* pointer to all registered sessions */
const tmedia_session_plugin_def_t* __tmedia_session_plugins[TMED_SESSION_MAX_PLUGINS] = { 0 };
#if !defined(TMEDIA_SESSION_MAX_LINES)
# define TMEDIA_SESSION_MAX_LINES 64 // too high to but who knows
#endif /* TMEDIA_SESSION_MAX_LINES */
/* === local functions === */
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);
static int _tmedia_session_mgr_load_sessions(tmedia_session_mgr_t* self);
@ -59,7 +67,9 @@ static int _tmedia_session_mgr_disable_or_enable_session_with_type(tmedia_sessio
static const tmedia_session_t* _tmedia_session_mgr_find_session_at_index(const tmedia_sessions_L_t* list, tsk_size_t index);
static int _tmedia_session_mgr_clear_sessions(tmedia_session_mgr_t* self);
static int _tmedia_session_mgr_apply_params(tmedia_session_mgr_t* self);
const tsdp_message_t* _tmedia_session_mgr_get_lo(tmedia_session_mgr_t* self, tsk_bool_t skip_session_load);
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);
static int _tmedia_session_mgr_start(tmedia_session_mgr_t* self, int session_index);
static int _tmedia_session_mgr_stop(tmedia_session_mgr_t* self, int session_index);
static int _tmedia_session_prepare(tmedia_session_t* self);
static int _tmedia_session_set_ro(tmedia_session_t* self, const tsdp_header_M_t* m);
@ -107,6 +117,14 @@ static int __pred_find_codec_by_id(const tsk_list_item_t *item, const void *id)
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;
}
uint64_t tmedia_session_get_unique_id(){
static uint64_t __UniqueId = 1; // MUST not be equal to zero
return __UniqueId++;
@ -239,30 +257,16 @@ tsk_bool_t tmedia_session_set_2(tmedia_session_t* self, const tmedia_param_t* pa
return tsk_false;
}
tsk_bool_t tmedia_session_get(tmedia_session_t* self, tmedia_param_t* param)
int tmedia_session_get(tmedia_session_t* self, tmedia_param_t* param)
{
if (!self || !param){
if (!self || !param) {
TSK_DEBUG_ERROR("Invalid parameter");
return tsk_false;
return -1;
}
if (param->plugin_type == tmedia_ppt_session){
if (param->value_type == tmedia_pvt_int32){
if (tsk_striequals(param->key, "codecs-negotiated")){ // negotiated codecs
tmedia_codecs_L_t* neg_codecs = tsk_object_ref(self->neg_codecs);
if (neg_codecs){
const tsk_list_item_t* item;
tsk_list_foreach(item, neg_codecs){
((int32_t*)param->value)[0] |= TMEDIA_CODEC(item->data)->id;
}
TSK_OBJECT_SAFE_FREE(neg_codecs);
}
return tsk_true;
}
}
if (self->plugin && self->plugin->get) {
return self->plugin->get(self, param);
}
return tsk_false;
return -2;
}
/**@ingroup tmedia_session_group
@ -938,42 +942,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)
{
int ret = 0, started_count = 0;
tsk_list_item_t* item;
tmedia_session_t* session;
if (!self){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
tsk_safeobj_lock(self);
if (self->started){
goto bail;
}
tsk_list_foreach(item, self->sessions){
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;
}
++started_count;
}
self->started = (started_count > 0) ? tsk_true : tsk_false;
bail:
tsk_safeobj_unlock(self);
return (self->started ? 0 : -2);
return _tmedia_session_mgr_start(self, kSessionIndexAll);
}
/**@ingroup tmedia_session_group
@ -1104,39 +1073,7 @@ int tmedia_session_mgr_get(tmedia_session_mgr_t* self, ...)
*/
int tmedia_session_mgr_stop(tmedia_session_mgr_t* self)
{
int ret = 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);
if (!self->started){
goto bail;
}
tsk_list_foreach(item, self->sessions){
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;
}
self->started = tsk_false;
bail:
tsk_safeobj_unlock(self);
return ret;
return _tmedia_session_mgr_stop(self, kSessionIndexAll);
}
/**@ingroup tmedia_session_group
@ -1144,7 +1081,7 @@ bail:
*/
const tsdp_message_t* tmedia_session_mgr_get_lo(tmedia_session_mgr_t* self)
{
return _tmedia_session_mgr_get_lo(self, kSkipSessionLoadFalse);
return _tmedia_session_mgr_get_lo(self, kSkipSessionLoadFalse, kForceUpdateLOFalse);
}
@ -1158,17 +1095,21 @@ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t*
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;
tsk_size_t active_sessions_count = 0, m_lines_count = 0;
int ret = 0;
tsk_bool_t found;
tsk_bool_t stopped_to_reconf = tsk_false;
tsk_bool_t is_ro_codecs_changed = tsk_false;
tsk_bool_t is_ro_network_info_changed = tsk_false;
tsk_bool_t is_ro_hold_resume_changed = tsk_false;
tsk_bool_t is_ro_loopback_address = tsk_false;
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 };
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 is_ice_active = 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;
@ -1191,9 +1132,6 @@ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t*
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);
is_ice_active = (self->ice.ctx_audio && (self->type & tmedia_audio) && tnet_ice_ctx_is_active(self->ice.ctx_audio))
|| (self->ice.ctx_video && (self->type & tmedia_video) && tnet_ice_ctx_is_active(self->ice.ctx_video))
|| (self->ice.ctx_bfcpvid && (self->type & tmedia_bfcp_video) && tnet_ice_ctx_is_active(self->ice.ctx_bfcpvid));
// Remove BFCP offer if not locally enabled. Only the client can init BFCP session.
if ((ro_type & tmedia_ro_type_offer)) {
@ -1238,66 +1176,96 @@ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t*
goto bail;
}
/* SDP comparison
*/
/* 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 %d>%d", m_lines_count, TMEDIA_SESSION_MAX_LINES);
ret = -2;
goto bail;
}
M1 = (const tsdp_header_M_t*)tsdp_message_get_headerAt(sdp, tsdp_htype_M, index);
if (!M1 || !tsk_striequals(M1->media, M0->media)){
// media lines must be at the same index
// (M1 == null) means media lines are not at the same index or new one have been added/removed
is_ro_media_lines_changed = tsk_true;
// 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);
if (med_level_diff & tsdp_header_M_diff_media_type);
// hold/resume
is_ro_hold_resume_changed |= (M1 && !tsk_striequals(tsdp_header_M_get_holdresume_att(M0), tsdp_header_M_get_holdresume_att(M1)));
// media lines
if (!is_ro_media_lines_changed){
char* M0ProtoF = tsk_null; // old proto with "F" at the end, do nothing if we transit from AVP to AVPF because it means nego. succeeded
tsk_strcat_2(&M0ProtoF, "%sF", M0->proto);
is_ro_media_lines_changed
// (M1 == null) means media lines are not at the same index or new one have been added/removed
|= (!M1)
// same media (e.g. audio)
|| !tsk_striequals(M1->media, M0->media)
// same protos (e.g. SRTP)
|| !(tsk_striequals(M1->proto, M0->proto) || tsk_striequals(M1->proto, M0ProtoF));
TSK_FREE(M0ProtoF);
// 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));
}
// codecs
if (!is_ro_media_lines_changed){ // make sure it's same media at same index
const tsk_list_item_t* codec_item0;
const tsdp_fmt_t* codec_fmt1;
tsk_size_t codec_index0 = 0;
tsk_list_foreach(codec_item0, M0->FMTs){
codec_fmt1 = (const tsdp_fmt_t*)tsk_list_find_object_by_pred_at_index(M1->FMTs, tsk_null, tsk_null, codec_index0++);
if (!codec_fmt1 || !tsk_striequals(codec_fmt1->value, ((const tsdp_fmt_t*)codec_item0->data)->value)){
is_ro_codecs_changed = tsk_true;
break;
}
}
// make sure M0 and M1 have same number of codecs
is_ro_codecs_changed |= (tsk_list_find_object_by_pred_at_index(M1->FMTs, tsk_null, tsk_null, codec_index0) != tsk_null);
}
// network ports
is_ro_network_info_changed |= ((M1 ? M1->port : 0) != (M0->port));
if (!is_ro_network_info_changed){
// 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 = !((C0 && C1) || (!C0 && !C1)))){
if (C0){
is_ro_network_info_changed = (!tsk_strequals(C1->addr, C0->addr) || !tsk_strequals(C1->nettype, C0->nettype) || !tsk_strequals(C1->addrtype, C0->addrtype));
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));
}
}
}
// 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"
@ -1316,7 +1284,7 @@ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t*
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 || is_ro_network_info_changed);
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.
@ -1324,8 +1292,11 @@ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t*
* Loopback address is only invalid on
*/
if ((C = (const tsdp_header_C_t*)tsdp_message_get_header(sdp, tsdp_htype_C)) && C->addr){
is_ro_loopback_address = (tsk_striequals("IP4", C->addrtype) && tsk_striequals("127.0.0.1", 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
@ -1340,22 +1311,30 @@ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t*
}
TSK_DEBUG_INFO(
"is_ice_active=%d,\n"
"is_ro_hold_resume_changed=%d,\n"
"m_lines_count=%u,\n"
"is_dtls_fingerprint_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=%d,\n"
"is_ro_loopback_address=%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=%d\n",
is_ice_active,
is_ro_hold_resume_changed,
"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_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,
is_ro_network_info_changed,
is_ro_loopback_address,
(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,
is_ro_codecs_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)
);
/*
@ -1367,15 +1346,19 @@ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t*
* loopback address won't work on embedded devices such as iOS and Android.
*
*/
if ((self->started && !is_ro_loopback_address) && (is_ro_codecs_changed || is_ro_network_info_changed || is_ro_media_lines_changed || is_media_type_changed)){
TSK_DEBUG_INFO("stopped_to_reconf=true,is_ice_active=%s", is_ice_active ? "true" : "false");
stopped_to_reconf = tsk_true;
tmedia_session_mgr_set(self,
TMEDIA_SESSION_SET_INT32(self->type, "stop-to-reconf", stopped_to_reconf),
TMEDIA_SESSION_SET_NULL());
if ((ret = tmedia_session_mgr_stop(self))){
TSK_DEBUG_ERROR("Failed to stop session manager");
goto bail;
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])) {
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;
}
}
}
}
@ -1387,7 +1370,7 @@ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t*
- 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 && !(is_ro_hold_resume_changed || is_ro_network_info_changed || is_ro_media_lines_changed || is_ro_codecs_changed)){
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;
}
@ -1398,6 +1381,19 @@ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t*
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
@ -1446,11 +1442,9 @@ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t*
#endif
if (ms && (tsk_striequals(tmedia_session_get_media(ms), M->media))) {
/* prepare the media session */
if (!self->started) {
if (!ms->prepared && M->port && (_tmedia_session_prepare(TMEDIA_SESSION(ms)))){
TSK_DEBUG_ERROR("Failed to prepare session"); /* should never happen */
goto bail;
}
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) {
@ -1462,7 +1456,7 @@ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t*
}
else {
TSK_DEBUG_WARN("_tmedia_session_set_ro() failed");
ret = 0; // add ghost for this session. Do not goto bail because se_ro() is allowed to fail (e.g. codec mismatch).
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) {
@ -1493,7 +1487,7 @@ int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t*
}
tsk_list_push_back_data(self->sessions, (void**)&ghost);
}
else{
else {
TSK_DEBUG_ERROR("Failed to create ghost session");
continue;
}
@ -1508,7 +1502,7 @@ end_of_sessions_update:
}
/* signal that ro has changed (will be used to update lo) unless there was no ro_sdp */
self->ro_changed = (had_ro_sdp && (is_ro_hold_resume_changed || is_ro_network_info_changed || is_ro_media_lines_changed || is_ro_codecs_changed /*|| is_media_type_changed || is_new_mediatype_striped*/));
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);
@ -1516,15 +1510,17 @@ end_of_sessions_update:
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));
(_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.
*/
if (stopped_to_reconf && !is_ice_active) {
if ((ret = tmedia_session_mgr_start(self))) {
TSK_DEBUG_ERROR("Failed to re-start session manager");
goto bail;
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", index);
goto bail;
}
}
}
@ -2057,6 +2053,16 @@ int tmedia_session_mgr_set_bfcp_cbfn(tmedia_session_mgr_t* self, const void* usr
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;
}
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;
@ -2199,12 +2205,15 @@ static int _tmedia_session_mgr_clear_sessions(tmedia_session_mgr_t* self)
}
/* internal function */
const tsdp_message_t* _tmedia_session_mgr_get_lo(tmedia_session_mgr_t* self, tsk_bool_t skip_session_load)
// 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");
@ -2234,21 +2243,51 @@ const tsdp_message_t* _tmedia_session_mgr_get_lo(tmedia_session_mgr_t* self, tsk
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;
if (self->sdp.lo){
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, self->sdp.lo_ver++))){
/* 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);
}
else{
self->sdp.lo_ver--;
TSK_DEBUG_ERROR("Failed to create empty SDP message");
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 */
@ -2297,6 +2336,81 @@ bail:
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) {
self->started = tsk_true;
}
bail:
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;
}
bail:
tsk_safeobj_unlock(self);
return ret;
}
/* internal function */
static int _tmedia_session_mgr_apply_params(tmedia_session_mgr_t* self)
{

View File

@ -2235,7 +2235,7 @@ static int _tnet_ice_ctx_recv_stun_message_for_pair(tnet_ice_ctx_t* self, const
}
else;
}
else{ // I'm ICE-CONTROLLED
else { // I'm ICE-CONTROLLED
const tnet_stun_attr_vdata_t* stun_att_ice_controlled;
if ((ret = tnet_stun_pkt_attr_find_first(message, tnet_stun_attr_type_ice_controlled, (const tnet_stun_attr_t**)&stun_att_ice_controlled)) == 0 && stun_att_ice_controlled) {
TSK_DEBUG_WARN("Role conflicts (SEND)");
@ -2272,6 +2272,7 @@ static int _tnet_ice_ctx_recv_stun_message_for_pair(tnet_ice_ctx_t* self, const
else if (TNET_STUN_PKT_IS_RESP(message)) {
if (pair || (pair = tnet_ice_pairs_find_by_response(self->candidates_pairs, message))) {
ret = tnet_ice_pair_recv_response(((tnet_ice_pair_t*)pair), message, self->is_connchecking);
#if 0
if (TNET_STUN_PKT_RESP_IS_ERROR(message)) {
uint16_t u_code;
if ((ret = tnet_stun_pkt_get_errorcode(message, &u_code)) == 0 && u_code == kStunErrCodeIceConflict) {
@ -2281,6 +2282,7 @@ static int _tnet_ice_ctx_recv_stun_message_for_pair(tnet_ice_ctx_t* self, const
*role_conflict = tsk_true;
}
}
#endif
}
}
}
@ -2693,6 +2695,10 @@ enum tnet_socket_type_e e_transport,
// TURN requires credentials
if ((e_proto & tnet_ice_server_proto_turn) == tnet_ice_server_proto_turn && (tsk_strnullORempty(str_username) || tsk_strnullORempty(str_password))) {
/* rfc5766 - 4. General Behavior
The server MUST demand that all requests from the client
be authenticated using this mechanism, or that a equally strong or
stronger mechanism for client authentication is used.*/
TSK_DEBUG_ERROR("TURN requires credentials");
return -1;
}

View File

@ -503,9 +503,13 @@ int tnet_ice_pair_send_response(tnet_ice_pair_t *self, const tnet_stun_pkt_req_t
if (ret == 0 && !is_error) {
tsk_bool_t change_state =
self->is_ice_jingle || // ICE-JINGLE don't have ICE-CONTROLLING/ICE-CONTROLLED attributes
(!self->is_controlling && tnet_stun_pkt_attr_exists(request, tnet_stun_attr_type_ice_use_candidate)) || // Sender is controlling and uses "ICE-USE-CANDIDATE" attribute
(self->is_controlling) // We always use agressive nomination and final check-list will have high-priority pairs on the top
self->is_ice_jingle // ICE-JINGLE don't have ICE-CONTROLLING/ICE-CONTROLLED attributes
|| (!self->is_controlling && tnet_stun_pkt_attr_exists(request, tnet_stun_attr_type_ice_use_candidate)) // Sender is controlling and uses "ICE-USE-CANDIDATE" attribute
#if TNET_ICE_AGGRESSIVE_NOMINATION || 1 // This is not a typo but a *must*. We've to change the answer state regardless the nomination mode otherwise we'll never get a nominee. Only the offer state is controlled based on the mode and depends on "is_nominated".
|| (self->is_controlling) // We are controlling and using aggressive mode
#else
|| (self->is_controlling && self->is_nominated) // We're controlling and using regular mode
#endif
;
TNET_ICE_PAIR_DEBUG_INFO("ICE pair-answer changing state to 'succeed' ? %s, comp-id=%d, found=%s, addr=%s",
change_state?"yes":"no",
@ -650,16 +654,29 @@ int tnet_ice_pair_auth_conncheck(const tnet_ice_pair_t *self, const tnet_stun_pk
int tnet_ice_pair_recv_response(tnet_ice_pair_t *self, const tnet_stun_pkt_resp_t* response, tsk_bool_t is_4conncheck)
{
if (self && response && is_4conncheck) {
if (self && response && TNET_STUN_PKT_IS_RESP(response)) {
if (self->last_request && tnet_stun_utils_transac_id_equals(self->last_request->transac_id, response->transac_id)){
// ignore errors (e.g. STALE-CREDENTIALS) which could happen in some special cases before success
if (TNET_STUN_PKT_RESP_IS_SUCCESS(response)){
self->state_offer = tnet_ice_pair_state_succeed; // we must not change the state after connection cheking to make sure another pair won't be picked as nominated
TNET_ICE_PAIR_DEBUG_INFO("ICE pair-offer changing state to 'succeed', comp-id=%d, found=%s, addr=%s",
self->candidate_offer->comp_id,
self->candidate_offer->foundation,
self->candidate_offer->connection_addr
);
if (TNET_STUN_PKT_RESP_IS_SUCCESS(response)) {
if (is_4conncheck) {
self->state_offer = tnet_ice_pair_state_succeed; // we must not change the state after connection cheking to make sure another pair won't be picked as nominated
TNET_ICE_PAIR_DEBUG_INFO("ICE pair-offer changing state to 'succeed', comp-id=%d, found=%s, addr=%s",
self->candidate_offer->comp_id,
self->candidate_offer->foundation,
self->candidate_offer->connection_addr
);
}
}
else {
// The response is an error
uint16_t u_code;
int ret;
if ((ret = tnet_stun_pkt_get_errorcode(response, &u_code)) == 0 && u_code == kStunErrCodeIceConflict) {
TSK_DEBUG_INFO("ICE Pair %llu received conflict error message", self->id);
// If this code is called this means that we have lower tie-breaker and we must toggle our role
self->is_controlling = !self->is_controlling;
TSK_OBJECT_SAFE_FREE(self->last_request); // delete the "last_request" to make sure a new one will be created with right attributes
}
}
}
}

View File

@ -551,8 +551,8 @@ static int _trtp_manager_srtp_set_enabled(trtp_manager_t* self, tmedia_srtp_type
}
self->srtp_state = trtp_srtp_state_enabled;
}
else{
if(srtp_type & tmedia_srtp_type_dtls){
else {
if (srtp_type & tmedia_srtp_type_dtls) {
if (self->transport) {
ret = tnet_transport_dtls_set_enabled(self->transport, tsk_false, sockets, count);
}
@ -569,6 +569,10 @@ static int _trtp_manager_srtp_set_enabled(trtp_manager_t* self, tmedia_srtp_type
self->srtp_ctx_neg_local = tsk_null;
self->srtp_ctx_neg_remote = tsk_null;
self->srtp_state = trtp_srtp_state_none;
// Reset SRTP session to the RTCP session manager
if (self->rtcp.session) {
trtp_rtcp_session_set_srtp_sess(self->rtcp.session, tsk_null);
}
}
}
}
@ -870,6 +874,8 @@ int trtp_manager_prepare(trtp_manager_t* self)
TSK_DEBUG_INFO("ICE enabled on RTP manager");
// Get Sockets when the transport is started
rtp_local_ip = rtcp_local_ip = self->use_ipv6 ? "::1" : "127.0.0.1";
rtp_local_port = 2; // ICE default rtp port, do not use zero which is reserved to disabled medias
rtcp_local_port = 1; // ICE default rtcp port, do not use zero which is reserved to disabled medias
}
else{
#define __retry_count_max 5

View File

@ -242,6 +242,7 @@ TINYSAK_API int tsk_list_find_index_by_pred(const tsk_list_t* list, tsk_list_fun
TINYSAK_API tsk_list_t* tsk_list_clone(const tsk_list_t* list);
TINYSAK_API tsk_size_t tsk_list_count(const tsk_list_t* list, tsk_list_func_predicate predicate, const void* data);
#define tsk_list_count_all(list) tsk_list_count((list), tsk_null, tsk_null)
TINYSAK_GEXTERN const tsk_object_def_t *tsk_list_def_t;
TINYSAK_GEXTERN const tsk_object_def_t *tsk_list_item_def_t;

View File

@ -98,6 +98,21 @@ typedef struct tsdp_header_M_s
}
tsdp_header_M_t;
typedef enum tsdp_header_M_diff_e {
tsdp_header_M_diff_none = 0x0000000,
tsdp_header_M_diff_hold_resume = (0x0000001 << 0),
tsdp_header_M_diff_index = (0x0000001 << 1),
tsdp_header_M_diff_codecs = (0x0000001 << 2),
tsdp_header_M_diff_network_info = (0x0000001 << 3),
tsdp_header_M_diff_ice_enabled = (0x0000001 << 4),
tsdp_header_M_diff_ice_restart = (0x0000001 << 5),
tsdp_header_M_diff_dtls_fingerprint = (0x0000001 << 6),
tsdp_header_M_diff_sdes_crypto = (0x0000001 << 7),
tsdp_header_M_diff_media_type = (0x0000001 << 8),
tsdp_header_M_diff_all = 0xFFFFFFFF
}
tsdp_header_M_diff_t;
typedef tsk_list_t tsdp_headers_M_L_t;
TINYSDP_API tsdp_header_M_t* tsdp_header_M_create(const char* media, uint32_t port, const char* proto);
@ -110,7 +125,7 @@ TINYSDP_API int tsdp_header_M_add_headers(tsdp_header_M_t* self, ...);
TINYSDP_API int tsdp_header_M_add_headers_2(tsdp_header_M_t* self, const tsdp_headers_L_t* headers);
TINYSDP_API int tsdp_header_M_add_fmt(tsdp_header_M_t* self, const char* fmt);
TINYSDP_API int tsdp_header_M_remove_fmt(tsdp_header_M_t* self, const char* fmt);
TINYSDP_API tsk_bool_t tsdp_header_M_have_fmt(tsdp_header_M_t* self, const char* fmt);
TINYSDP_API tsk_bool_t tsdp_header_M_have_fmt(const tsdp_header_M_t* self, const char* fmt);
TINYSDP_API const tsdp_header_A_t* tsdp_header_M_findA_at(const tsdp_header_M_t* self, const char* field, tsk_size_t index);
TINYSDP_API const tsdp_header_A_t* tsdp_header_M_findA(const tsdp_header_M_t* self, const char* field);
TINYSDP_API char* tsdp_header_M_getAValue(const tsdp_header_M_t* self, const char* field, const char* fmt);
@ -122,6 +137,9 @@ TINYSDP_API tsk_bool_t tsdp_header_M_is_held(const tsdp_header_M_t* self, tsk_bo
TINYSDP_API int tsdp_header_M_set_holdresume_att(tsdp_header_M_t* self, tsk_bool_t lo_held, tsk_bool_t ro_held);
TINYSDP_API const char* tsdp_header_M_get_holdresume_att(const tsdp_header_M_t* self);
TINYSDP_API int tsdp_header_M_resume(tsdp_header_M_t* self, tsk_bool_t local);
TINYSDP_API tsk_bool_t tsdp_header_M_is_ice_enabled(const tsdp_header_M_t* self);
TINYSDP_API tsk_bool_t tsdp_header_M_is_ice_restart(const tsdp_header_M_t* self);
TINYSDP_API int tsdp_header_M_diff(const tsdp_header_M_t* M_old, const tsdp_header_M_t* M_new, tsdp_header_M_diff_t* diff);
TINYSDP_GEXTERN const tsk_object_def_t *tsdp_header_M_def_t;

View File

@ -1,7 +1,5 @@
/*
* Copyright (C) 2010-2011 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
* Copyright (C) 2010-2015 Mamadou DIOP.
*
* This file is part of Open Source Doubango Framework.
*
@ -23,9 +21,6 @@
/**@file tsdp_message.h
* @brief SDP message.
*
* @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
*/
#ifndef TINYSDP_MESSAGE_H
#define TINYSDP_MESSAGE_H
@ -83,6 +78,7 @@ TINYSDP_API const tsdp_header_t *tsdp_message_get_header(const tsdp_message_t *s
TINYSDP_API const tsdp_header_A_t* tsdp_message_get_headerA_at(const tsdp_message_t* self, const char* field, tsk_size_t index);
TINYSDP_API const tsdp_header_A_t* tsdp_message_get_headerA(const tsdp_message_t* self, const char* field);
TINYSDP_API const tsdp_header_t *tsdp_message_get_headerByName(const tsdp_message_t *self, char name);
TINYSDP_API int tsdp_message_get_sess_version(const tsdp_message_t *self, uint32_t *version);
TINYSDP_API int tsdp_message_serialize(const tsdp_message_t *self, tsk_buffer_t *output);
TINYSDP_API char* tsdp_message_tostring(const tsdp_message_t *self);
@ -99,6 +95,9 @@ TINYSDP_API const tsdp_header_M_t* tsdp_message_find_media(const tsdp_message_t
TINYSDP_API int tsdp_message_hold(tsdp_message_t* self, const char* media);
TINYSDP_API int tsdp_message_resume(tsdp_message_t* self, const char* media);
TINYSDP_API tsk_bool_t tsdp_message_is_ice_enabled(const tsdp_message_t *self, tsk_size_t media_index);
TINYSDP_API tsk_bool_t tsdp_message_is_ice_restart(const tsdp_message_t *self, tsk_size_t media_index);
TINYSDP_API tsdp_message_t* tsdp_message_create();
TINYSDP_GEXTERN const tsk_object_def_t *tsdp_message_def_t;

File diff suppressed because it is too large Load Diff

View File

@ -1,445 +1,503 @@
/*
* Copyright (C) 2010-2011 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
/**@file tsdp_message.c
* @brief SDP message.
*
* @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
*/
#include "tinysdp/tsdp_message.h"
#include "tinysdp/headers/tsdp_header_O.h"
#include "tinysdp/headers/tsdp_header_S.h"
#include "tinysdp/headers/tsdp_header_T.h"
#include "tinysdp/headers/tsdp_header_V.h"
#include "tsk_debug.h"
#define TSDP_LINE_S_VALUE_DEFAULT "-" /* as per RFC 3264 subclause 5 */
#define TSDP_LINE_O_USERNAME_DEFAULT "doubango"
#define TSDP_LINE_O_SESSION_VER_DEFAULT 2301
#define TSDP_LINE_O_SESSION_ID_DEFAULT 1983
/*== Predicate function to find tsdp_header_t object by type. */
static int __pred_find_header_by_type(const tsk_list_item_t *item, const void *tsdp_htype)
{
if(item && item->data){
tsdp_header_t *header = item->data;
tsdp_header_type_t htype = *((tsdp_header_type_t*)tsdp_htype);
return (header->type - htype);
}
return -1;
}
/*== Predicate function to find tsdp_header_t object by name. */
static int __pred_find_header_by_name(const tsk_list_item_t *item, const void *name)
{
if(item && item->data && name){
tsdp_header_t *header = item->data;
return tsdp_header_get_nameex(header) - *((const char*)name);
}
return -1;
}
/*== Predicate function to find media object by name. */
static int __pred_find_media_by_name(const tsk_list_item_t *item, const void *name)
{
if(item && item->data && name){
tsdp_header_t *header = item->data;
if(header->type == tsdp_htype_M){
return tsk_stricmp(((tsdp_header_M_t*)header)->media, (const char*)name);
/*
* Copyright (C) 2010-2015 Mamadou DIOP.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
/**@file tsdp_message.c
* @brief SDP message.
*/
#include "tinysdp/tsdp_message.h"
#include "tinysdp/headers/tsdp_header_O.h"
#include "tinysdp/headers/tsdp_header_S.h"
#include "tinysdp/headers/tsdp_header_T.h"
#include "tinysdp/headers/tsdp_header_V.h"
#include "tsk_debug.h"
#define TSDP_LINE_S_VALUE_DEFAULT "-" /* as per RFC 3264 subclause 5 */
#define TSDP_LINE_O_USERNAME_DEFAULT "doubango"
#define TSDP_LINE_O_SESSION_VER_DEFAULT 2301
#define TSDP_LINE_O_SESSION_ID_DEFAULT 1983
/*== Predicate function to find tsdp_header_t object by type. */
static int __pred_find_header_by_type(const tsk_list_item_t *item, const void *tsdp_htype)
{
if(item && item->data){
tsdp_header_t *header = item->data;
tsdp_header_type_t htype = *((tsdp_header_type_t*)tsdp_htype);
return (header->type - htype);
}
return -1;
}
/*== Predicate function to find tsdp_header_t object by name. */
static int __pred_find_header_by_name(const tsk_list_item_t *item, const void *name)
{
if(item && item->data && name){
tsdp_header_t *header = item->data;
return tsdp_header_get_nameex(header) - *((const char*)name);
}
return -1;
}
/*== Predicate function to find media object by name. */
static int __pred_find_media_by_name(const tsk_list_item_t *item, const void *name)
{
if(item && item->data && name){
tsdp_header_t *header = item->data;
if(header->type == tsdp_htype_M){
return tsk_stricmp(((tsdp_header_M_t*)header)->media, (const char*)name);
}
}
return -1;
}
tsdp_message_t* tsdp_message_create()
{
return tsk_object_new(tsdp_message_def_t);
}
/*== Add headers/fmt to the media line */
int __add_headers(tsdp_header_M_t* m, va_list *ap)
{
const tsk_object_def_t* objdef;
tsdp_header_t *header;
tsdp_fmt_t* fmt;
if(!m){
return -1;
}
while((objdef = va_arg(*ap, const tsk_object_def_t*))){
if(objdef == tsdp_fmt_def_t){
if((fmt = tsk_object_new_2(objdef, ap))){
tsk_list_push_back_data(m->FMTs, (void**)&fmt);
}
}
else{
if((header = tsk_object_new_2(objdef, ap))){
tsdp_header_M_add(m, header);
TSK_OBJECT_SAFE_FREE(header);
}
}
}
return 0;
}
int tsdp_message_add_header(tsdp_message_t *self, const tsdp_header_t *hdr)
{
if(self && hdr){
tsdp_header_t *header = tsk_object_ref((void*)hdr);
tsk_list_push_ascending_data(self->headers, (void**)&header); // Very important: Headers MUST appear in a fixed order (see ranks def).
return 0;
}
return -1;
}
int tsdp_message_add_headers(tsdp_message_t *self, ...)
{
const tsk_object_def_t* objdef;
tsdp_header_t *header;
va_list ap;
if(!self){
return -1;
}
va_start(ap, self);
while((objdef = va_arg(ap, const tsk_object_def_t*))){
if((header = tsk_object_new_2(objdef, &ap))){
tsdp_message_add_header(self, header);
TSK_OBJECT_SAFE_FREE(header);
}
}
va_end(ap);
return 0;
}
const tsdp_header_t *tsdp_message_get_headerAt(const tsdp_message_t *self, tsdp_header_type_t type, tsk_size_t index)
{
tsk_size_t pos = 0;
const tsk_list_item_t *item;
const tsdp_header_t *hdr;
if(!self || !self->headers){
return tsk_null;
}
tsk_list_foreach(item, self->headers){
hdr = item->data;
if(hdr->type == type){
if(pos++ >= index){
return hdr;
}
}
}
return tsk_null;
}
const tsdp_header_t *tsdp_message_get_header(const tsdp_message_t *self, tsdp_header_type_t type)
{
return tsdp_message_get_headerAt(self, type, 0);
}
const tsdp_header_A_t* tsdp_message_get_headerA_at(const tsdp_message_t* self, const char* field, tsk_size_t index)
{
tsk_size_t pos = 0;
const tsk_list_item_t *item;
const tsdp_header_t *hdr;
const tsdp_header_A_t *hdrA;
if(!self || !self->headers){
return tsk_null;
}
tsk_list_foreach(item, self->headers){
hdr = item->data;
if((hdr->type == tsdp_htype_A) && (hdrA = (const tsdp_header_A_t *)hdr) && (tsk_striequals(hdrA->field, field))){
if(pos++ >= index){
return hdrA;
}
}
}
return tsk_null;
}
const tsdp_header_A_t* tsdp_message_get_headerA(const tsdp_message_t* self, const char* field)
{
return tsdp_message_get_headerA_at(self, field, 0);
}
const tsdp_header_t *tsdp_message_get_headerByName(const tsdp_message_t *self, char name)
{
if(self){
return tsk_list_find_object_by_pred(self->headers, __pred_find_header_by_name, &name);
}
else{
TSK_DEBUG_ERROR("Invalid parameter");
return tsk_null;
}
}
int tsdp_message_get_sess_version(const tsdp_message_t *self, uint32_t *version)
{
const tsdp_header_O_t* O;
if (!self || !version) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
if ((O = (const tsdp_header_O_t*)tsdp_message_get_header(self, tsdp_htype_O))) {
*version = O->sess_version;
return 0;
}
return -2;
}
int tsdp_message_serialize(const tsdp_message_t *self, tsk_buffer_t *output)
{
const tsk_list_item_t* item;
if(!self || !output){
return -1;
}
tsk_list_foreach(item, self->headers){
if(tsdp_header_serialize(TSDP_HEADER(item->data), output)){
// Abort?
}
}
return 0;
}
char* tsdp_message_tostring(const tsdp_message_t *self)
{
tsk_buffer_t* output = tsk_buffer_create_null();
char* ret = tsk_null;
if(!tsdp_message_serialize(self, output)){
ret = tsk_strndup(TSK_BUFFER_DATA(output), TSK_BUFFER_SIZE(output));
}
TSK_OBJECT_SAFE_FREE(output);
return ret;
}
tsdp_message_t* tsdp_message_create_empty(const char* addr, tsk_bool_t ipv6, uint32_t version)
{
tsdp_message_t* ret = 0;
if(!(ret = tsdp_message_create())){
return tsk_null;
}
/* RFC 3264 - 5 Generating the Initial Offer
The numeric value of the session id and version in the o line MUST be
representable with a 64 bit signed integer. The initial value of the version MUST be less than
(2**62)-1, to avoid rollovers.
*/
TSDP_MESSAGE_ADD_HEADER(ret, TSDP_HEADER_V_VA_ARGS(0));
TSDP_MESSAGE_ADD_HEADER(ret, TSDP_HEADER_O_VA_ARGS(
TSDP_LINE_O_USERNAME_DEFAULT,
TSDP_LINE_O_SESSION_ID_DEFAULT,
version,
"IN",
ipv6 ? "IP6" : "IP4",
addr));
/* RFC 3264 - 5 Generating the Initial Offer
The SDP "s=" line conveys the subject of the session, which is
reasonably defined for multicast, but ill defined for unicast. For
unicast sessions, it is RECOMMENDED that it consist of a single space
character (0x20) or a dash (-).
Unfortunately, SDP does not allow the "s=" line to be empty.
*/
TSDP_MESSAGE_ADD_HEADER(ret, TSDP_HEADER_S_VA_ARGS(TSDP_LINE_S_VALUE_DEFAULT));
/* RFC 3264 - 5 Generating the Initial Offer
The SDP "t=" line conveys the time of the session. Generally,
streams for unicast sessions are created and destroyed through
external signaling means, such as SIP. In that case, the "t=" line
SHOULD have a value of "0 0".
*/
TSDP_MESSAGE_ADD_HEADER(ret, TSDP_HEADER_T_VA_ARGS(0, 0));
return ret;
}
tsdp_message_t* tsdp_message_clone(const tsdp_message_t *self)
{
tsdp_message_t* clone = tsk_null;
tsk_list_item_t* item;
tsdp_header_t* header;
if(!self){
goto bail;
}
if((clone = tsdp_message_create())){
tsk_list_foreach(item, self->headers){
if((header = tsdp_header_clone(TSDP_HEADER(item->data)))){
tsk_list_push_back_data(clone->headers, (void**)&header);
}
}
}
bail:
return clone;
}
int tsdp_message_add_media(tsdp_message_t *self, const char* media, uint32_t port, const char* proto, ...)
{
va_list ap;
int ret;
va_start(ap, proto);
ret = tsdp_message_add_media_2(self, media, port, proto, &ap);
va_end(ap);
return ret;
}
int tsdp_message_add_media_2(tsdp_message_t *self, const char* media, uint32_t port, const char* proto, va_list *ap)
{
int ret = -1;
tsdp_header_M_t* m;
if(!self){
return -1;
}
if((m = tsdp_header_M_create(media, port, proto))){
__add_headers(m, ap);
ret = tsdp_message_add_header(self, TSDP_HEADER(m));
TSK_OBJECT_SAFE_FREE(m);
}
return ret;
}
int tsdp_message_remove_media(tsdp_message_t *self, const char* media)
{
if(!self || !media){
return 0;
}
tsk_list_remove_item_by_pred(self->headers, __pred_find_media_by_name, media);
return 0;
}
const tsdp_header_M_t* tsdp_message_find_media(const tsdp_message_t *self, const char* media)
{
if(self && media){
const tsk_list_item_t* item;
if((item = tsk_list_find_item_by_pred(self->headers, __pred_find_media_by_name, media))){
return TSDP_HEADER_M(item->data);
}
}
return tsk_null;
}
/* ================= 3GPP TS 34.610 :: Communication HOLD (HOLD) using IP Multimedia (IM) Core ================*/
int tsdp_message_hold(tsdp_message_t* self, const char* media)
{
tsdp_header_M_t* M;
const tsk_list_item_t* item;
if(!self){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
// 3GPP TS 34.610-900 - 4.5.2.1 Actions at the invoking UE
if((item = tsk_list_find_item_by_pred(self->headers, __pred_find_media_by_name, media))){
M = TSDP_HEADER_M(item->data);
tsdp_header_M_hold(M, tsk_true);
}
return 0;
}
int tsdp_message_resume(tsdp_message_t* self, const char* media)
{
tsdp_header_M_t* M;
const tsk_list_item_t* item;
if(!self){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
// 3GPP TS 34.610-900 - 4.5.2.1 Actions at the invoking UE
if((item = tsk_list_find_item_by_pred(self->headers, __pred_find_media_by_name, media))){
M = TSDP_HEADER_M(item->data);
tsdp_header_M_resume(M, tsk_true);
}
return 0;
}
tsk_bool_t tsdp_message_is_ice_enabled(const tsdp_message_t *self, tsk_size_t media_index)
{
if (self) {
const tsdp_header_A_t *A;
const tsdp_header_M_t *M;
tsk_bool_t have_ufrag = tsk_false, have_pwd = tsk_false, have_candidates = tsk_false;
tsk_size_t index0 = 0;
// session level attributes
if ((A = tsdp_message_get_headerA(self, "ice-ufrag"))) {
have_ufrag = tsk_true;
}
}
return -1;
}
tsdp_message_t* tsdp_message_create()
{
return tsk_object_new(tsdp_message_def_t);
}
/*== Add headers/fmt to the media line */
int __add_headers(tsdp_header_M_t* m, va_list *ap)
{
const tsk_object_def_t* objdef;
tsdp_header_t *header;
tsdp_fmt_t* fmt;
if(!m){
return -1;
}
while((objdef = va_arg(*ap, const tsk_object_def_t*))){
if(objdef == tsdp_fmt_def_t){
if((fmt = tsk_object_new_2(objdef, ap))){
tsk_list_push_back_data(m->FMTs, (void**)&fmt);
if ((A = tsdp_message_get_headerA(self, "ice-pwd"))) {
have_pwd = tsk_true;
}
while ((M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(self, tsdp_htype_M, index0))) {
if (index0 == media_index) {
if ((A = tsdp_header_M_findA(M, "ice-ufrag"))) {
have_ufrag = tsk_true;
}
if ((A = tsdp_header_M_findA(M, "ice-pwd"))) {
have_pwd = tsk_true;
}
have_candidates = (tsdp_header_M_findA_at(M, "candidate", 0) != tsk_null);
return have_ufrag && have_pwd && have_candidates;
}
}
else{
if((header = tsk_object_new_2(objdef, ap))){
tsdp_header_M_add(m, header);
TSK_OBJECT_SAFE_FREE(header);
++index0;
}
}
return tsk_false;
}
tsk_bool_t tsdp_message_is_ice_restart(const tsdp_message_t *self, tsk_size_t media_index)
{
// https://tools.ietf.org/html/rfc5245#section-9.1.1.1
if (self) {
const tsdp_header_C_t *C;
const tsdp_header_M_t *M;
tsk_size_t index0 = 0;
// Session level
if ((C = (const tsdp_header_C_t*)tsdp_message_get_header(self, tsdp_htype_C)) && C->addr){
if (tsk_striequals("0.0.0.0", C->addr)) {
return tsk_true;
}
}
// Media level
while ((M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(self, tsdp_htype_M, index0))) {
if (index0 == media_index) {
return (M->C && M->C->addr && tsk_striequals("0.0.0.0", M->C->addr));
}
}
}
return 0;
}
int tsdp_message_add_header(tsdp_message_t *self, const tsdp_header_t *hdr)
{
if(self && hdr){
tsdp_header_t *header = tsk_object_ref((void*)hdr);
tsk_list_push_ascending_data(self->headers, (void**)&header); // Very important: Headers MUST appear in a fixed order (see ranks def).
return 0;
}
return -1;
}
int tsdp_message_add_headers(tsdp_message_t *self, ...)
{
const tsk_object_def_t* objdef;
tsdp_header_t *header;
va_list ap;
if(!self){
return -1;
}
va_start(ap, self);
while((objdef = va_arg(ap, const tsk_object_def_t*))){
if((header = tsk_object_new_2(objdef, &ap))){
tsdp_message_add_header(self, header);
TSK_OBJECT_SAFE_FREE(header);
}
}
va_end(ap);
return 0;
}
const tsdp_header_t *tsdp_message_get_headerAt(const tsdp_message_t *self, tsdp_header_type_t type, tsk_size_t index)
{
tsk_size_t pos = 0;
const tsk_list_item_t *item;
const tsdp_header_t *hdr;
if(!self || !self->headers){
return tsk_null;
}
tsk_list_foreach(item, self->headers){
hdr = item->data;
if(hdr->type == type){
if(pos++ >= index){
return hdr;
}
}
}
return tsk_null;
}
const tsdp_header_t *tsdp_message_get_header(const tsdp_message_t *self, tsdp_header_type_t type)
{
return tsdp_message_get_headerAt(self, type, 0);
}
const tsdp_header_A_t* tsdp_message_get_headerA_at(const tsdp_message_t* self, const char* field, tsk_size_t index)
{
tsk_size_t pos = 0;
const tsk_list_item_t *item;
const tsdp_header_t *hdr;
const tsdp_header_A_t *hdrA;
if(!self || !self->headers){
return tsk_null;
}
tsk_list_foreach(item, self->headers){
hdr = item->data;
if((hdr->type == tsdp_htype_A) && (hdrA = (const tsdp_header_A_t *)hdr) && (tsk_striequals(hdrA->field, field))){
if(pos++ >= index){
return hdrA;
}
}
}
return tsk_null;
}
const tsdp_header_A_t* tsdp_message_get_headerA(const tsdp_message_t* self, const char* field)
{
return tsdp_message_get_headerA_at(self, field, 0);
}
const tsdp_header_t *tsdp_message_get_headerByName(const tsdp_message_t *self, char name)
{
if(self){
return tsk_list_find_object_by_pred(self->headers, __pred_find_header_by_name, &name);
}
else{
TSK_DEBUG_ERROR("Invalid parameter");
return tsk_null;
}
}
int tsdp_message_serialize(const tsdp_message_t *self, tsk_buffer_t *output)
{
const tsk_list_item_t* item;
if(!self || !output){
return -1;
}
tsk_list_foreach(item, self->headers){
if(tsdp_header_serialize(TSDP_HEADER(item->data), output)){
// Abort?
}
}
return 0;
}
char* tsdp_message_tostring(const tsdp_message_t *self)
{
tsk_buffer_t* output = tsk_buffer_create_null();
char* ret = tsk_null;
if(!tsdp_message_serialize(self, output)){
ret = tsk_strndup(TSK_BUFFER_DATA(output), TSK_BUFFER_SIZE(output));
}
TSK_OBJECT_SAFE_FREE(output);
return ret;
}
tsdp_message_t* tsdp_message_create_empty(const char* addr, tsk_bool_t ipv6, uint32_t version)
{
tsdp_message_t* ret = 0;
if(!(ret = tsdp_message_create())){
return tsk_null;
}
/* RFC 3264 - 5 Generating the Initial Offer
The numeric value of the session id and version in the o line MUST be
representable with a 64 bit signed integer. The initial value of the version MUST be less than
(2**62)-1, to avoid rollovers.
*/
TSDP_MESSAGE_ADD_HEADER(ret, TSDP_HEADER_V_VA_ARGS(0));
TSDP_MESSAGE_ADD_HEADER(ret, TSDP_HEADER_O_VA_ARGS(
TSDP_LINE_O_USERNAME_DEFAULT,
TSDP_LINE_O_SESSION_ID_DEFAULT,
version,
"IN",
ipv6 ? "IP6" : "IP4",
addr));
/* RFC 3264 - 5 Generating the Initial Offer
The SDP "s=" line conveys the subject of the session, which is
reasonably defined for multicast, but ill defined for unicast. For
unicast sessions, it is RECOMMENDED that it consist of a single space
character (0x20) or a dash (-).
Unfortunately, SDP does not allow the "s=" line to be empty.
*/
TSDP_MESSAGE_ADD_HEADER(ret, TSDP_HEADER_S_VA_ARGS(TSDP_LINE_S_VALUE_DEFAULT));
/* RFC 3264 - 5 Generating the Initial Offer
The SDP "t=" line conveys the time of the session. Generally,
streams for unicast sessions are created and destroyed through
external signaling means, such as SIP. In that case, the "t=" line
SHOULD have a value of "0 0".
*/
TSDP_MESSAGE_ADD_HEADER(ret, TSDP_HEADER_T_VA_ARGS(0, 0));
return ret;
}
tsdp_message_t* tsdp_message_clone(const tsdp_message_t *self)
{
tsdp_message_t* clone = tsk_null;
tsk_list_item_t* item;
tsdp_header_t* header;
if(!self){
goto bail;
}
if((clone = tsdp_message_create())){
tsk_list_foreach(item, self->headers){
if((header = tsdp_header_clone(TSDP_HEADER(item->data)))){
tsk_list_push_back_data(clone->headers, (void**)&header);
}
}
}
bail:
return clone;
}
int tsdp_message_add_media(tsdp_message_t *self, const char* media, uint32_t port, const char* proto, ...)
{
va_list ap;
int ret;
va_start(ap, proto);
ret = tsdp_message_add_media_2(self, media, port, proto, &ap);
va_end(ap);
return ret;
}
int tsdp_message_add_media_2(tsdp_message_t *self, const char* media, uint32_t port, const char* proto, va_list *ap)
{
int ret = -1;
tsdp_header_M_t* m;
if(!self){
return -1;
}
if((m = tsdp_header_M_create(media, port, proto))){
__add_headers(m, ap);
ret = tsdp_message_add_header(self, TSDP_HEADER(m));
TSK_OBJECT_SAFE_FREE(m);
}
return ret;
}
int tsdp_message_remove_media(tsdp_message_t *self, const char* media)
{
if(!self || !media){
return 0;
}
tsk_list_remove_item_by_pred(self->headers, __pred_find_media_by_name, media);
return 0;
}
const tsdp_header_M_t* tsdp_message_find_media(const tsdp_message_t *self, const char* media)
{
if(self && media){
const tsk_list_item_t* item;
if((item = tsk_list_find_item_by_pred(self->headers, __pred_find_media_by_name, media))){
return TSDP_HEADER_M(item->data);
}
}
return tsk_null;
}
/* ================= 3GPP TS 34.610 :: Communication HOLD (HOLD) using IP Multimedia (IM) Core ================*/
int tsdp_message_hold(tsdp_message_t* self, const char* media)
{
tsdp_header_M_t* M;
const tsk_list_item_t* item;
if(!self){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
// 3GPP TS 34.610-900 - 4.5.2.1 Actions at the invoking UE
if((item = tsk_list_find_item_by_pred(self->headers, __pred_find_media_by_name, media))){
M = TSDP_HEADER_M(item->data);
tsdp_header_M_hold(M, tsk_true);
}
return 0;
}
int tsdp_message_resume(tsdp_message_t* self, const char* media)
{
tsdp_header_M_t* M;
const tsk_list_item_t* item;
if(!self){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
// 3GPP TS 34.610-900 - 4.5.2.1 Actions at the invoking UE
if((item = tsk_list_find_item_by_pred(self->headers, __pred_find_media_by_name, media))){
M = TSDP_HEADER_M(item->data);
tsdp_header_M_resume(M, tsk_true);
}
return 0;
}
//=================================================================================================
// SDP object definition
//
static void* tsdp_message_ctor(void * self, va_list * app)
{
tsdp_message_t *message = self;
if(message){
message->headers = tsk_list_create();
}
return self;
}
static void* tsdp_message_dtor(void * self)
{
tsdp_message_t *message = self;
if(message){
TSK_OBJECT_SAFE_FREE(message->headers);
}
return self;
}
static const tsk_object_def_t tsdp_message_def_s =
{
sizeof(tsdp_message_t),
tsdp_message_ctor,
tsdp_message_dtor,
tsk_null,
};
const tsk_object_def_t *tsdp_message_def_t = &tsdp_message_def_s;
++index0;
}
}
return tsk_false;
}
//=================================================================================================
// SDP object definition
//
static void* tsdp_message_ctor(void * self, va_list * app)
{
tsdp_message_t *message = self;
if(message){
message->headers = tsk_list_create();
}
return self;
}
static void* tsdp_message_dtor(void * self)
{
tsdp_message_t *message = self;
if(message){
TSK_OBJECT_SAFE_FREE(message->headers);
}
return self;
}
static const tsk_object_def_t tsdp_message_def_s =
{
sizeof(tsdp_message_t),
tsdp_message_ctor,
tsdp_message_dtor,
tsk_null,
};
const tsk_object_def_t *tsdp_message_def_t = &tsdp_message_def_s;