},
{ "call", lpc_cmd_call, "Call a SIP uri or number",
#ifdef VIDEO_ENABLED
- "'call <sip-url or number> [--audio-only]' \t: initiate a call to the specified destination.\n"
+ "'call <sip-url or number> [options]' \t: initiate a call to the specified destination.\n"
+ "Options can be:\n"
+ "--audio-only : initiate the call without video.\n"
+ "--early-media : sends audio and video stream immediately when remote proposes early media.\n"
#else
"'call <sip-url or number>' \t: initiate a call to the specified destination.\n"
#endif
{
LinphoneCall *call;
LinphoneCallParams *cp=linphone_core_create_default_call_parameters (lc);
- char *opt;
+ char *opt1,*opt2;
if ( linphone_core_in_call(lc) )
{
linphonec_out("Terminate or hold on the current call first.\n");
return 1;
}
- opt=strstr(args,"--audio-only");
- if (opt){
- opt[0]='\0';
+ opt1=strstr(args,"--audio-only");
+ opt2=strstr(args,"--early-media");
+ if (opt1){
+ opt1[0]='\0';
linphone_call_params_enable_video (cp,FALSE);
}
+ if (opt2){
+ opt2[0]='\0';
+ linphone_call_params_enable_early_media_sending(cp,TRUE);
+ }
if ( NULL == (call=linphone_core_invite_with_params(lc, args,cp)) )
{
linphonec_out("Error from linphone_core_invite.\n");
break;
case LinphoneCallOutgoingInit:
linphonec_call_identify(call);
+ id=(long)linphone_call_get_user_pointer (call);
+ from=linphone_call_get_remote_address_as_string(call);
+ linphonec_out("Establishing call id to %s, assigned id %i\n", from,id);
break;
case LinphoneCallUpdatedByRemote:
linphonec_call_updated(call);
break;
+ case LinphoneCallOutgoingProgress:
+ linphonec_out("Call %i to %s in progress.\n", id, from);
+ break;
+ case LinphoneCallOutgoingRinging:
+ linphonec_out("Call %i to %s ringing.\n", id, from);
+ break;
+ case LinphoneCallConnected:
+ linphonec_out("Call %i with %s connected.\n", id, from);
+ break;
+ case LinphoneCallOutgoingEarlyMedia:
+ linphonec_out("Call %i with %s early media.\n", id, from);
+ break;
+ case LinphoneCallError:
+ linphonec_out("Call %i with %s error.\n", id, from);
+ break;
default:
break;
}
static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details);
-static void linphone_connect_incoming(LinphoneCore *lc, LinphoneCall *call){
+void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md){
+ SalMediaDescription *oldmd=call->resultdesc;
+
if (lc->ringstream!=NULL){
ring_stop(lc->ringstream);
lc->ringstream=NULL;
}
- linphone_call_start_media_streams(call);
+ if (new_md!=NULL){
+ sal_media_description_ref(new_md);
+ call->media_pending=FALSE;
+ }else{
+ call->media_pending=TRUE;
+ }
+ call->resultdesc=new_md;
+ if (call->audiostream && call->audiostream->ticker){
+ /* we already started media: check if we really need to restart it*/
+ if (oldmd){
+ if (sal_media_description_equals(oldmd,new_md)){
+ sal_media_description_unref(oldmd);
+ if (call->all_muted){
+ ms_message("Early media finished, unmuting inputs...");
+ /*we were in early media, now we want to enable real media */
+ linphone_call_enable_camera (call,linphone_call_camera_enabled (call));
+ if (call->audiostream)
+ linphone_core_mute_mic (lc, linphone_core_is_mic_muted(lc));
+ }
+ ms_message("No need to restart streams, SDP is unchanged.");
+ return;
+ }else{
+ ms_message("Media descriptions are different, need to restart the streams.");
+ }
+ }
+ linphone_call_stop_media_streams (call);
+ linphone_call_init_media_streams (call);
+ }
+ if (oldmd)
+ sal_media_description_unref(oldmd);
+
+ if (new_md) {
+ bool_t all_muted=FALSE;
+ bool_t send_ringbacktone=FALSE;
+
+ if (call->audiostream==NULL){
+ /*this happens after pausing the call locally. The streams is destroyed and then we wait the 200Ok to recreate it*/
+ linphone_call_init_media_streams (call);
+ }
+ if (call->state==LinphoneCallIncomingEarlyMedia ||
+ (call->state==LinphoneCallOutgoingEarlyMedia && !call->params.real_early_media)){
+ all_muted=TRUE;
+ }else if (call->state==LinphoneCallIncomingEarlyMedia && linphone_core_get_remote_ringback_tone (lc)!=NULL){
+ send_ringbacktone=TRUE;
+ }
+ linphone_call_start_media_streams(call,all_muted,send_ringbacktone);
+ }
}
static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, const LinphoneAddress *to){
char *tmp;
LinphoneAddress *from_parsed;
LinphoneAddress *from_addr, *to_addr;
- const char * early_media=linphone_core_get_remote_ringback_tone (lc);
+ SalMediaDescription *md;
+ bool_t propose_early_media=lp_config_get_int(lc->config,"sip","incoming_calls_early_media",FALSE);
+ const char *ringback_tone=linphone_core_get_remote_ringback_tone (lc);
/* first check if we can answer successfully to this invite */
if (lc->presence_mode==LinphoneStatusBusy ||
call=linphone_call_new_incoming(lc,from_addr,to_addr,h);
sal_call_set_local_media_description(h,call->localdesc);
- call->resultdesc=sal_call_get_final_media_description(h);
- if (call->resultdesc)
- sal_media_description_ref(call->resultdesc);
- if (call->resultdesc && sal_media_description_empty(call->resultdesc)){
+ md=sal_call_get_final_media_description(h);
+
+ if (md && sal_media_description_empty(md)){
sal_call_decline(h,SalReasonMedia,NULL);
linphone_call_unref(call);
return;
}
-
/* the call is acceptable so we can now add it to our list */
linphone_core_add_call(lc,call);
}else{
/*TODO : play a tone within the context of the current call */
}
- sal_call_notify_ringing(h,early_media!=NULL);
-#if !(__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
- linphone_call_init_media_streams(call);
- if (early_media!=NULL){
- linphone_call_start_early_media (call);
+ linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call");
+
+ sal_call_notify_ringing(h,propose_early_media || ringback_tone!=NULL);
+
+ if (propose_early_media || ringback_tone!=NULL){
+ linphone_call_set_state(call,LinphoneCallIncomingEarlyMedia,"Incoming call early media");
+ linphone_core_update_streams(lc,call,md);
}
-#endif
ms_free(barmesg);
ms_free(tmp);
- linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call");
+
if (sal_call_get_replaces(call->op)!=NULL && lp_config_get_int(lc->config,"sip","auto_answer_replacing_calls",1)){
linphone_core_accept_call(lc,call);
}
ms_message("Early media already started.");
return;
}
- sal_media_description_ref(md);
- call->resultdesc=md;
if (lc->vtable.show) lc->vtable.show(lc);
if (lc->vtable.display_status)
lc->vtable.display_status(lc,_("Early media."));
lc->ringstream=NULL;
}
ms_message("Doing early media...");
- linphone_call_start_media_streams(call);
- call->media_pending=TRUE;
+ linphone_core_update_streams (lc,call,md);
}
}
static void call_accepted(SalOp *op){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
-
+ SalMediaDescription *md;
+
if (call==NULL){
ms_warning("No call to accept.");
return ;
}
- if ((call->audiostream!=NULL) && (call->audiostream->ticker!=NULL)){
- /*case where we accepted early media or already in call*/
- linphone_call_stop_media_streams(call);
- }
- if (call->audiostream==NULL){
- linphone_call_init_media_streams(call);
- }
- if (call->resultdesc)
- sal_media_description_unref(call->resultdesc);
- call->resultdesc=sal_call_get_final_media_description(op);
- if (call->resultdesc){
- sal_media_description_ref(call->resultdesc);
- call->media_pending=FALSE;
- }
+
+ md=sal_call_get_final_media_description(op);
+
if (call->state==LinphoneCallOutgoingProgress ||
call->state==LinphoneCallOutgoingRinging ||
call->state==LinphoneCallOutgoingEarlyMedia){
linphone_call_set_state(call,LinphoneCallConnected,"Connected");
}
- if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){
- if (sal_media_description_has_dir(call->resultdesc,SalStreamSendOnly) ||
- sal_media_description_has_dir(call->resultdesc,SalStreamInactive)){
+ if (md && !sal_media_description_empty(md)){
+ if (sal_media_description_has_dir(md,SalStreamSendOnly) ||
+ sal_media_description_has_dir(md,SalStreamInactive)){
if (lc->vtable.display_status){
char *tmp=linphone_call_get_remote_address_as_string (call);
char *msg=ms_strdup_printf(_("Call with %s is paused."),tmp);
ms_free(msg);
}
linphone_call_set_state(call,LinphoneCallPaused,"Call paused");
- }else if (sal_media_description_has_dir(call->resultdesc,SalStreamRecvOnly)){
+ }else if (sal_media_description_has_dir(md,SalStreamRecvOnly)){
/*we are put on hold when the call is initially accepted */
if (lc->vtable.display_status){
char *tmp=linphone_call_get_remote_address_as_string (call);
}
linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)");
}
- linphone_connect_incoming (lc,call);
+ linphone_core_update_streams (lc,call,md);
}else{
/*send a bye*/
ms_error("Incompatible SDP offer received in 200Ok, need to abort the call");
return ;
}
if (call->media_pending){
- if (call->audiostream->ticker!=NULL){
- /*case where we accepted early media */
- linphone_call_stop_media_streams(call);
- linphone_call_init_media_streams(call);
- }
- if (call->resultdesc)
- sal_media_description_unref(call->resultdesc);
- call->resultdesc=sal_call_get_final_media_description(op);
- if (call->resultdesc)
- sal_media_description_ref(call->resultdesc);
- if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){
- linphone_connect_incoming(lc,call);
+ SalMediaDescription *md=sal_call_get_final_media_description(op);
+ if (md && !sal_media_description_empty(md)){
+ linphone_core_update_streams (lc,call,md);
linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)");
}else{
/*send a bye*/
linphone_core_abort_call(lc,call,"No codec intersection");
return;
}
- call->media_pending=FALSE;
}
}
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
LinphoneCallState prevstate=LinphoneCallIdle;
+ SalMediaDescription *md;
- if (call->resultdesc)
- sal_media_description_unref(call->resultdesc);
- call->resultdesc=sal_call_get_final_media_description(op);
- if (call->resultdesc)
- sal_media_description_ref(call->resultdesc);
-
- if (call->resultdesc && !sal_media_description_empty(call->resultdesc))
+ md=sal_call_get_final_media_description(op);
+
+ if (md && !sal_media_description_empty(md))
{
if ((call->state==LinphoneCallPausedByRemote || call->state==LinphoneCallPaused) &&
- sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv) && strcmp(call->resultdesc->addr,"0.0.0.0")!=0){
+ sal_media_description_has_dir(md,SalStreamSendRecv) && strcmp(md->addr,"0.0.0.0")!=0){
/*make sure we can be resumed */
if (lc->current_call!=NULL && lc->current_call!=call){
ms_warning("Attempt to be resumed but already in call with somebody else!");
linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)");
}
else if(call->state==LinphoneCallStreamsRunning &&
- ( sal_media_description_has_dir(call->resultdesc,SalStreamRecvOnly)
- || sal_media_description_has_dir(call->resultdesc,SalStreamInactive)
- || strcmp(call->resultdesc->addr,"0.0.0.0")==0)){
+ ( sal_media_description_has_dir(md,SalStreamRecvOnly)
+ || sal_media_description_has_dir(md,SalStreamInactive)
+ || strcmp(md->addr,"0.0.0.0")==0)){
if(lc->vtable.display_status)
lc->vtable.display_status(lc,_("We are being paused..."));
linphone_call_set_state (call,LinphoneCallPausedByRemote,"Call paused by remote");
}
/*accept the modification (sends a 200Ok)*/
sal_call_accept(op);
- linphone_call_stop_media_streams (call);
- linphone_call_init_media_streams (call);
- linphone_call_start_media_streams (call);
+ linphone_core_update_streams (lc,call,md);
if (prevstate!=LinphoneCallIdle){
linphone_call_set_state (call,prevstate,"Connected (streams running)");
}
return "LinphoneCallPausedByRemote";
case LinphoneCallUpdatedByRemote:
return "LinphoneCallUpdatedByRemote";
+ case LinphoneCallIncomingEarlyMedia:
+ return "LinphoneCallIncomingEarlyMedia";
}
return "undefined state";
}
return cp->has_video;
}
+/**
+ * Enable sending of real early media (during outgoing calls).
+**/
+void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled){
+ cp->real_early_media=enabled;
+}
+
+bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp){
+ return cp->real_early_media;
+}
+
/**
*
**/
float ng_floorgain=lp_config_get_float(lc->config,"sound","ng_floorgain",0);
int dc_removal=lp_config_get_int(lc->config,"sound","dc_removal",0);
- if (mic_gain!=-1)
+ if (!call->audio_muted)
audio_stream_set_mic_gain(st,mic_gain);
- call->audio_muted=FALSE;
+ else
+ audio_stream_set_mic_gain(st,0);
recv_gain = lc->sound_conf.soft_play_lev;
if (recv_gain != 0) {
return prof;
}
+
static void setup_ring_player(LinphoneCore *lc, LinphoneCall *call){
int pause_time=3000;
audio_stream_play(call->audiostream,lc->sound_conf.ringback_tone);
ms_filter_call_method(call->audiostream->soundread,MS_FILE_PLAYER_LOOP,&pause_time);
}
-static void _linphone_call_start_media_streams(LinphoneCall *call, bool_t send_early_media){
+
+void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone){
LinphoneCore *lc=call->core;
LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc);
const char *tool="linphone-" LINPHONE_VERSION;
char *cname;
int used_pt=-1;
+
if(call->audiostream == NULL)
{
ms_fatal("start_media_stream() called without prior init !");
if (stream->port==0 || stream->dir==SalStreamRecvOnly){
captcard=NULL;
playfile=NULL;
- }else if (stream->dir==SalStreamSendOnly || send_early_media){
+ }else if (stream->dir==SalStreamSendOnly){
playcard=NULL;
captcard=NULL;
recfile=NULL;
- if (send_early_media)
- playfile=NULL;
+ if (!send_ringbacktone) playfile=NULL;
}
/*if playfile are supplied don't use soundcards*/
if (lc->use_files) {
captcard,
captcard==NULL ? FALSE : linphone_core_echo_cancellation_enabled(lc));
post_configure_audio_streams(call);
- if (send_early_media) setup_ring_player(lc,call);
+ if (all_inputs_muted){
+ audio_stream_set_mic_gain(call->audiostream,0);
+ }
+ if (send_ringbacktone){
+ setup_ring_player(lc,call);
+ }
audio_stream_set_rtcp_information(call->audiostream, cname, tool);
}else ms_warning("No audio stream accepted ?");
}
}
#ifdef VIDEO_ENABLED
- if (!send_early_media){
+ {
const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc,
SalProtoRtpAvp,SalVideo);
used_pt=-1;
/*either inactive or incompatible with local capabilities*/
is_inactive=TRUE;
}
- if (call->camera_active==FALSE){
+ if (call->camera_active==FALSE || all_inputs_muted){
cam=get_nowebcam_device();
}
if (!is_inactive){
}
}
#endif
+ call->all_muted=all_inputs_muted;
+
goto end;
end:
ms_free(cname);
linphone_address_destroy(me);
}
-
-void linphone_call_start_media_streams(LinphoneCall *call){
- _linphone_call_start_media_streams(call,FALSE);
-}
-
-void linphone_call_start_early_media(LinphoneCall *call){
- _linphone_call_start_media_streams(call,TRUE);
-}
-
static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){
audio_stream_get_local_rtp_stats (st,&log->local_stats);
}
gain=lp_config_get_float(lc->config,"sound","playback_gain_db",0);
linphone_core_set_playback_gain_db (lc,gain);
-
+/*
linphone_core_set_remote_ringback_tone (lc,lp_config_get_string(lc->config,"sound","ringback_tone",NULL));
+ */
}
static void sip_config_read(LinphoneCore *lc)
LinphoneProxyConfig *cfg=NULL;
const char *contact=NULL;
SalOp *replaced;
+ SalMediaDescription *new_md;
if (call==NULL){
//if just one call is present answer the only one ...
contact=get_fixed_contact(lc,call,cfg);
if (contact)
sal_op_set_contact(call->op,contact);
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 40000
- linphone_call_init_media_streams(call);
-#else
- if (call->audiostream!=NULL && call->audiostream->ticker!=NULL){
- /*case where we sent early media*/
- linphone_call_stop_media_streams (call);
- linphone_call_init_media_streams (call);
- }
-#endif
+
+ if (call->audiostream==NULL)
+ linphone_call_init_media_streams(call);
+
sal_call_accept(call->op);
if (lc->vtable.display_status!=NULL)
lc->vtable.display_status(lc,_("Connected."));
lc->current_call=call;
linphone_call_set_state(call,LinphoneCallConnected,"Connected");
- call->resultdesc=sal_call_get_final_media_description(call->op);
- if (call->resultdesc){
- linphone_call_start_media_streams(call);
+ new_md=sal_call_get_final_media_description(call->op);
+ linphone_core_update_streams(lc, call, new_md);
+ if (new_md){
linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)");
- sal_media_description_ref(call->resultdesc);
}else call->media_pending=TRUE;
+
ms_message("call answered.");
return 0;
}
}
bool_t linphone_core_is_mic_muted(LinphoneCore *lc) {
- float gain=1.0;
LinphoneCall *call=linphone_core_get_current_call(lc);
if (call==NULL){
ms_warning("linphone_core_is_mic_muted(): No current call !");
return FALSE;
}
- if (call->audiostream && call->audiostream->volsend){
- ms_filter_call_method(call->audiostream->volsend,MS_VOLUME_GET_GAIN,&gain);
- }else ms_warning("Could not get gain: gain control wasn't activated. ");
-
- return gain==0 || call->audio_muted;
+ return call->audio_muted;
}
// returns rtp transmission status for an active stream
LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp);
void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled);
bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp);
+void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled);
+bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp);
void linphone_call_params_destroy(LinphoneCallParams *cp);
/**
LinphoneCallError, /**<The call encountered an error*/
LinphoneCallEnd, /**<The call ended normally*/
LinphoneCallPausedByRemote, /**<The call is paused by remote end*/
- LinphoneCallUpdatedByRemote /**<The call's parameters are updated, used for example when video is asked by remote */
+ LinphoneCallUpdatedByRemote, /**<The call's parameters are updated, used for example when video is asked by remote */
+ LinphoneCallIncomingEarlyMedia /**<We are proposing early media to an incoming call */
} LinphoneCallState;
const char *linphone_call_state_to_string(LinphoneCallState cs);
const char *linphone_core_get_ring(const LinphoneCore *lc);
void linphone_core_set_ringback(LinphoneCore *lc, const char *path);
const char * linphone_core_get_ringback(const LinphoneCore *lc);
+
void linphone_core_set_remote_ringback_tone(LinphoneCore *lc,const char *);
const char *linphone_core_get_remote_ringback_tone(const LinphoneCore *lc);
+
int linphone_core_preview_ring(LinphoneCore *lc, const char *ring,LinphoneCoreCbFunc func,void * userdata);
void linphone_core_enable_echo_cancellation(LinphoneCore *lc, bool_t val);
bool_t linphone_core_echo_cancellation_enabled(LinphoneCore *lc);
struct _LinphoneCallParams{
LinphoneCall *referer; /*in case this call creation is consecutive to an incoming transfer, this points to the original call */
bool_t has_video;
- bool_t pad[3];
+ bool_t real_early_media; /*send real media even during early media (for outgoing calls)*/
+ bool_t pad[2];
};
struct _LinphoneCall
bool_t media_pending;
bool_t audio_muted;
bool_t camera_active;
+ bool_t all_muted; /*this flag is set during early medias*/
};
void linphone_core_text_received(LinphoneCore *lc, const char *from, const char *msg);
void linphone_call_init_media_streams(LinphoneCall *call);
-void linphone_call_start_media_streams(LinphoneCall *call);
-void linphone_call_start_early_media(LinphoneCall *call);
+void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone);
void linphone_call_stop_media_streams(LinphoneCall *call);
const char * linphone_core_get_identity(LinphoneCore *lc);
SalMediaDescription *create_local_media_description(LinphoneCore *lc,
LinphoneCall *call, bool_t with_video, bool_t only_one_codec);
-
+void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md);
#define linphone_core_ready(lc) ((lc)->state!=LinphoneGlobalStartup)
void _linphone_core_configure_resolver();
return found;
}
+/*
+static bool_t fmtp_equals(const char *p1, const char *p2){
+ if (p1 && p2 && strcmp(p1,p2)==0) return TRUE;
+ if (p1==NULL && p2==NULL) return TRUE;
+ return FALSE;
+}
+*/
+
+static bool_t payload_type_equals(const PayloadType *p1, const PayloadType *p2){
+ if (p1->type!=p2->type) return FALSE;
+ if (strcmp(p1->mime_type,p2->mime_type)!=0) return FALSE;
+ if (p1->clock_rate!=p2->clock_rate) return FALSE;
+ if (p1->channels!=p2->channels) return FALSE;
+ /*
+ Do not compare fmtp right now: they are modified internally when the call is started
+ */
+ /*
+ if (!fmtp_equals(p1->recv_fmtp,p2->recv_fmtp) ||
+ !fmtp_equals(p1->send_fmtp,p2->send_fmtp))
+ return FALSE;
+ */
+ return TRUE;
+}
+
+static bool_t payload_list_equals(const MSList *l1, const MSList *l2){
+ const MSList *e1,*e2;
+ for(e1=l1,e2=l2;e1!=NULL && e2!=NULL; e1=e1->next,e2=e2->next){
+ PayloadType *p1=(PayloadType*)e1->data;
+ PayloadType *p2=(PayloadType*)e2->data;
+ if (!payload_type_equals(p1,p2))
+ return FALSE;
+ }
+ if (e1!=NULL || e2!=NULL){
+ /*means one list is longer than the other*/
+ abort();
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool_t sal_stream_description_equals(const SalStreamDescription *sd1, const SalStreamDescription *sd2){
+ if (sd1->proto!=sd2->proto) return FALSE;
+ if (sd1->type!=sd2->type) return FALSE;
+ if (strcmp(sd1->addr,sd2->addr)!=0) return FALSE;
+ if (sd1->port!=sd2->port) return FALSE;
+ if (!payload_list_equals(sd1->payloads,sd2->payloads)) return FALSE;
+ if (sd1->bandwidth!=sd2->bandwidth) return FALSE;
+ if (sd1->ptime!=sd2->ptime) return FALSE;
+ /* compare candidates: TODO */
+ if (sd1->dir!=sd2->dir) return FALSE;
+ return TRUE;
+}
+
+bool_t sal_media_description_equals(const SalMediaDescription *md1, const SalMediaDescription *md2){
+ int i;
+
+ if (strcmp(md1->addr,md2->addr)!=0) return FALSE;
+ if (md1->nstreams!=md2->nstreams) return FALSE;
+ if (md1->bandwidth!=md2->bandwidth) return FALSE;
+ for(i=0;i<md1->nstreams;++i){
+ if (!sal_stream_description_equals(&md1->streams[i],&md2->streams[i]))
+ return FALSE;
+ }
+ return TRUE;
+}
+
static void assign_string(char **str, const char *arg){
if (*str){
ms_free(*str);
void sal_media_description_ref(SalMediaDescription *md);
void sal_media_description_unref(SalMediaDescription *md);
bool_t sal_media_description_empty(const SalMediaDescription *md);
+bool_t sal_media_description_equals(const SalMediaDescription *md1, const SalMediaDescription *md2);
bool_t sal_media_description_has_dir(const SalMediaDescription *md, SalStreamDir dir);
SalStreamDescription *sal_media_description_find_stream(SalMediaDescription *md,
SalMediaProto proto, SalStreamType type);
-Subproject commit ffacf56718c198cb80a290f7a65975916d8a9b6b
+Subproject commit b1a4c2b6470a6de62a8e58d4f2874eda091a8db3