Make media change detection stronger and smarter
This commit is contained in:
parent
7ffe7eba0b
commit
493815f88e
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue