forked from mirrors/linphone-iphone
implement early media forking at client side
This commit is contained in:
parent
85df75c119
commit
10c9de93ca
7 changed files with 187 additions and 29 deletions
|
|
@ -152,11 +152,12 @@ static void process_dialog_terminated(void *ctx, const belle_sip_dialog_terminat
|
|||
static void handle_sdp_from_response(SalOp* op,belle_sip_response_t* response) {
|
||||
belle_sdp_session_description_t* sdp;
|
||||
SalReason reason;
|
||||
if (op->base.remote_media){
|
||||
sal_media_description_unref(op->base.remote_media);
|
||||
op->base.remote_media=NULL;
|
||||
}
|
||||
if (extract_sdp(BELLE_SIP_MESSAGE(response),&sdp,&reason)==0) {
|
||||
if (sdp){
|
||||
if (op->base.remote_media){
|
||||
sal_media_description_unref(op->base.remote_media);
|
||||
}
|
||||
op->base.remote_media=sal_media_description_new();
|
||||
sdp_to_media_description(sdp,op->base.remote_media);
|
||||
if (op->base.local_media) sdp_process(op);
|
||||
|
|
@ -177,6 +178,7 @@ static int vfu_retry (void *user_data, unsigned int events) {
|
|||
sal_op_unref(op);
|
||||
return BELLE_SIP_STOP;
|
||||
}
|
||||
|
||||
static void call_process_response(void *op_base, const belle_sip_response_event_t *event){
|
||||
SalOp* op = (SalOp*)op_base;
|
||||
belle_sip_request_t* ack;
|
||||
|
|
@ -186,17 +188,17 @@ static void call_process_response(void *op_base, const belle_sip_response_event_
|
|||
belle_sip_response_t* response=belle_sip_response_event_get_response(event);
|
||||
int code = belle_sip_response_get_status_code(response);
|
||||
belle_sip_header_content_type_t *header_content_type=NULL;
|
||||
|
||||
belle_sip_dialog_t *dialog=belle_sip_response_event_get_dialog(event);
|
||||
|
||||
if (!client_transaction) {
|
||||
ms_warning("Discarding stateless response [%i] on op [%p]",code,op);
|
||||
return;
|
||||
}
|
||||
req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction));
|
||||
set_or_update_dialog(op,belle_sip_response_event_get_dialog(event));
|
||||
dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL;
|
||||
set_or_update_dialog(op,dialog);
|
||||
dialog_state=dialog ? belle_sip_dialog_get_state(dialog) : BELLE_SIP_DIALOG_NULL;
|
||||
|
||||
ms_message("Op [%p] receiving call response [%i], dialog is [%p] in state [%s]",op,code,op->dialog,belle_sip_dialog_state_to_string(dialog_state));
|
||||
ms_message("Op [%p] receiving call response [%i], dialog is [%p] in state [%s]",op,code,dialog,belle_sip_dialog_state_to_string(dialog_state));
|
||||
|
||||
switch(dialog_state) {
|
||||
case BELLE_SIP_DIALOG_NULL:
|
||||
|
|
@ -218,14 +220,18 @@ static void call_process_response(void *op_base, const belle_sip_response_event_
|
|||
if (op->dialog==NULL) call_set_released(op);
|
||||
}
|
||||
}
|
||||
} else if (code >= 180 && code<300) {
|
||||
handle_sdp_from_response(op,response);
|
||||
op->base.root->callbacks.call_ringing(op);
|
||||
} else if (code >= 180 && code<200) {
|
||||
belle_sip_response_t *prev_response=belle_sip_object_data_get(BELLE_SIP_OBJECT(dialog),"early_response");
|
||||
if (!prev_response || code>belle_sip_response_get_status_code(prev_response)){
|
||||
handle_sdp_from_response(op,response);
|
||||
op->base.root->callbacks.call_ringing(op);
|
||||
}
|
||||
belle_sip_object_data_set(BELLE_SIP_OBJECT(dialog),"early_response",belle_sip_object_ref(response),belle_sip_object_unref);
|
||||
} else if (code>=300){
|
||||
call_set_error(op,response);
|
||||
if (op->dialog==NULL) call_set_released(op);
|
||||
}
|
||||
} else if ( code >=200
|
||||
} else if (code >=200
|
||||
&& code<300
|
||||
&& strcmp("UPDATE",belle_sip_request_get_method(req))==0) {
|
||||
handle_sdp_from_response(op,response);
|
||||
|
|
|
|||
|
|
@ -560,20 +560,28 @@ const SalErrorInfo *sal_op_get_error_info(const SalOp *op){
|
|||
return &op->error_info;
|
||||
}
|
||||
|
||||
static void unlink_op_with_dialog(SalOp *op, belle_sip_dialog_t* dialog){
|
||||
belle_sip_dialog_set_application_data(dialog,NULL);
|
||||
sal_op_unref(op);
|
||||
belle_sip_object_unref(dialog);
|
||||
}
|
||||
|
||||
static belle_sip_dialog_t *link_op_with_dialog(SalOp *op, belle_sip_dialog_t* dialog){
|
||||
belle_sip_dialog_set_application_data(dialog,sal_op_ref(op));
|
||||
belle_sip_object_ref(dialog);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
void set_or_update_dialog(SalOp* op, belle_sip_dialog_t* dialog) {
|
||||
/*check if dialog has changed*/
|
||||
if (dialog && dialog != op->dialog) {
|
||||
ms_message("Dialog set from [%p] to [%p] for op [%p]",op->dialog,dialog,op);
|
||||
/*fixme, shouldn't we cancel previous dialog*/
|
||||
if (op->dialog) {
|
||||
belle_sip_dialog_set_application_data(op->dialog,NULL);
|
||||
belle_sip_object_unref(op->dialog);
|
||||
sal_op_unref(op);
|
||||
if (dialog==NULL) return;
|
||||
ms_message("op [%p] : set_or_update_dialog() current=[%p] new=[%p]",op,op->dialog,dialog);
|
||||
if (dialog && op->dialog!=dialog){
|
||||
if (op->dialog){
|
||||
/*FIXME: shouldn't we delete unconfirmed dialogs ?*/
|
||||
unlink_op_with_dialog(op,op->dialog);
|
||||
op->dialog=NULL;
|
||||
}
|
||||
op->dialog=dialog;
|
||||
belle_sip_dialog_set_application_data(op->dialog,op);
|
||||
sal_op_ref(op);
|
||||
belle_sip_object_ref(op->dialog);
|
||||
op->dialog=link_op_with_dialog(op,dialog);
|
||||
}
|
||||
}
|
||||
/*return reffed op*/
|
||||
|
|
|
|||
|
|
@ -72,6 +72,32 @@ void linphone_core_update_streams_destinations(LinphoneCore *lc, LinphoneCall *c
|
|||
#endif
|
||||
}
|
||||
|
||||
static void _clear_early_media_destinations(LinphoneCall *call, MediaStream *ms){
|
||||
RtpSession *session=ms->sessions.rtp_session;
|
||||
rtp_session_clear_aux_remote_addr(session);
|
||||
if (!call->ice_session) rtp_session_set_symmetric_rtp(session,TRUE);/*restore symmetric rtp if ICE is not used*/
|
||||
}
|
||||
|
||||
static void clear_early_media_destinations(LinphoneCall *call){
|
||||
if (call->audiostream){
|
||||
_clear_early_media_destinations(call,(MediaStream*)call->audiostream);
|
||||
}
|
||||
if (call->videostream){
|
||||
_clear_early_media_destinations(call,(MediaStream*)call->videostream);
|
||||
}
|
||||
}
|
||||
|
||||
static void prepare_early_media_forking(LinphoneCall *call){
|
||||
/*we need to disable symmetric rtp otherwise our outgoing streams will be switching permanently between the multiple destinations*/
|
||||
if (call->audiostream){
|
||||
rtp_session_set_symmetric_rtp(call->audiostream->ms.sessions.rtp_session,FALSE);
|
||||
}
|
||||
if (call->videostream){
|
||||
rtp_session_set_symmetric_rtp(call->videostream->ms.sessions.rtp_session,FALSE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md){
|
||||
SalMediaDescription *oldmd=call->resultdesc;
|
||||
bool_t all_muted=FALSE;
|
||||
|
|
@ -98,6 +124,7 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
|
|||
call->expect_media_in_ack=FALSE;
|
||||
call->resultdesc=new_md;
|
||||
if ((call->audiostream && call->audiostream->ms.state==MSStreamStarted) || (call->videostream && call->videostream->ms.state==MSStreamStarted)){
|
||||
clear_early_media_destinations(call);
|
||||
/* we already started media: check if we really need to restart it*/
|
||||
if (oldmd){
|
||||
int md_changed = media_parameters_changed(call, oldmd, new_md);
|
||||
|
|
@ -146,6 +173,9 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
|
|||
if ((call->state==LinphoneCallIncomingEarlyMedia || call->state==LinphoneCallOutgoingEarlyMedia) && !call->params.real_early_media){
|
||||
all_muted=TRUE;
|
||||
}
|
||||
if (call->params.real_early_media && call->state==LinphoneCallOutgoingEarlyMedia){
|
||||
prepare_early_media_forking(call);
|
||||
}
|
||||
linphone_call_start_media_streams(call,all_muted,send_ringbacktone);
|
||||
if (call->state==LinphoneCallPausing && call->paused_by_app && ms_list_size(lc->calls)==1){
|
||||
linphone_core_play_named_tone(lc,LinphoneToneCallOnHold);
|
||||
|
|
@ -276,6 +306,38 @@ static void call_received(SalOp *h){
|
|||
linphone_core_notify_incoming_call(lc,call);
|
||||
}
|
||||
|
||||
static void try_early_media_forking(LinphoneCall *call, SalMediaDescription *md){
|
||||
SalMediaDescription *cur_md=call->resultdesc;
|
||||
int i;
|
||||
SalStreamDescription *ref_stream,*new_stream;
|
||||
ms_message("Early media response received from another branch, checking if media can be forked to this new destination.");
|
||||
|
||||
for (i=0;i<cur_md->n_active_streams;++i){
|
||||
ref_stream=&cur_md->streams[i];
|
||||
new_stream=&md->streams[i];
|
||||
if (ref_stream->type==new_stream->type && ref_stream->payloads && new_stream->payloads){
|
||||
PayloadType *refpt, *newpt;
|
||||
refpt=(PayloadType*)ref_stream->payloads->data;
|
||||
newpt=(PayloadType*)new_stream->payloads->data;
|
||||
if (strcmp(refpt->mime_type,newpt->mime_type)==0 && refpt->clock_rate==newpt->clock_rate
|
||||
&& payload_type_get_number(refpt)==payload_type_get_number(newpt)){
|
||||
MediaStream *ms=NULL;
|
||||
if (ref_stream->type==SalAudio){
|
||||
ms=(MediaStream*)call->audiostream;
|
||||
}else if (ref_stream->type==SalVideo){
|
||||
ms=(MediaStream*)call->videostream;
|
||||
}
|
||||
if (ms){
|
||||
RtpSession *session=ms->sessions.rtp_session;
|
||||
const char *rtp_addr=new_stream->rtp_addr[0]!='\0' ? new_stream->rtp_addr : md->addr;
|
||||
const char *rtcp_addr=new_stream->rtcp_addr[0]!='\0' ? new_stream->rtcp_addr : md->addr;
|
||||
rtp_session_add_aux_remote_addr_full(session,rtp_addr,new_stream->rtp_port,rtcp_addr,new_stream->rtcp_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void call_ringing(SalOp *h){
|
||||
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
|
||||
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(h);
|
||||
|
|
@ -309,7 +371,7 @@ static void call_ringing(SalOp *h){
|
|||
/*accept early media */
|
||||
if (call->audiostream && audio_stream_started(call->audiostream)){
|
||||
/*streams already started */
|
||||
ms_message("Early media already started.");
|
||||
try_early_media_forking(call,md);
|
||||
return;
|
||||
}
|
||||
if (lc->vtable.show) lc->vtable.show(lc);
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 916b6465a2f46883cb2ad17ccc7b8fc210f1a942
|
||||
Subproject commit e0ff32eebb29671e855cb3cfe9c0ea46929419b4
|
||||
2
oRTP
2
oRTP
|
|
@ -1 +1 @@
|
|||
Subproject commit ae0fec37506f4cc5de7dfc8ca84321b7ae311bbe
|
||||
Subproject commit a9ffd72d73d459e531fad094d18eb18f67870aba
|
||||
|
|
@ -1200,7 +1200,8 @@ static void call_waiting_indication_with_param(bool_t enable_caller_privacy) {
|
|||
|
||||
if (pauline_called_by_laure && enable_caller_privacy )
|
||||
CU_ASSERT_EQUAL(linphone_call_params_get_privacy(linphone_call_get_current_params(pauline_called_by_laure)),LinphonePrivacyId);
|
||||
|
||||
/*wait a bit for ACK to be sent*/
|
||||
wait_for_list(lcs,NULL,0,1000);
|
||||
linphone_core_terminate_all_calls(pauline->lc);
|
||||
|
||||
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,2000));
|
||||
|
|
@ -2162,6 +2163,86 @@ static void statistics_sent_at_call_termination() {
|
|||
}
|
||||
|
||||
#ifdef VIDEO_ENABLED
|
||||
/*this is call forking with early media managed at client side (not by flexisip server)*/
|
||||
static void multiple_early_media(void) {
|
||||
LinphoneCoreManager* marie1 = linphone_core_manager_new("marie_early_rc");
|
||||
LinphoneCoreManager* marie2 = linphone_core_manager_new("marie_early_rc");
|
||||
LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_tcp_rc");
|
||||
MSList *lcs=NULL;
|
||||
LinphoneCallParams *params=linphone_core_create_default_call_parameters(pauline->lc);
|
||||
LinphoneVideoPolicy pol;
|
||||
LinphoneCall *marie1_call;
|
||||
LinphoneCall *marie2_call;
|
||||
LinphoneCall *pauline_call;
|
||||
int dummy=0;
|
||||
char ringbackpath[256];
|
||||
snprintf(ringbackpath,sizeof(ringbackpath), "%s/sounds/hello8000.wav" /*use hello because rinback is too short*/, liblinphone_tester_file_prefix);
|
||||
|
||||
pol.automatically_accept=1;
|
||||
pol.automatically_initiate=1;
|
||||
|
||||
linphone_core_enable_video(pauline->lc,TRUE,TRUE);
|
||||
|
||||
linphone_core_enable_video(marie1->lc,TRUE,TRUE);
|
||||
linphone_core_set_video_policy(marie1->lc,&pol);
|
||||
/*use playfile for marie1 to avoid locking on capture card*/
|
||||
linphone_core_use_files(marie1->lc,TRUE);
|
||||
linphone_core_set_play_file(marie1->lc,ringbackpath);
|
||||
|
||||
linphone_core_enable_video(marie2->lc,TRUE,TRUE);
|
||||
linphone_core_set_video_policy(marie2->lc,&pol);
|
||||
linphone_core_set_audio_port_range(marie2->lc,40200,40300);
|
||||
linphone_core_set_video_port_range(marie2->lc,40400,40500);
|
||||
/*use playfile for marie2 to avoid locking on capture card*/
|
||||
linphone_core_use_files(marie2->lc,TRUE);
|
||||
linphone_core_set_play_file(marie2->lc,ringbackpath);
|
||||
|
||||
|
||||
lcs=ms_list_append(lcs,marie1->lc);
|
||||
lcs=ms_list_append(lcs,marie2->lc);
|
||||
lcs=ms_list_append(lcs,pauline->lc);
|
||||
|
||||
linphone_call_params_enable_early_media_sending(params,TRUE);
|
||||
linphone_call_params_enable_video(params,TRUE);
|
||||
|
||||
linphone_core_invite_address_with_params(pauline->lc,marie1->identity,params);
|
||||
linphone_call_params_destroy(params);
|
||||
|
||||
CU_ASSERT_TRUE(wait_for_list(lcs, &marie1->stat.number_of_LinphoneCallIncomingEarlyMedia,1,3000));
|
||||
CU_ASSERT_TRUE(wait_for_list(lcs, &marie2->stat.number_of_LinphoneCallIncomingEarlyMedia,1,3000));
|
||||
CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallOutgoingEarlyMedia,1,3000));
|
||||
|
||||
pauline_call=linphone_core_get_current_call(pauline->lc);
|
||||
marie1_call=linphone_core_get_current_call(marie1->lc);
|
||||
marie2_call=linphone_core_get_current_call(marie2->lc);
|
||||
|
||||
/*wait a bit that streams are established*/
|
||||
wait_for_list(lcs,&dummy,1,6000);
|
||||
CU_ASSERT_TRUE(linphone_call_get_audio_stats(pauline_call)->download_bandwidth>70);
|
||||
CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie1_call)->download_bandwidth>70);
|
||||
CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie2_call)->download_bandwidth>70);
|
||||
|
||||
linphone_core_accept_call(marie1->lc,linphone_core_get_current_call(marie1->lc));
|
||||
CU_ASSERT_TRUE(wait_for_list(lcs,&marie1->stat.number_of_LinphoneCallStreamsRunning,1,3000));
|
||||
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallStreamsRunning,1,3000));
|
||||
|
||||
/*marie2 should get her call terminated*/
|
||||
CU_ASSERT_TRUE(wait_for_list(lcs,&marie2->stat.number_of_LinphoneCallEnd,1,1000));
|
||||
|
||||
/*wait a bit that streams are established*/
|
||||
wait_for_list(lcs,&dummy,1,1000);
|
||||
CU_ASSERT_TRUE(linphone_call_get_audio_stats(pauline_call)->download_bandwidth>71);
|
||||
CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie1_call)->download_bandwidth>71);
|
||||
|
||||
linphone_core_terminate_all_calls(pauline->lc);
|
||||
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,1000));
|
||||
CU_ASSERT_TRUE(wait_for_list(lcs,&marie1->stat.number_of_LinphoneCallEnd,1,1000));
|
||||
|
||||
ms_list_free(lcs);
|
||||
linphone_core_manager_destroy(marie1);
|
||||
linphone_core_manager_destroy(marie2);
|
||||
linphone_core_manager_destroy(pauline);
|
||||
}
|
||||
#endif
|
||||
|
||||
test_t call_tests[] = {
|
||||
|
|
@ -2196,6 +2277,7 @@ test_t call_tests[] = {
|
|||
{ "Call with video added", call_with_video_added },
|
||||
{ "Call with video added (random ports)", call_with_video_added_random_ports },
|
||||
{ "Call with video declined",call_with_declined_video},
|
||||
{ "Call with multiple early media", multiple_early_media },
|
||||
#endif
|
||||
{ "SRTP ice call", srtp_ice_call },
|
||||
{ "ZRTP ice call", zrtp_ice_call },
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ subscribe=0
|
|||
|
||||
|
||||
[rtp]
|
||||
audio_rtp_port=8070
|
||||
video_rtp_port=8072
|
||||
audio_rtp_port=18070
|
||||
video_rtp_port=19072
|
||||
|
||||
[video]
|
||||
display=0
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue