diff --git a/configure.ac b/configure.ac index 82c4e64a5..3ded4bbea 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ if test "$LINPHONE_EXTRA_VERSION" != "" ;then LINPHONE_VERSION=$LINPHONE_VERSION.${LINPHONE_EXTRA_VERSION} fi -LIBLINPHONE_SO_CURRENT=4 dnl increment this number when you add/change/remove an interface +LIBLINPHONE_SO_CURRENT=5 dnl increment this number when you add/change/remove an interface LIBLINPHONE_SO_REVISION=0 dnl increment this number when you change source code, without changing interfaces; set to 0 when incrementing CURRENT LIBLINPHONE_SO_AGE=0 dnl increment this number when you add an interface, set to 0 if you remove an interface @@ -102,9 +102,13 @@ AC_SUBST(ALL_LINGUAS) AC_DEFINE_UNQUOTED(LINPHONE_ALL_LANGS, "$ALL_LINGUAS", [All supported languages]) if test "$mingw_found" != "yes" ; then -dnl gettext macro does not work properly under mingw. And we want to use the one provided by GTK. -AM_GNU_GETTEXT([external]) -LIBS="$LIBS $LIBINTL" + dnl gettext macro does not work properly under mingw. And we want to use the one provided by GTK. + + dnl AM_GNU_GETTEXT pollutes CPPFLAGS: workaround this. + CPPFLAGS_save=$CPPFLAGS + AM_GNU_GETTEXT([external]) + CPPFLAGS=$CPPFLAGS_save + LIBS="$LIBS $LIBINTL" else AC_DEFINE(ENABLE_NLS,1,[Tells whether localisation is possible]) AC_DEFINE(HAVE_GETTEXT,1,[Tells wheter localisation is possible]) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 63bff23a9..bde6a2f40 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -276,6 +276,7 @@ static void call_ringing(SalOp *h){ if (lc->ringstream!=NULL) return; /*already ringing !*/ if (lc->sound_conf.play_sndcard!=NULL){ MSSndCard *ringcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard; + if (call->localdesc->streams[0].max_rate>0) ms_snd_card_set_preferred_sample_rate(ringcard, call->localdesc->streams[0].max_rate); lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,ringcard); } ms_message("Remote ringing..."); @@ -323,6 +324,7 @@ static void call_accepted(SalOp *op){ call->state==LinphoneCallOutgoingRinging || call->state==LinphoneCallOutgoingEarlyMedia){ linphone_call_set_state(call,LinphoneCallConnected,"Connected"); + if (call->referer) linphone_core_notify_refer_state(lc,call->referer,call); } if (md && !sal_media_description_empty(md)){ if (sal_media_description_has_dir(md,SalStreamSendOnly) || @@ -368,9 +370,9 @@ static void call_accepted(SalOp *op){ } } linphone_core_update_streams (lc,call,md); - linphone_call_set_state(call, LinphoneCallStreamsRunning, "Streams running"); if (!call->current_params.in_conference) lc->current_call=call; + linphone_call_set_state(call, LinphoneCallStreamsRunning, "Streams running"); } }else{ /*send a bye*/ @@ -418,6 +420,7 @@ static void call_resumed(LinphoneCore *lc, LinphoneCall *call){ if(lc->vtable.display_status) lc->vtable.display_status(lc,_("We have been resumed.")); linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); + linphone_call_set_transfer_state(call, LinphoneCallIdle); } static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){ @@ -558,16 +561,18 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de int i; ms_message("Outgoing call failed with SRTP (SAVP) enabled - retrying with AVP"); linphone_call_stop_media_streams(call); - /* clear SRTP local params */ - call->params.media_encryption = LinphoneMediaEncryptionNone; - for(i=0; ilocaldesc->nstreams; i++) { - call->localdesc->streams[i].proto = SalProtoRtpAvp; - memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto)); + if (call->state==LinphoneCallOutgoingInit || call->state==LinphoneCallOutgoingProgress){ + /* clear SRTP local params */ + call->params.media_encryption = LinphoneMediaEncryptionNone; + for(i=0; ilocaldesc->nstreams; i++) { + call->localdesc->streams[i].proto = SalProtoRtpAvp; + memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto)); + } + linphone_core_start_invite(lc, call, NULL); } - linphone_core_start_invite(lc, call, NULL); return; } - msg=_("No common codecs"); + msg=_("Incompatible media parameters."); if (lc->vtable.display_status) lc->vtable.display_status(lc,msg); break; @@ -582,6 +587,10 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de lc->ringstream=NULL; } linphone_call_stop_media_streams (call); + if (call->referer && linphone_call_get_state(call->referer)==LinphoneCallPaused && call->referer->was_automatically_paused){ + /*resume to the call that send us the refer automatically*/ + linphone_core_resume_call(lc,call->referer); + } if (sr == SalReasonDeclined) { call->reason=LinphoneReasonDeclined; linphone_call_set_state(call,LinphoneCallEnd,"Call declined."); @@ -744,12 +753,11 @@ static void refer_received(Sal *sal, SalOp *op, const char *referto){ if (call->state!=LinphoneCallPaused){ ms_message("Automatically pausing current call to accept transfer."); linphone_core_pause_call(lc,call); + call->was_automatically_paused=TRUE; } linphone_core_start_refered_call(lc,call); - sal_call_accept_refer(op); }else if (lc->vtable.refer_received){ lc->vtable.refer_received(lc,referto); - sal_call_accept_refer(op); } } @@ -766,7 +774,7 @@ static void notify(SalOp *op, const char *from, const char *msg){ lc->vtable.notify_recv(lc,call,from,msg); } -static void notify_presence(SalOp *op, SalSubscribeState ss, SalPresenceStatus status, const char *msg){ +static void notify_presence(SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status, const char *msg){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); linphone_notify_recv(lc,op,ss,status); } @@ -795,6 +803,34 @@ static void ping_reply(SalOp *op){ } } +static void notify_refer(SalOp *op, SalReferStatus status){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + LinphoneCall *call=(LinphoneCall*) sal_op_get_user_pointer(op); + LinphoneCallState cstate; + if (call==NULL) { + ms_warning("Receiving notify_refer for unknown call."); + return ; + } + switch(status){ + case SalReferTrying: + cstate=LinphoneCallOutgoingProgress; + break; + case SalReferSuccess: + cstate=LinphoneCallConnected; + break; + case SalReferFailed: + cstate=LinphoneCallError; + break; + default: + cstate=LinphoneCallError; + } + linphone_call_set_transfer_state(call, cstate); + if (cstate==LinphoneCallConnected){ + /*automatically terminate the call as the transfer is complete.*/ + linphone_core_terminate_call(lc,call); + } +} + SalCallbacks linphone_sal_callbacks={ call_received, call_ringing, @@ -814,6 +850,7 @@ SalCallbacks linphone_sal_callbacks={ text_received, notify, notify_presence, + notify_refer, subscribe_received, subscribe_closed, ping_reply diff --git a/coreapi/chat.c b/coreapi/chat.c index d90cf61e4..9847c2c3b 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -52,15 +52,20 @@ void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg){ const char *route=NULL; const char *identity=linphone_core_find_best_identity(cr->lc,cr->peer_url,&route); - SalOp *op; + SalOp *op=NULL; LinphoneCall *call; - if((call = linphone_core_get_call_by_remote_address(cr->lc,cr->peer))!=NULL) - { - ms_message("send SIP message into the call\n"); - op = call->op; + if((call = linphone_core_get_call_by_remote_address(cr->lc,cr->peer))!=NULL){ + if (call->state==LinphoneCallConnected || + call->state==LinphoneCallStreamsRunning || + call->state==LinphoneCallPaused || + call->state==LinphoneCallPausing || + call->state==LinphoneCallPausedByRemote){ + ms_message("send SIP message through the existing call."); + op = call->op; + } } - else - { + if (op==NULL){ + /*sending out of calls*/ op = sal_op_new(cr->lc->sal); sal_op_set_route(op,route); if (cr->op!=NULL){ diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 4872a4cdc..d41c6cdad 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -35,6 +35,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "mediastreamer2/msfileplayer.h" #include "mediastreamer2/msjpegwriter.h" #include "mediastreamer2/mseventqueue.h" +#include "mediastreamer2/mssndcard.h" #ifdef VIDEO_ENABLED static MSWebCam *get_nowebcam_device(){ @@ -172,9 +173,10 @@ void linphone_call_set_authentication_token_verified(LinphoneCall *call, bool_t propagate_encryption_changed(call); } -static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit){ +static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit,int* max_sample_rate){ MSList *l=NULL; const MSList *it; + if (max_sample_rate) *max_sample_rate=0; for(it=codecs;it!=NULL;it=it->next){ PayloadType *pt=(PayloadType*)it->data; if (pt->flags & PAYLOAD_TYPE_ENABLED){ @@ -184,6 +186,7 @@ static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandw } if (linphone_core_check_payload_type_usability(lc,pt)){ l=ms_list_append(l,payload_type_clone(pt)); + if (max_sample_rate && payload_type_get_rate(pt)>*max_sample_rate) *max_sample_rate=payload_type_get_rate(pt); } } } @@ -199,6 +202,7 @@ static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, Li const char *username=linphone_address_get_username (addr); SalMediaDescription *md=sal_media_description_new(); + md->session_id=session_id; md->session_ver=session_ver; md->nstreams=1; @@ -213,10 +217,11 @@ static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, Li SalProtoRtpSavp : SalProtoRtpAvp; md->streams[0].type=SalAudio; md->streams[0].ptime=lc->net_conf.down_ptime; - l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw); + l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw,&md->streams[0].max_rate); pt=payload_type_clone(rtp_profile_get_payload_from_mime(&av_profile,"telephone-event")); l=ms_list_append(l,pt); md->streams[0].payloads=l; + if (call->params.has_video){ @@ -224,7 +229,7 @@ static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, Li md->streams[1].port=call->video_port; md->streams[1].proto=md->streams[0].proto; md->streams[1].type=SalVideo; - l=make_codec_list(lc,lc->codecs_conf.video_codecs,0); + l=make_codec_list(lc,lc->codecs_conf.video_codecs,0,NULL); md->streams[1].payloads=l; } @@ -290,6 +295,7 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, call->magic=linphone_call_magic; call->refcnt=1; call->state=LinphoneCallIdle; + call->transfer_state = LinphoneCallIdle; call->start_time=time(NULL); call->media_start_time=0; call->log=linphone_call_log_new(call, from, to); @@ -332,6 +338,7 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr discover_mtu(lc,linphone_address_get_domain (to)); if (params->referer){ sal_call_set_referer(call->op,params->referer->op); + call->referer=linphone_call_ref(params->referer); } return call; } @@ -401,6 +408,10 @@ static void linphone_call_set_terminated(LinphoneCall *call){ linphone_core_stop_dtmf(lc); call->ringing_beep=FALSE; } + if (call->referer){ + linphone_call_unref(call->referer); + call->referer=NULL; + } } void linphone_call_fix_call_parameters(LinphoneCall *call){ @@ -842,12 +853,18 @@ static void rendercb(void *data, const MSPicture *local, const MSPicture *remote #ifdef VIDEO_ENABLED static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const unsigned int event_id, const void *args){ + LinphoneCall* call = (LinphoneCall*) user_pointer; ms_warning("In linphonecall.c: video_stream_event_cb"); switch (event_id) { case MS_VIDEO_DECODER_DECODING_ERRORS: ms_warning("Case is MS_VIDEO_DECODER_DECODING_ERRORS"); - linphone_call_send_vfu_request((LinphoneCall*) user_pointer); + linphone_call_send_vfu_request(call); break; + case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED: + ms_message("First video frame decoded successfully"); + if (call->nextVideoFrameDecoded._func != NULL) + call->nextVideoFrameDecoded._func(call, call->nextVideoFrameDecoded._user_data); + break; default: ms_warning("Unhandled event %i", event_id); break; @@ -855,6 +872,14 @@ static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const u } #endif +void linphone_call_set_next_video_frame_decoded_callback(LinphoneCall *call, LinphoneCallCbFunc cb, void* user_data) { + call->nextVideoFrameDecoded._func = cb; + call->nextVideoFrameDecoded._user_data = user_data; +#ifdef VIDEO_ENABLED + ms_filter_call_method_noarg(call->videostream->decoder, MS_VIDEO_DECODER_RESET_FIRST_IMAGE_NOTIFICATION); +#endif +} + void linphone_call_init_media_streams(LinphoneCall *call){ LinphoneCore *lc=call->core; SalMediaDescription *md=call->localdesc; @@ -1157,7 +1182,8 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna captcard=playcard=NULL; } use_ec=captcard==NULL ? FALSE : linphone_core_echo_cancellation_enabled(lc); - + if (playcard && stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(playcard, stream->max_rate); + if (captcard && stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(captcard, stream->max_rate); audio_stream_enable_adaptive_bitrate_control(call->audiostream,use_arc); audio_stream_start_full( call->audiostream, @@ -1273,6 +1299,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna cam=get_nowebcam_device(); } if (!is_inactive){ + call->log->video_enabled = TRUE; video_stream_set_direction (call->videostream, dir); ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation); video_stream_set_device_rotation(call->videostream, lc->device_rotation); @@ -1649,4 +1676,16 @@ void linphone_call_log_completed(LinphoneCall *call){ call_logs_write_to_config_file(lc); } +LinphoneCallState linphone_call_get_transfer_state(LinphoneCall *call) { + return call->transfer_state; +} + +void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState state) { + if (state != call->transfer_state) { + LinphoneCore* lc = call->core; + call->transfer_state = state; + if (lc->vtable.transfer_state_changed) + lc->vtable.transfer_state_changed(lc, call, state); + } +} diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 63be1a7fc..5854774ff 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -132,6 +132,7 @@ void call_logs_write_to_config_file(LinphoneCore *lc){ lp_config_set_int(cfg,logsection,"duration",cl->duration); if (cl->refkey) lp_config_set_string(cfg,logsection,"refkey",cl->refkey); lp_config_set_float(cfg,logsection,"quality",cl->quality); + lp_config_set_int(cfg,logsection,"video_enabled", cl->video_enabled); } for(;imax_call_logs;++i){ snprintf(logsection,sizeof(logsection),"call_log_%i",i); @@ -160,6 +161,7 @@ static void call_logs_read_from_config_file(LinphoneCore *lc){ tmp=lp_config_get_string(cfg,logsection,"refkey",NULL); if (tmp) cl->refkey=ms_strdup(tmp); cl->quality=lp_config_get_float(cfg,logsection,"quality",-1); + cl->video_enabled=lp_config_get_int(cfg,logsection,"video_enabled",0); lc->call_logs=ms_list_append(lc->call_logs,cl); }else break; } @@ -1798,7 +1800,7 @@ void linphone_core_iterate(LinphoneCore *lc){ /*start the call even if the OPTIONS reply did not arrive*/ linphone_core_start_invite(lc,call,NULL); } - if (call->dir==LinphoneCallIncoming && call->state==LinphoneCallOutgoingRinging){ + if (call->state==LinphoneCallIncomingReceived){ elapsed=curtime-call->start_time; ms_message("incoming call ringing for %i seconds",elapsed); if (elapsed>lc->sip_conf.inc_timeout){ @@ -1941,12 +1943,20 @@ const char * linphone_core_get_route(LinphoneCore *lc){ void linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall *call){ if (call->refer_pending){ LinphoneCallParams *cp=linphone_core_create_default_call_parameters(lc); + LinphoneCall *newcall; cp->has_video &= !!lc->video_policy.automatically_initiate; cp->referer=call; ms_message("Starting new call to refered address %s",call->refer_to); call->refer_pending=FALSE; - linphone_core_invite_with_params(lc,call->refer_to,cp); + newcall=linphone_core_invite_with_params(lc,call->refer_to,cp); linphone_call_params_destroy(cp); + if (newcall) linphone_core_notify_refer_state(lc,call,newcall); + } +} + +void linphone_core_notify_refer_state(LinphoneCore *lc, LinphoneCall *referer, LinphoneCall *newcall){ + if (referer->op!=NULL){ + sal_call_notify_refer_state(referer->op,newcall ? newcall->op : NULL); } } @@ -2252,6 +2262,7 @@ int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char sal_call_refer(call->op,real_url); ms_free(real_url); linphone_address_destroy(real_parsed_url); + linphone_call_set_transfer_state(call, LinphoneCallOutgoingInit); return 0; } @@ -2268,7 +2279,9 @@ int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char * close the call with us (the 'dest' call). **/ int linphone_core_transfer_call_to_another(LinphoneCore *lc, LinphoneCall *call, LinphoneCall *dest){ - return sal_call_refer_with_replaces (call->op,dest->op); + int result = sal_call_refer_with_replaces (call->op,dest->op); + linphone_call_set_transfer_state(call, LinphoneCallOutgoingInit); + return result; } bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){ @@ -4573,8 +4586,10 @@ void linphone_core_start_dtmf_stream(LinphoneCore* lc) { } void linphone_core_stop_dtmf_stream(LinphoneCore* lc) { - if (lc->ringstream) ring_stop(lc->ringstream); - lc->ringstream=NULL; + if (lc->ringstream && lc->dmfs_playing_start_time!=0) { + ring_stop(lc->ringstream); + lc->ringstream=NULL; + } } int linphone_core_get_max_calls(LinphoneCore *lc) { diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 35fa25ad3..0bca75ce9 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -153,6 +153,7 @@ typedef struct _LinphoneCallLog{ rtp_stats_t local_stats; rtp_stats_t remote_stats; float quality; + int video_enabled; struct _LinphoneCore *lc; } LinphoneCallLog; @@ -225,6 +226,9 @@ typedef struct _LinphoneVideoPolicy LinphoneVideoPolicy; **/ struct _LinphoneCall; typedef struct _LinphoneCall LinphoneCall; + +/** Callback prototype */ +typedef void (*LinphoneCallCbFunc)(struct _LinphoneCall *call,void * user_data); /** * LinphoneCallState enum represents the different state a call can reach into. @@ -286,6 +290,9 @@ void linphone_call_set_authentication_token_verified(LinphoneCall *call, bool_t void linphone_call_send_vfu_request(LinphoneCall *call); void *linphone_call_get_user_pointer(LinphoneCall *call); void linphone_call_set_user_pointer(LinphoneCall *call, void *user_pointer); +void linphone_call_set_next_video_frame_decoded_callback(LinphoneCall *call, LinphoneCallCbFunc cb, void* user_data); +LinphoneCallState linphone_call_get_transfer_state(LinphoneCall *call); + /** * Enables or disable echo cancellation for this call * @param call @@ -613,6 +620,8 @@ typedef void (*DtmfReceived)(struct _LinphoneCore* lc, LinphoneCall *call, int d typedef void (*ReferReceived)(struct _LinphoneCore *lc, const char *refer_to); /** Callback prototype */ typedef void (*BuddyInfoUpdated)(struct _LinphoneCore *lc, LinphoneFriend *lf); +/** Callback prototype for in progress transfers. The new_call_state is the state of the call resulting of the transfer, at the other party. */ +typedef void (*LinphoneTransferStateChanged)(struct _LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state); /** * This structure holds all callbacks that the application should implement. @@ -629,6 +638,8 @@ typedef struct _LinphoneVTable{ TextMessageReceived text_received; /**< A text message has been received */ DtmfReceived dtmf_received; /**< A dtmf has been received received */ ReferReceived refer_received; /**< An out of call refer was received */ + CallEncryptionChangedCb call_encryption_changed; /**mime_type)){ MSFilterDesc *desc=ms_filter_get_encoder(pt->mime_type); diff --git a/coreapi/presence.c b/coreapi/presence.c index d97f4ed94..ca6357258 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -101,7 +101,7 @@ void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from){ ms_free(tmp); } -void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeState ss, SalPresenceStatus sal_status){ +void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceStatus sal_status){ char *tmp; LinphoneFriend *lf; LinphoneAddress *friend=NULL; diff --git a/coreapi/private.h b/coreapi/private.h index ab317c981..ec57eaf9c 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -74,6 +74,12 @@ struct _LinphoneCallParams{ bool_t pad; }; + +typedef struct _CallCallbackObj +{ + LinphoneCallCbFunc _func; + void * _user_data; +}CallCallbackObj; static const int linphone_call_magic=0x3343; @@ -84,6 +90,7 @@ struct _LinphoneCall SalMediaDescription *localdesc; SalMediaDescription *resultdesc; LinphoneCallDir dir; + LinphoneCall *referer; /*when this call is the result of a transfer, referer is set to the original call that caused the transfer*/ struct _RtpProfile *audio_profile; struct _RtpProfile *video_profile; struct _LinphoneCallLog *log; @@ -93,6 +100,7 @@ struct _LinphoneCall time_t start_time; /*time at which the call was initiated*/ time_t media_start_time; /*time at which it was accepted, media streams established*/ LinphoneCallState state; + LinphoneCallState transfer_state; /*idle if no transfer*/ LinphoneReason reason; int refcnt; void * user_pointer; @@ -122,6 +130,8 @@ struct _LinphoneCall bool_t audiostream_encrypted; bool_t auth_token_verified; bool_t defer_update; + bool_t was_automatically_paused; + CallCallbackObj nextVideoFrameDecoded; }; @@ -133,6 +143,7 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const LinphoneCallLog * linphone_call_log_new(LinphoneCall *call, LinphoneAddress *local, LinphoneAddress * remote); void linphone_call_log_completed(LinphoneCall *call); void linphone_call_log_destroy(LinphoneCallLog *cl); +void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState state); void linphone_auth_info_write_config(struct _LpConfig *config, LinphoneAuthInfo *obj, int pos); @@ -193,7 +204,7 @@ SalPresenceStatus linphone_online_status_to_sal(LinphoneOnlineStatus os); void linphone_process_authentication(LinphoneCore* lc, SalOp *op); void linphone_authentication_ok(LinphoneCore *lc, SalOp *op); void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from); -void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeState ss, SalPresenceStatus status); +void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status); void linphone_proxy_config_process_authentication_failure(LinphoneCore *lc, SalOp *op); void linphone_subscription_answered(LinphoneCore *lc, SalOp *op); @@ -549,6 +560,7 @@ void linphone_call_add_to_conf(LinphoneCall *call, bool_t muted); void linphone_call_remove_from_conf(LinphoneCall *call); void linphone_core_conference_check_uninit(LinphoneCore *lc); bool_t linphone_core_sound_resources_available(LinphoneCore *lc); +void linphone_core_notify_refer_state(LinphoneCore *lc, LinphoneCall *referer, LinphoneCall *newcall); void __linphone_core_invalidate_registers(LinphoneCore* lc); diff --git a/coreapi/sal.h b/coreapi/sal.h index 1ae18a9d4..bbe4dafff 100644 --- a/coreapi/sal.h +++ b/coreapi/sal.h @@ -134,6 +134,7 @@ typedef struct SalStreamDescription{ SalStreamDir dir; SalSrtpCryptoAlgo crypto[SAL_CRYPTO_ALGO_MAX]; unsigned int crypto_local_tag; + int max_rate; } SalStreamDescription; #define SAL_MEDIA_DESCRIPTION_MAX_STREAMS 4 @@ -206,10 +207,16 @@ typedef enum SalPresenceStatus{ SalPresenceAltService, }SalPresenceStatus; -typedef enum SalSubscribeState{ +typedef enum SalReferStatus{ + SalReferTrying, + SalReferSuccess, + SalReferFailed +}SalReferStatus; + +typedef enum SalSubscribeStatus{ SalSubscribeActive, SalSubscribeTerminated -}SalSubscribeState; +}SalSubscribeStatus; typedef void (*SalOnCallReceived)(SalOp *op); typedef void (*SalOnCallRinging)(SalOp *op); @@ -227,8 +234,9 @@ typedef void (*SalOnVfuRequest)(SalOp *op); typedef void (*SalOnDtmfReceived)(SalOp *op, char dtmf); typedef void (*SalOnRefer)(Sal *sal, SalOp *op, const char *referto); typedef void (*SalOnTextReceived)(Sal *sal, const char *from, const char *msg); -typedef void (*SalOnNotify)(SalOp *op, const char *from, const char *value); -typedef void (*SalOnNotifyPresence)(SalOp *op, SalSubscribeState ss, SalPresenceStatus status, const char *msg); +typedef void (*SalOnNotify)(SalOp *op, const char *from, const char *event); +typedef void (*SalOnNotifyRefer)(SalOp *op, SalReferStatus state); +typedef void (*SalOnNotifyPresence)(SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status, const char *msg); typedef void (*SalOnSubscribeReceived)(SalOp *salop, const char *from); typedef void (*SalOnSubscribeClosed)(SalOp *salop, const char *from); typedef void (*SalOnPingReply)(SalOp *salop); @@ -252,6 +260,7 @@ typedef struct SalCallbacks{ SalOnTextReceived text_received; SalOnNotify notify; SalOnNotifyPresence notify_presence; + SalOnNotifyRefer notify_refer; SalOnSubscribeReceived subscribe_received; SalOnSubscribeClosed subscribe_closed; SalOnPingReply ping_reply; @@ -339,6 +348,7 @@ int sal_call_terminate(SalOp *h); bool_t sal_call_autoanswer_asked(SalOp *op); void sal_call_send_vfu_request(SalOp *h); int sal_call_is_offerer(const SalOp *h); +int sal_call_notify_refer_state(SalOp *h, SalOp *newcall); /*Registration*/ int sal_register(SalOp *op, const char *proxy, const char *from, int expires); diff --git a/coreapi/sal_eXosip2.c b/coreapi/sal_eXosip2.c index 6d66bf4c4..a89a8a13c 100644 --- a/coreapi/sal_eXosip2.c +++ b/coreapi/sal_eXosip2.c @@ -728,6 +728,45 @@ int sal_call_set_referer(SalOp *h, SalOp *refered_call){ return 0; } +static int send_notify_for_refer(int did, const char *sipfrag){ + osip_message_t *msg; + eXosip_lock(); + eXosip_call_build_notify(did,EXOSIP_SUBCRSTATE_ACTIVE,&msg); + if (msg==NULL){ + eXosip_unlock(); + ms_error("Could not build NOTIFY for refer."); + return -1; + } + osip_message_set_content_type(msg,"message/sipfrag"); + osip_message_set_header(msg,"Event","refer"); + osip_message_set_body(msg,sipfrag,strlen(sipfrag)); + eXosip_call_send_request(did,msg); + eXosip_unlock(); + return 0; +} + +/* currently only support to notify trying and 200Ok*/ +int sal_call_notify_refer_state(SalOp *h, SalOp *newcall){ + if (newcall==NULL){ + /* in progress*/ + send_notify_for_refer(h->did,"SIP/2.0 100 Trying\r\n"); + } + else if (newcall->cid!=-1){ + if (newcall->did==-1){ + /* not yet established*/ + if (!newcall->terminated){ + /* in progress*/ + send_notify_for_refer(h->did,"SIP/2.0 100 Trying\r\n"); + } + }else{ + if (!newcall->terminated){ + send_notify_for_refer(h->did,"SIP/2.0 200 Ok\r\n"); + } + } + } + return 0; +} + int sal_ping(SalOp *op, const char *from, const char *to){ osip_message_t *options=NULL; @@ -748,26 +787,6 @@ int sal_ping(SalOp *op, const char *from, const char *to){ return -1; } -int sal_call_accept_refer(SalOp *op){ - osip_message_t *msg=NULL; - int err=0; - eXosip_lock(); - err = eXosip_call_build_notify(op->did,EXOSIP_SUBCRSTATE_ACTIVE,&msg); - if(msg != NULL) - { - osip_message_set_header(msg,(const char *)"event","refer"); - osip_message_set_content_type(msg,"message/sipfrag"); - osip_message_set_body(msg,"SIP/2.0 100 Trying",sizeof("SIP/2.0 100 Trying")); - eXosip_call_send_request(op->did,msg); - } - else - { - ms_error("could not get a notify built\n"); - } - eXosip_unlock(); - return err; -} - int sal_call_refer(SalOp *h, const char *refer_to){ osip_message_t *msg=NULL; int err=0; @@ -1517,6 +1536,51 @@ static void process_refer(Sal *sal, SalOp *op, eXosip_event_t *ev){ } } +void process_notify(Sal *sal, eXosip_event_t *ev){ + osip_header_t *h=NULL; + char *from=NULL; + SalOp *op=find_op(sal,ev); + osip_message_t *ans=NULL; + + ms_message("Receiving NOTIFY request !"); + osip_from_to_str(ev->request->from,&from); + osip_message_header_get_byname(ev->request,"Event",0,&h); + if(h){ + osip_body_t *body=NULL; + //osip_content_type_t *ct=NULL; + osip_message_get_body(ev->request,0,&body); + //ct=osip_message_get_content_type(ev->request); + if (h->hvalue && strcasecmp(h->hvalue,"refer")==0){ + /*special handling of refer events*/ + if (body && body->body){ + osip_message_t *msg; + osip_message_init(&msg); + if (osip_message_parse_sipfrag(msg,body->body,strlen(body->body))==0){ + int code=osip_message_get_status_code(msg); + if (code==100){ + sal->callbacks.notify_refer(op,SalReferTrying); + }else if (code==200){ + sal->callbacks.notify_refer(op,SalReferSuccess); + }else if (code>=400){ + sal->callbacks.notify_refer(op,SalReferFailed); + } + } + osip_message_free(msg); + } + }else{ + /*generic handling*/ + sal->callbacks.notify(op,from,h->hvalue); + } + } + /*answer that we received the notify*/ + eXosip_lock(); + eXosip_call_build_answer(ev->tid,200,&ans); + if (ans) + eXosip_call_send_answer(ev->tid,200,ans); + eXosip_unlock(); + osip_free(from); +} + static void call_message_new(Sal *sal, eXosip_event_t *ev){ osip_message_t *ans=NULL; if (ev->request){ @@ -1559,22 +1623,7 @@ static void call_message_new(Sal *sal, eXosip_event_t *ev){ ms_message("Receiving REFER request !"); process_refer(sal,op,ev); }else if(MSG_IS_NOTIFY(ev->request)){ - osip_header_t *h=NULL; - char *from=NULL; - SalOp *op=find_op(sal,ev); - - ms_message("Receiving NOTIFY request !"); - osip_from_to_str(ev->request->from,&from); - osip_message_header_get_byname(ev->request,"Event",0,&h); - if(h) - sal->callbacks.notify(op,from,h->hvalue); - /*answer that we received the notify*/ - eXosip_lock(); - eXosip_call_build_answer(ev->tid,200,&ans); - if (ans) - eXosip_call_send_answer(ev->tid,200,ans); - eXosip_unlock(); - osip_free(from); + process_notify(sal,ev); }else if (MSG_IS_OPTIONS(ev->request)){ eXosip_lock(); eXosip_call_build_answer(ev->tid,200,&ans); diff --git a/gtk/incall_view.c b/gtk/incall_view.c index d693ac149..d19dba8c2 100644 --- a/gtk/incall_view.c +++ b/gtk/incall_view.c @@ -579,6 +579,28 @@ void linphone_gtk_in_call_view_terminate(LinphoneCall *call, const char *error_m linphone_gtk_terminate_conference_participant(call); } +void linphone_gtk_in_call_view_set_transfer_status(LinphoneCall *call,LinphoneCallState cstate){ + GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call); + if (callview){ + GtkWidget *duration=linphone_gtk_get_widget(callview,"in_call_duration"); + const char *transfer_status="unknown"; + switch(cstate){ + case LinphoneCallOutgoingProgress: + transfer_status=_("Transfer in progress"); + break; + case LinphoneCallConnected: + transfer_status=_("Transfer done."); + break; + case LinphoneCallError: + transfer_status=_("Transfer failed."); + break; + default: + break; + } + gtk_label_set_text(GTK_LABEL(duration),transfer_status); + } +} + void linphone_gtk_draw_mute_button(GtkButton *button, gboolean active){ g_object_set_data(G_OBJECT(button),"active",GINT_TO_POINTER(active)); if (active){ diff --git a/gtk/linphone.h b/gtk/linphone.h index 7074bc87d..d4cafe28f 100644 --- a/gtk/linphone.h +++ b/gtk/linphone.h @@ -107,6 +107,7 @@ void linphone_gtk_in_call_view_update_duration(LinphoneCall *call); void linphone_gtk_in_call_view_terminate(LinphoneCall *call, const char *error_msg); void linphone_gtk_in_call_view_set_incoming(LinphoneCall *call); void linphone_gtk_in_call_view_set_paused(LinphoneCall *call); +void linphone_gtk_in_call_view_set_transfer_status(LinphoneCall *call,LinphoneCallState cstate); void linphone_gtk_mute_clicked(GtkButton *button); void linphone_gtk_enable_mute_button(GtkButton *button, gboolean sensitive); void linphone_gtk_enable_hold_button(LinphoneCall *call, gboolean sensitive, gboolean holdon); diff --git a/gtk/main.c b/gtk/main.c index 60c4556d7..819195268 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -63,6 +63,7 @@ static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const ch static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl); static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg); static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token); +static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate); static gboolean linphone_gtk_auto_answer(LinphoneCall *call); static void linphone_gtk_status_icon_set_blinking(gboolean val); @@ -225,6 +226,7 @@ static void linphone_gtk_init_liblinphone(const char *config_file, vtable.refer_received=linphone_gtk_refer_received; vtable.buddy_info_updated=linphone_gtk_buddy_info_updated; vtable.call_encryption_changed=linphone_gtk_call_encryption_changed; + vtable.transfer_state_changed=linphone_gtk_transfer_state_changed; linphone_core_set_user_agent("Linphone", LINPHONE_VERSION); the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL); @@ -1133,6 +1135,10 @@ static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall linphone_gtk_in_call_view_show_encryption(call); } +static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){ + linphone_gtk_in_call_view_set_transfer_status(call,cstate); +} + static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){ GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities")); GtkTreeModel *model=gtk_combo_box_get_model(box); diff --git a/linphone.spec.in b/linphone.spec.in index ff79974e6..86929b575 100644 --- a/linphone.spec.in +++ b/linphone.spec.in @@ -11,7 +11,7 @@ Name: linphone Version: @VERSION@ -Release: %(git describe --tags | sed 's/.*-\([0-9]*\)-g.*/\1/' || echo '1')%{?dist} +Release: %(git describe --tags --abbrev=40 | sed -rn 's/^.*-([0-9]+)-g[a-z0-9]{40}$/\1/p' || echo '1')%{?dist} Summary: Phone anywhere in the whole world by using the Internet Group: Applications/Communications @@ -27,7 +27,7 @@ BuildRequires: gtk2-devel BuildRequires: libeXosip2-devel speex-devel gettext BuildRequires: intltool gettext-devel %if %{video} -BuildRequires: ffmpeg-devel SDL-devel +BuildRequires: ffmpeg-devel SDL-devel %endif %description @@ -35,12 +35,12 @@ Linphone is a SIP compliant audio & video phone. It can be used to run calls over the internet. It has a gtk+ and console interface. %package devel -Summary: Development libraries for linphone -Group: Development/Libraries -Requires: %{name} = %{version}-%{release} -Requires: ortp-devel = @ORTP_VERSION@ -Requires: mediastreamer2-devel = @MS2_VERSION@ -Requires: glib2-devel +Summary: Development libraries for linphone +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} +Requires: ortp-devel = @ORTP_VERSION@ +Requires: mediastreamer-devel = @MS2_VERSION@ +Requires: glib2-devel %description devel Libraries and headers required to develop software with linphone. diff --git a/mediastreamer2 b/mediastreamer2 index 33599a544..3870493e9 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 33599a544a23878604cd42a769c1044d599c1347 +Subproject commit 3870493e9a59ddf7858cf4628dd86ec7a8b469d8 diff --git a/oRTP b/oRTP index 3fb614e2e..04570bd7a 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 3fb614e2ed15803f2c96c223cceb5545a60f2431 +Subproject commit 04570bd7a02f9baa682c4667fbba16e8c951b62b