Merge branch 'quality_reporting'

Conflicts:
	coreapi/linphonecall.c
	mediastreamer2
	tester/call_tester.c
This commit is contained in:
Gautier Pelloux-Prayer 2014-04-22 14:42:20 +02:00
commit ceb8533cf9
16 changed files with 1069 additions and 197 deletions

View file

@ -64,7 +64,8 @@ LOCAL_SRC_FILES := \
xml.c \
xml2lpc.c \
lpc2xml.c \
remote_provisioning.c
remote_provisioning.c \
quality_reporting.c
ifndef LINPHONE_VERSION
LINPHONE_VERSION = "Devel"

View file

@ -203,6 +203,7 @@
<ClCompile Include="..\..\..\coreapi\offeranswer.c" />
<ClCompile Include="..\..\..\coreapi\presence.c" />
<ClCompile Include="..\..\..\coreapi\proxy.c" />
<ClCompile Include="..\..\..\coreapi\quality_reporting.c" />
<ClCompile Include="..\..\..\coreapi\sal.c" />
<ClCompile Include="..\..\..\coreapi\siplogin.c" />
<ClCompile Include="..\..\..\coreapi\sipsetup.c" />
@ -275,4 +276,4 @@
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsPhone\v$(TargetPlatformVersion)\Microsoft.Cpp.WindowsPhone.$(TargetPlatformVersion).targets" Condition="'$(Platform)'=='ARM'" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View file

@ -74,6 +74,7 @@ set(SOURCE_FILES
offeranswer.c
presence.c
proxy.c
quality_reporting.c
remote_provisioning.c
sal.c
siplogin.c

View file

@ -56,6 +56,7 @@ liblinphone_la_SOURCES=\
xml2lpc.c \
lpc2xml.c \
remote_provisioning.c \
quality_reporting.c \
$(GITVERSION_FILE)
if BUILD_UPNP

View file

@ -393,7 +393,7 @@ static int find_port_offset(LinphoneCore *lc, int stream_index, int base_port){
int tried_port;
int existing_port;
bool_t already_used=FALSE;
for(offset=0;offset<100;offset+=2){
tried_port=base_port+offset;
already_used=FALSE;
@ -480,10 +480,10 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from,
linphone_core_get_audio_port_range(call->core, &min_port, &max_port);
port_config_set(call,0,min_port,max_port);
linphone_core_get_video_port_range(call->core, &min_port, &max_port);
port_config_set(call,1,min_port,max_port);
linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO], LINPHONE_CALL_STATS_AUDIO);
linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO], LINPHONE_CALL_STATS_VIDEO);
}
@ -768,7 +768,6 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const
return;
}
}
ms_message("Call %p: moving from state %s to %s",call,linphone_call_state_to_string(call->state),
linphone_call_state_to_string(cstate));
@ -777,6 +776,7 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const
Indeed it does not change the state of the call (still paused or running)*/
call->state=cstate;
}
if (cstate==LinphoneCallEnd || cstate==LinphoneCallError){
switch(call->non_op_error.reason){
case SalReasonDeclined:
@ -795,9 +795,17 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const
call->media_start_time=time(NULL);
}
if (cstate == LinphoneCallStreamsRunning) {
linphone_reporting_update_ip(call);
}
if (lc->vtable.call_state_changed)
lc->vtable.call_state_changed(lc,call,cstate,message);
if (cstate==LinphoneCallReleased){
if (call->log->status == LinphoneCallSuccess)
linphone_reporting_publish(call);
if (call->op!=NULL) {
/*transfer the last error so that it can be obtained even in Released state*/
if (call->non_op_error.reason==SalReasonNone){
@ -1301,7 +1309,7 @@ const char *linphone_call_params_get_session_name(const LinphoneCallParams *cp){
/**
* Set the session name of the media session (ie in SDP). Subject from the SIP message (which is different) can be set using linphone_call_params_set_custom_header().
* @param cp the call parameters.
* @param name the session name
* @param name the session name
**/
void linphone_call_params_set_session_name(LinphoneCallParams *cp, const char *name){
if (cp->session_name){
@ -1451,17 +1459,17 @@ static void _linphone_call_prepare_ice_for_stream(LinphoneCall *call, int stream
int linphone_call_prepare_ice(LinphoneCall *call, bool_t incoming_offer){
SalMediaDescription *remote;
bool_t has_video=FALSE;
if ((linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) && (call->ice_session != NULL)){
if (incoming_offer){
remote=sal_call_get_remote_media_description(call->op);
has_video=linphone_core_media_description_contains_video_stream(remote);
}else has_video=call->params.has_video;
_linphone_call_prepare_ice_for_stream(call,0,TRUE);
if (has_video) _linphone_call_prepare_ice_for_stream(call,1,TRUE);
/*start ICE gathering*/
if (incoming_offer)
if (incoming_offer)
linphone_core_update_ice_from_remote_media_description(call,remote); /*this may delete the ice session*/
if (call->ice_session && !ice_session_candidates_gathered(call->ice_session)){
if (call->audiostream->ms.state==MSStreamInitialized)
@ -1536,7 +1544,7 @@ void linphone_call_init_audio_stream(LinphoneCall *call){
call->audiostream_app_evq = ortp_ev_queue_new();
rtp_session_register_event_queue(audiostream->ms.sessions.rtp_session,call->audiostream_app_evq);
_linphone_call_prepare_ice_for_stream(call,0,FALSE);
}
@ -1548,7 +1556,7 @@ void linphone_call_init_video_stream(LinphoneCall *call){
int video_recv_buf_size=lp_config_get_int(lc->config,"video","recv_buf_size",0);
int dscp=linphone_core_get_video_dscp(lc);
const char *display_filter=linphone_core_get_video_display_filter(lc);
if (call->sessions[1].rtp_session==NULL){
call->videostream=video_stream_new(call->media_ports[1].rtp_port,call->media_ports[1].rtcp_port, call->af==AF_INET6);
}else{
@ -1956,7 +1964,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna
#ifdef VIDEO_ENABLED
LinphoneCore *lc=call->core;
int used_pt=-1;
/* look for savp stream first */
const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc,
SalProtoRtpSavp,SalVideo);
@ -1978,8 +1986,9 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna
const char *rtp_addr=vstream->rtp_addr[0]!='\0' ? vstream->rtp_addr : call->resultdesc->addr;
const char *rtcp_addr=vstream->rtcp_addr[0]!='\0' ? vstream->rtcp_addr : call->resultdesc->addr;
const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,vstream->proto,SalVideo);
call->video_profile=make_profile(call,call->resultdesc,vstream,&used_pt);
if (used_pt!=-1){
VideoStreamDir dir=VideoStreamSendRecv;
MSWebCam *cam=lc->video_conf.device;
@ -2143,7 +2152,7 @@ void linphone_call_update_crypto_parameters(LinphoneCall *call, SalMediaDescript
SalStreamDescription *old_stream;
SalStreamDescription *new_stream;
const SalStreamDescription *local_st_desc;
local_st_desc = sal_media_description_find_stream(call->localdesc, SalProtoRtpSavp, SalAudio);
old_stream = sal_media_description_find_stream(old_md, SalProtoRtpSavp, SalAudio);
new_stream = sal_media_description_find_stream(new_md, SalProtoRtpSavp, SalAudio);
@ -2202,6 +2211,7 @@ static void linphone_call_log_fill_stats(LinphoneCallLog *log, MediaStream *st){
void linphone_call_stop_audio_stream(LinphoneCall *call) {
if (call->audiostream!=NULL) {
linphone_reporting_update(call, LINPHONE_CALL_STATS_AUDIO);
media_stream_reclaim_sessions(&call->audiostream->ms,&call->sessions[0]);
rtp_session_unregister_event_queue(call->audiostream->ms.sessions.rtp_session,call->audiostream_app_evq);
ortp_ev_queue_flush(call->audiostream_app_evq);
@ -2230,6 +2240,7 @@ void linphone_call_stop_audio_stream(LinphoneCall *call) {
void linphone_call_stop_video_stream(LinphoneCall *call) {
#ifdef VIDEO_ENABLED
if (call->videostream!=NULL){
linphone_reporting_update(call, LINPHONE_CALL_STATS_VIDEO);
media_stream_reclaim_sessions(&call->videostream->ms,&call->sessions[1]);
rtp_session_unregister_event_queue(call->videostream->ms.sessions.rtp_session,call->videostream_app_evq);
ortp_ev_queue_flush(call->videostream_app_evq);
@ -2756,6 +2767,8 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse
evd->packet = NULL;
call->stats[LINPHONE_CALL_STATS_VIDEO].updated = LINPHONE_CALL_STATS_RECEIVED_RTCP_UPDATE;
update_local_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO],(MediaStream*)call->videostream);
if (linphone_call_params_video_enabled(linphone_call_get_current_params(call)))
linphone_reporting_call_stats_updated(call, LINPHONE_CALL_STATS_VIDEO);
if (lc->vtable.call_stats_updated)
lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]);
} else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
@ -2766,6 +2779,8 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse
evd->packet = NULL;
call->stats[LINPHONE_CALL_STATS_VIDEO].updated = LINPHONE_CALL_STATS_SENT_RTCP_UPDATE;
update_local_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO],(MediaStream*)call->videostream);
if (linphone_call_params_video_enabled(linphone_call_get_current_params(call)))
linphone_reporting_call_stats_updated(call, LINPHONE_CALL_STATS_VIDEO);
if (lc->vtable.call_stats_updated)
lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]);
} else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)
@ -2801,6 +2816,7 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse
evd->packet = NULL;
call->stats[LINPHONE_CALL_STATS_AUDIO].updated = LINPHONE_CALL_STATS_RECEIVED_RTCP_UPDATE;
update_local_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO],(MediaStream*)call->audiostream);
linphone_reporting_call_stats_updated(call, LINPHONE_CALL_STATS_AUDIO);
if (lc->vtable.call_stats_updated)
lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]);
} else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
@ -2811,6 +2827,7 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse
evd->packet = NULL;
call->stats[LINPHONE_CALL_STATS_AUDIO].updated = LINPHONE_CALL_STATS_SENT_RTCP_UPDATE;
update_local_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO],(MediaStream*)call->audiostream);
linphone_reporting_call_stats_updated(call, LINPHONE_CALL_STATS_AUDIO);
if (lc->vtable.call_stats_updated)
lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]);
} else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)

View file

@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "sipsetup.h"
#include "lpconfig.h"
#include "private.h"
#include "quality_reporting.h"
#include <math.h>
#include <ortp/telephonyevents.h>
@ -127,6 +128,9 @@ LinphoneCallLog * linphone_call_log_new(LinphoneCall *call, LinphoneAddress *fro
cl->to=to;
cl->status=LinphoneCallAborted; /*default status*/
cl->quality=-1;
cl->reports[LINPHONE_CALL_STATS_AUDIO]=linphone_reporting_new();
cl->reports[LINPHONE_CALL_STATS_VIDEO]=linphone_reporting_new();
return cl;
}
@ -390,6 +394,9 @@ void linphone_call_log_destroy(LinphoneCallLog *cl){
if (cl->to!=NULL) linphone_address_destroy(cl->to);
if (cl->refkey!=NULL) ms_free(cl->refkey);
if (cl->call_id) ms_free(cl->call_id);
if (cl->reports[LINPHONE_CALL_STATS_AUDIO]!=NULL) linphone_reporting_destroy(cl->reports[LINPHONE_CALL_STATS_AUDIO]);
if (cl->reports[LINPHONE_CALL_STATS_VIDEO]!=NULL) linphone_reporting_destroy(cl->reports[LINPHONE_CALL_STATS_VIDEO]);
ms_free(cl);
}
@ -607,7 +614,7 @@ static void sound_config_read(LinphoneCore *lc)
linphone_core_set_play_file(lc,lp_config_get_string(lc->config,"sound","hold_music",PACKAGE_SOUND_DIR "/" HOLD_MUSIC));
lc->sound_conf.latency=0;
#ifndef __ios
#ifndef __ios
tmp=TRUE;
#else
tmp=FALSE; /* on iOS we have builtin echo cancellation.*/
@ -626,7 +633,7 @@ static void sound_config_read(LinphoneCore *lc)
/*just parse requested stream feature once at start to print out eventual errors*/
linphone_core_get_audio_features(lc);
_linphone_core_set_tone(lc,LinphoneReasonBusy,LinphoneToneBusy,NULL);
}
@ -640,7 +647,7 @@ static void certificates_config_read(LinphoneCore *lc)
#endif
linphone_core_set_root_ca(lc,rootca);
linphone_core_verify_server_certificates(lc,lp_config_get_int(lc->config,"sip","verify_server_certs",TRUE));
linphone_core_verify_server_cn(lc,lp_config_get_int(lc->config,"sip","verify_server_cn",TRUE));
linphone_core_verify_server_cn(lc,lp_config_get_int(lc->config,"sip","verify_server_cn",TRUE));
}
static void sip_config_read(LinphoneCore *lc)
@ -663,12 +670,12 @@ static void sip_config_read(LinphoneCore *lc)
}
linphone_core_enable_ipv6(lc,ipv6);
memset(&tr,0,sizeof(tr));
tr.udp_port=lp_config_get_int(lc->config,"sip","sip_port",5060);
tr.tcp_port=lp_config_get_int(lc->config,"sip","sip_tcp_port",5060);
/*we are not listening inbound connection for tls, port has no meaning*/
tr.tls_port=lp_config_get_int(lc->config,"sip","sip_tls_port",LC_SIP_TRANSPORT_RANDOM);
certificates_config_read(lc);
/*setting the dscp must be done before starting the transports, otherwise it is not taken into effect*/
sal_set_dscp(lc->sal,linphone_core_get_sip_dscp(lc));
@ -703,7 +710,7 @@ static void sip_config_read(LinphoneCore *lc)
tmp=lp_config_get_int(lc->config,"sip","in_call_timeout",0);
linphone_core_set_in_call_timeout(lc,tmp);
tmp=lp_config_get_int(lc->config,"sip","delayed_timeout",4);
linphone_core_set_delayed_timeout(lc,tmp);
@ -975,8 +982,8 @@ static void video_config_read(LinphoneCore *lc){
int capture, display, self_view;
int automatic_video=1;
#endif
const char *str;
#ifdef VIDEO_ENABLED
const char *str;
#ifdef VIDEO_ENABLED
LinphoneVideoPolicy vpol;
memset(&vpol, 0, sizeof(LinphoneVideoPolicy));
#endif
@ -1039,7 +1046,7 @@ bool_t linphone_core_tunnel_available(void){
/**
* Enable adaptive rate control.
*
*
* @ingroup media_parameters
*
* Adaptive rate control consists in using RTCP feedback provided information to dynamically
@ -1053,7 +1060,7 @@ void linphone_core_enable_adaptive_rate_control(LinphoneCore *lc, bool_t enabled
/**
* Returns whether adaptive rate control is enabled.
*
*
* @ingroup media_parameters
*
* See linphone_core_enable_adaptive_rate_control().
@ -1073,7 +1080,7 @@ bool_t linphone_core_rtcp_enabled(const LinphoneCore *lc){
* calls (within SDP messages) so that the remote end can have
* sufficient knowledge to properly configure its audio & video
* codec output bitrate to not overflow available bandwidth.
*
*
* @ingroup media_parameters
*
* @param lc the LinphoneCore object
@ -1159,7 +1166,7 @@ void linphone_core_set_upload_ptime(LinphoneCore *lc, int ptime){
* Set audio packetization time linphone will send (in absence of requirement from peer)
* A value of 0 stands for the current codec default packetization time.
*
*
*
* @ingroup media_parameters
**/
int linphone_core_get_upload_ptime(LinphoneCore *lc){
@ -1180,14 +1187,14 @@ const char * linphone_core_get_version(void){
static void linphone_core_assign_payload_type(LinphoneCore *lc, PayloadType *const_pt, int number, const char *recv_fmtp){
PayloadType *pt;
#ifdef ANDROID
if (const_pt->channels==2){
ms_message("Stereo %s codec not supported on this platform.",const_pt->mime_type);
return;
}
#endif
pt=payload_type_clone(const_pt);
if (number==-1){
/*look for a free number */
@ -1251,10 +1258,10 @@ void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const
static void misc_config_read(LinphoneCore *lc) {
LpConfig *config=lc->config;
const char *uuid;
lc->max_call_logs=lp_config_get_int(config,"misc","history_max_size",15);
lc->max_calls=lp_config_get_int(config,"misc","max_calls",NB_MAX_CALLS);
uuid=lp_config_get_string(config,"misc","uuid",NULL);
if (!uuid){
char tmp[64];
@ -1290,12 +1297,12 @@ static void linphone_core_start(LinphoneCore * lc) {
void linphone_configuring_terminated(LinphoneCore *lc, LinphoneConfiguringState state, const char *message) {
if (lc->vtable.configuring_status)
lc->vtable.configuring_status(lc, state, message);
if (state == LinphoneConfiguringSuccessful) {
if (linphone_core_is_provisioning_transient(lc) == TRUE)
linphone_core_set_provisioning_uri(lc, NULL);
}
linphone_core_start(lc);
}
@ -1342,7 +1349,7 @@ static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtab
linphone_core_assign_payload_type(lc,&payload_type_mp4v,99,"profile-level-id=3");
linphone_core_assign_payload_type(lc,&payload_type_h264,102,"profile-level-id=42801F");
linphone_core_assign_payload_type(lc,&payload_type_vp8,103,NULL);
linphone_core_assign_payload_type(lc,&payload_type_theora,97,NULL);
linphone_core_assign_payload_type(lc,&payload_type_x_snow,-1,NULL);
/* due to limited space in SDP, we have to disable this h264 line which is normally no more necessary */
@ -1372,7 +1379,7 @@ static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtab
linphone_core_assign_payload_type(lc,&payload_type_opus,-1,"useinbandfec=1; usedtx=0; cbr=1");
linphone_core_assign_payload_type(lc,&payload_type_isac,-1,NULL);
linphone_core_handle_static_payloads(lc);
ms_init();
/* create a mediastreamer2 event queue and set it as global */
/* This allows to run event's callback in linphone_core_iterate() */
@ -1386,13 +1393,13 @@ static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtab
lc->network_last_check = 0;
lc->network_last_status = FALSE;
lc->http_provider = belle_sip_stack_create_http_provider(sal_get_belle_sip_stack(lc->sal), "0.0.0.0");
lc->http_verify_policy = belle_tls_verify_policy_new();
belle_http_provider_set_tls_verify_policy(lc->http_provider,lc->http_verify_policy);
certificates_config_read(lc);
remote_provisioning_uri = linphone_core_get_provisioning_uri(lc);
if (remote_provisioning_uri == NULL) {
linphone_configuring_terminated(lc, LinphoneConfiguringSkipped, NULL);
@ -1940,7 +1947,7 @@ static int apply_transports(LinphoneCore *lc){
/*first of all invalidate all current registrations so that we can register again with new transports*/
__linphone_core_invalidate_registers(lc);
if (lc->sip_conf.ipv6_enabled)
anyaddr="::0";
else
@ -2212,7 +2219,7 @@ void linphone_core_iterate(LinphoneCore *lc){
int elapsed;
bool_t one_second_elapsed=FALSE;
const char *remote_provisioning_uri = NULL;
if (linphone_core_get_global_state(lc) == LinphoneGlobalStartup) {
if (sal_get_root_ca(lc->sal)) {
belle_tls_verify_policy_t *tls_policy = belle_tls_verify_policy_new();
@ -2223,7 +2230,7 @@ void linphone_core_iterate(LinphoneCore *lc){
if (lc->vtable.display_status)
lc->vtable.display_status(lc, _("Configuring"));
linphone_core_set_state(lc, LinphoneGlobalConfiguring, "Configuring");
remote_provisioning_uri = linphone_core_get_provisioning_uri(lc);
if (remote_provisioning_uri) {
int err = linphone_remote_provisioning_download_and_apply(lc, remote_provisioning_uri);
@ -2246,7 +2253,7 @@ void linphone_core_iterate(LinphoneCore *lc){
if (ecs==LinphoneEcCalibratorDone){
int len=lp_config_get_int(lc->config,"sound","ec_tail_len",0);
int margin=len/2;
lp_config_set_int(lc->config, "sound", "ec_delay",MAX(lc->ecc->delay-margin,0));
} else if (ecs == LinphoneEcCalibratorFailed) {
lp_config_set_int(lc->config, "sound", "ec_delay", -1);/*use default value from soundcard*/
@ -2359,7 +2366,7 @@ void linphone_core_iterate(LinphoneCore *lc){
/**
* Interpret a call destination as supplied by the user, and returns a fully qualified
* LinphoneAddress.
*
*
* @ingroup call_control
*
* A sip address should look like DisplayName <sip:username@domain:port> .
@ -2378,7 +2385,7 @@ LinphoneAddress * linphone_core_interpret_url(LinphoneCore *lc, const char *url)
LinphoneProxyConfig *proxy=lc->default_proxy;
char *tmpurl;
LinphoneAddress *uri;
if (*url=='\0') return NULL;
if (is_enum(url,&enum_domain)){
@ -2428,7 +2435,7 @@ LinphoneAddress * linphone_core_interpret_url(LinphoneCore *lc, const char *url)
if (uri!=NULL){
return uri;
}
return NULL;
}
@ -2466,7 +2473,7 @@ const char * linphone_core_get_route(LinphoneCore *lc){
* Start a new call as a consequence of a transfer request received from a call.
* This function is for advanced usage: the execution of transfers is automatically managed by the LinphoneCore. However if an application
* wants to have control over the call parameters for the new call, it should call this function immediately during the LinphoneCallRefered notification.
* @see LinphoneCoreVTable::call_state_changed
* @see LinphoneCoreVTable::call_state_changed
* @param lc the LinphoneCore
* @param call a call that has just been notified about LinphoneCallRefered state event.
* @param params the call parameters to be applied to the new call.
@ -2475,13 +2482,13 @@ const char * linphone_core_get_route(LinphoneCore *lc){
LinphoneCall * linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){
LinphoneCallParams *cp=params ? linphone_call_params_copy(params) : linphone_core_create_default_call_parameters(lc);
LinphoneCall *newcall;
if (call->state!=LinphoneCallPaused){
ms_message("Automatically pausing current call to accept transfer.");
_linphone_core_pause_call(lc,call);
call->was_automatically_paused=TRUE;
}
if (!params){
cp->has_video = call->current_params.has_video; /*start the call to refer-target with video enabled if original call had video*/
}
@ -2521,7 +2528,7 @@ void linphone_core_notify_refer_state(LinphoneCore *lc, LinphoneCall *referer, L
nodes with the basic SIP routing policy in order to get a workable
system.
*/
static MSList *make_routes_for_proxy(LinphoneProxyConfig *proxy, const LinphoneAddress *dest){
MSList *ret=NULL;
const char *local_route=linphone_proxy_config_get_route(proxy);
@ -2816,7 +2823,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const
bool_t defer = FALSE;
linphone_core_preempt_sound_resources(lc);
if(!linphone_core_can_we_add_call(lc)){
if (lc->vtable.display_warning)
lc->vtable.display_warning(lc,_("Sorry, we have reached the maximum number of simultaneous calls"));
@ -2848,7 +2855,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const
linphone_call_set_state (call,LinphoneCallOutgoingInit,"Starting outgoing call");
call->log->start_date_time=time(NULL);
linphone_call_init_media_streams(call);
if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) {
/* Defer the start of the call after the ICE gathering process. */
if (linphone_call_prepare_ice(call,FALSE)==1)
@ -2880,7 +2887,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const
defer = TRUE;
}
}
if (defer==FALSE) linphone_core_start_invite(lc,call,NULL);
if (real_url!=NULL) ms_free(real_url);
@ -2893,7 +2900,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const
* @ingroup call_control
* The remote endpoint is expected to issue a new call to the specified destination.
* The current call remains active and thus can be later paused or terminated.
*
*
* It is possible to follow the progress of the transfer provided that transferee sends notification about it.
* In this case, the transfer_state_changed callback of the #LinphoneCoreVTable is invoked to notify of the state of the new call at the other party.
* The notified states are #LinphoneCallOutgoingInit , #LinphoneCallOutgoingProgress, #LinphoneCallOutgoingRinging and #LinphoneCallOutgoingConnected.
@ -2925,7 +2932,7 @@ int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char
* @param lc linphone core object
* @param call a running call you want to transfer
* @param dest a running call whose remote person will receive the transfer
*
*
* @ingroup call_control
*
* The transfered call is supposed to be in paused state, so that it is able to accept the transfer immediately.
@ -2933,7 +2940,7 @@ int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char
* This method will send a transfer request to the transfered person. The phone of the transfered is then
* expected to automatically call to the destination of the transfer. The receiver of the transfer will then automatically
* close the call with us (the 'dest' call).
*
*
* It is possible to follow the progress of the transfer provided that transferee sends notification about it.
* In this case, the transfer_state_changed callback of the #LinphoneCoreVTable is invoked to notify of the state of the new call at the other party.
* The notified states are #LinphoneCallOutgoingInit , #LinphoneCallOutgoingProgress, #LinphoneCallOutgoingRinging and #LinphoneCallOutgoingConnected.
@ -2958,7 +2965,7 @@ bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){
bool_t linphone_core_media_description_has_srtp(const SalMediaDescription *md){
int i;
if (md->n_active_streams==0) return FALSE;
for(i=0;i<md->n_active_streams;i++){
const SalStreamDescription *sd=&md->streams[i];
if (sd->proto!=SalProtoRtpSavp){
@ -3031,11 +3038,11 @@ void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){
if (call->state==LinphoneCallIncomingReceived){
/*try to be best-effort in giving real local or routable contact address for 100Rel case*/
linphone_call_set_contact_op(call);
if (propose_early_media || ringback_tone!=NULL){
linphone_core_accept_early_media(lc,call);
}else sal_call_notify_ringing(call->op,FALSE);
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);
}
@ -3061,7 +3068,7 @@ void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){
int linphone_core_accept_early_media_with_params(LinphoneCore* lc, LinphoneCall* call, const LinphoneCallParams* params) {
if (call->state==LinphoneCallIncomingReceived){
SalMediaDescription* md;
/*try to be best-effort in giving real local or routable contact address for 100Rel case*/
linphone_call_set_contact_op(call);
@ -3072,7 +3079,7 @@ int linphone_core_accept_early_media_with_params(LinphoneCore* lc, LinphoneCall*
sal_call_set_local_media_description ( call->op,call->localdesc );
sal_op_set_sent_custom_header ( call->op,params->custom_headers );
}
sal_call_notify_ringing(call->op,TRUE);
linphone_call_set_state(call,LinphoneCallIncomingEarlyMedia,"Incoming call early media");
@ -3202,15 +3209,15 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho
/**
* @ingroup call_control
* When receiving a #LinphoneCallUpdatedByRemote state notification, prevent LinphoneCore from performing an automatic answer.
*
*
* When receiving a #LinphoneCallUpdatedByRemote state notification (ie an incoming reINVITE), the default behaviour of
* LinphoneCore is to automatically answer the reINIVTE with call parameters unchanged.
* However when for example when the remote party updated the call to propose a video stream, it can be useful
* to prompt the user before answering. This can be achieved by calling linphone_core_defer_call_update() during
* to prompt the user before answering. This can be achieved by calling linphone_core_defer_call_update() during
* the call state notifiacation, to deactivate the automatic answer that would just confirm the audio but reject the video.
* Then, when the user responds to dialog prompt, it becomes possible to call linphone_core_accept_call_update() to answer
* the reINVITE, with eventually video enabled in the LinphoneCallParams argument.
*
*
* @return 0 if successful, -1 if the linphone_core_defer_call_update() was done outside a #LinphoneCallUpdatedByRemote notification, which is illegal.
**/
int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call){
@ -3416,7 +3423,7 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call,
sal_call_set_local_media_description(call->op,call->localdesc);
sal_op_set_sent_custom_header(call->op,params->custom_headers);
}
/*give a chance a set card prefered sampling frequency*/
if (call->localdesc->streams[0].max_rate>0) {
ms_message ("configuring prefered card sampling rate to [%i]",call->localdesc->streams[0].max_rate);
@ -3425,7 +3432,7 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call,
if (lc->sound_conf.capt_sndcard)
ms_snd_card_set_preferred_sample_rate(lc->sound_conf.capt_sndcard, call->localdesc->streams[0].max_rate);
}
if (!was_ringing && call->audiostream->ms.state==MSStreamInitialized){
audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard);
}
@ -3543,9 +3550,9 @@ int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall *the_call)
/**
* Decline a pending incoming call, with a reason.
*
*
* @ingroup call_control
*
*
* @param lc the linphone core
* @param call the LinphoneCall, must be in the IncomingReceived state.
* @param reason the reason for rejecting the call: LinphoneReasonDeclined or LinphoneReasonBusy
@ -3679,12 +3686,12 @@ int linphone_core_pause_all_calls(LinphoneCore *lc){
void linphone_core_preempt_sound_resources(LinphoneCore *lc){
LinphoneCall *current_call;
if (linphone_core_is_in_conference(lc)){
linphone_core_leave_conference(lc);
return;
}
current_call=linphone_core_get_current_call(lc);
if(current_call != NULL){
ms_message("Pausing automatically the current call.");
@ -3703,7 +3710,7 @@ void linphone_core_preempt_sound_resources(LinphoneCore *lc){
int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call){
char temp[255]={0};
const char *subject="Call resuming";
if(call->state!=LinphoneCallPaused ){
ms_warning("we cannot resume a call that has not been established and paused before");
return -1;
@ -3718,7 +3725,7 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call){
}
call->was_automatically_paused=FALSE;
/* Stop playing music immediately. If remote side is a conference it
prevents the participants to hear it while the 200OK comes back.*/
if (call->audiostream) audio_stream_play(call->audiostream, NULL);
@ -3754,7 +3761,7 @@ static int remote_address_compare(LinphoneCall *call, const LinphoneAddress *rad
* @param lc
* @param remote_address
* @return the LinphoneCall of the call if found
*
*
* @ingroup call_control
*/
LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneCore *lc, const char *remote_address){
@ -4357,7 +4364,7 @@ const char *linphone_core_get_root_ca(LinphoneCore *lc){
/**
* Specify whether the tls server certificate must be verified when connecting to a SIP/TLS server.
*
*
* @ingroup initializing
**/
void linphone_core_verify_server_certificates(LinphoneCore *lc, bool_t yesno){
@ -4474,7 +4481,7 @@ void linphone_core_mute_mic(LinphoneCore *lc, bool_t val){
if ( linphone_core_get_rtp_no_xmit_on_audio_mute(lc) ){
audio_stream_mute_rtp(st,val);
}
}
}
@ -4560,7 +4567,7 @@ void linphone_core_set_stun_server(LinphoneCore *lc, const char *server){
if (server)
lc->net_conf.stun_server=ms_strdup(server);
else lc->net_conf.stun_server=NULL;
/* each time the stun server is changed, we must clean the resolved cached addrinfo*/
if (lc->net_conf.stun_addrinfo){
freeaddrinfo(lc->net_conf.stun_addrinfo);
@ -4570,7 +4577,7 @@ void linphone_core_set_stun_server(LinphoneCore *lc, const char *server){
if (lc->net_conf.stun_server){
linphone_core_resolve_stun_server(lc);
}
if (linphone_core_ready(lc))
lp_config_set_string(lc->config,"net","stun_server",lc->net_conf.stun_server);
}
@ -4627,7 +4634,7 @@ const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc)
char ipstring [INET6_ADDRSTRLEN];
if (lc->net_conf.nat_address==NULL) return NULL;
if (parse_hostname_to_addr (lc->net_conf.nat_address, &ss, &ss_len, 5060)<0) {
return lc->net_conf.nat_address;
}
@ -4996,7 +5003,7 @@ int linphone_core_set_static_picture(LinphoneCore *lc, const char *path) {
const char *linphone_core_get_static_picture(LinphoneCore *lc) {
const char *path=NULL;
#ifdef VIDEO_ENABLED
path=ms_static_image_get_default_image();
path=ms_static_image_get_default_image();
#else
ms_warning("Video support not compiled.");
#endif
@ -5071,7 +5078,7 @@ static void unset_video_window_id(LinphoneCore *lc, bool_t preview, unsigned lon
LinphoneCall *call;
MSList *elem;
#endif
if (id!=0 && id!=-1) {
ms_error("Invalid use of unset_video_window_id()");
return;
@ -5235,7 +5242,7 @@ static MSVideoSizeDef supported_resolutions[]={
#if !TARGET_OS_MAC || TARGET_OS_IPHONE /* OS_MAC is 1 for iPhone, but we need QVGA */
{ { MS_VIDEO_SIZE_QVGA_W, MS_VIDEO_SIZE_QVGA_H } , "qvga" },
#endif
{ { MS_VIDEO_SIZE_QCIF_W, MS_VIDEO_SIZE_QCIF_H } , "qcif" },
{ { MS_VIDEO_SIZE_QCIF_W, MS_VIDEO_SIZE_QCIF_H } , "qcif" },
{ { 0,0 } , NULL }
};
@ -5480,7 +5487,7 @@ void linphone_core_play_named_tone(LinphoneCore *lc, LinphoneToneID toneid){
def.frequencies[0]=620;
def.interval=250;
def.repeat_count=3;
break;
default:
ms_warning("Unhandled tone id.");
@ -5546,7 +5553,7 @@ int linphone_core_get_mtu(const LinphoneCore *lc){
* Sets the maximum transmission unit size in bytes.
* This information is useful for sending RTP packets.
* Default value is 1500.
*
*
* @ingroup media_parameters
**/
void linphone_core_set_mtu(LinphoneCore *lc, int mtu){
@ -5640,7 +5647,7 @@ void sip_config_uninit(LinphoneCore *lc)
int i;
sip_config_t *config=&lc->sip_conf;
bool_t still_registered=TRUE;
lp_config_set_int(lc->config,"sip","guess_hostname",config->guess_hostname);
lp_config_set_string(lc->config,"sip","contact",config->contact);
lp_config_set_int(lc->config,"sip","inc_timeout",config->inc_timeout);
@ -5655,7 +5662,7 @@ void sip_config_uninit(LinphoneCore *lc)
LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)(elem->data);
linphone_proxy_config_edit(cfg); /* to unregister */
}
ms_message("Unregistration started.");
for (i=0;i<20&&still_registered;i++){
@ -5687,7 +5694,7 @@ void sip_config_uninit(LinphoneCore *lc)
ms_message("Tunnel destroyed.");
}
#endif
sal_reset_transports(lc->sal);
sal_unlisten_ports(lc->sal); /*to make sure no new messages are received*/
if (lc->http_provider) {
@ -5893,7 +5900,7 @@ static void linphone_core_uninit(LinphoneCore *lc)
linphone_presence_model_unref(lc->presence_model);
}
linphone_core_free_payload_types(lc);
linphone_core_message_storage_close(lc);
ms_exit();
linphone_core_set_state(lc,LinphoneGlobalOff,"Off");
@ -5902,7 +5909,7 @@ static void linphone_core_uninit(LinphoneCore *lc)
static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime){
// second get the list of available proxies
const MSList *elem=linphone_core_get_proxy_config_list(lc);
if (lc->network_reachable==isReachable) return; // no change, ignore.
ms_message("Network state is now [%s]",isReachable?"UP":"DOWN");
@ -5919,7 +5926,7 @@ static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t cu
}
lc->netup_time=curtime;
lc->network_reachable=isReachable;
if (!lc->network_reachable){
linphone_core_invalidate_friend_subscriptions(lc);
sal_reset_transports(lc->sal);
@ -6393,7 +6400,7 @@ int linphone_core_set_media_encryption(LinphoneCore *lc, LinphoneMediaEncryption
LinphoneMediaEncryption linphone_core_get_media_encryption(LinphoneCore *lc) {
const char* menc = lp_config_get_string(lc->config, "sip", "media_encryption", NULL);
if (menc == NULL)
return LinphoneMediaEncryptionNone;
else if (strcmp(menc, "srtp")==0)
@ -6429,10 +6436,10 @@ const char* linphone_core_get_device_identifier(const LinphoneCore *lc) {
/**
* Set the DSCP field for SIP signaling channel.
*
*
* @ingroup network_parameters
* * The DSCP defines the quality of service in IP packets.
*
*
**/
void linphone_core_set_sip_dscp(LinphoneCore *lc, int dscp){
sal_set_dscp(lc->sal,dscp);
@ -6444,10 +6451,10 @@ void linphone_core_set_sip_dscp(LinphoneCore *lc, int dscp){
/**
* Get the DSCP field for SIP signaling channel.
*
*
* @ingroup network_parameters
* * The DSCP defines the quality of service in IP packets.
*
*
**/
int linphone_core_get_sip_dscp(const LinphoneCore *lc){
return lp_config_get_int(lc->config,"sip","dscp",0x1a);
@ -6458,7 +6465,7 @@ int linphone_core_get_sip_dscp(const LinphoneCore *lc){
*
* @ingroup network_parameters
* The DSCP defines the quality of service in IP packets.
*
*
**/
void linphone_core_set_audio_dscp(LinphoneCore *lc, int dscp){
if (linphone_core_ready(lc))
@ -6470,7 +6477,7 @@ void linphone_core_set_audio_dscp(LinphoneCore *lc, int dscp){
*
* @ingroup network_parameters
* The DSCP defines the quality of service in IP packets.
*
*
**/
int linphone_core_get_audio_dscp(const LinphoneCore *lc){
return lp_config_get_int(lc->config,"rtp","audio_dscp",0x2e);
@ -6481,12 +6488,12 @@ int linphone_core_get_audio_dscp(const LinphoneCore *lc){
*
* @ingroup network_parameters
* The DSCP defines the quality of service in IP packets.
*
*
**/
void linphone_core_set_video_dscp(LinphoneCore *lc, int dscp){
if (linphone_core_ready(lc))
lp_config_set_int_hex(lc->config,"rtp","video_dscp",dscp);
}
/**
@ -6494,7 +6501,7 @@ void linphone_core_set_video_dscp(LinphoneCore *lc, int dscp){
*
* @ingroup network_parameters
* The DSCP defines the quality of service in IP packets.
*
*
**/
int linphone_core_get_video_dscp(const LinphoneCore *lc){
return lp_config_get_int(lc->config,"rtp","video_dscp",0);

View file

@ -812,6 +812,17 @@ LINPHONE_PUBLIC void linphone_proxy_config_enable_publish(LinphoneProxyConfig *o
LINPHONE_PUBLIC void linphone_proxy_config_set_dial_escape_plus(LinphoneProxyConfig *cfg, bool_t val);
LINPHONE_PUBLIC void linphone_proxy_config_set_dial_prefix(LinphoneProxyConfig *cfg, const char *prefix);
/**
* Indicates either or not, quality statistics during call should be stored and sent to a collector at termination.
* @param cfg #LinphoneProxyConfig object
* @param val if true, quality statistics publish will be stored and sent to the collector
*
*/
LINPHONE_PUBLIC void linphone_proxy_config_enable_statistics(LinphoneProxyConfig *cfg, bool_t val);
LINPHONE_PUBLIC bool_t linphone_proxy_config_send_statistics_enabled(LinphoneProxyConfig *cfg);
LINPHONE_PUBLIC void linphone_proxy_config_set_statistics_collector(LinphoneProxyConfig *cfg, const char *collector);
LINPHONE_PUBLIC const char *linphone_proxy_config_get_statistics_collector(const LinphoneProxyConfig *obj);
/**
* Get the registration state of the given proxy config.
* @param[in] obj #LinphoneProxyConfig object.

View file

@ -170,7 +170,7 @@ static time_t parse_timestamp(const char *timestamp) {
return seconds - timezone;
}
static char * timestamp_to_string(time_t timestamp) {
char * linphone_timestamp_to_rfc3339_string(time_t timestamp) {
char timestamp_str[22];
struct tm *ret;
#ifndef WIN32
@ -1467,7 +1467,7 @@ void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from){
char *tmp;
LinphoneAddress *uri;
LinphoneProxyConfig *cfg;
uri=linphone_address_new(from);
linphone_address_clean(uri);
tmp=linphone_address_as_string(uri);
@ -1482,7 +1482,7 @@ void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from){
}
}
}
/* check if we answer to this subscription */
if (linphone_find_friend_by_address(lc->friends,uri,&lf)!=NULL){
lf->insub=op;
@ -1604,7 +1604,7 @@ static void write_xml_presence_note_obj(LinphonePresenceNote *note, struct _pres
static int write_xml_presence_timestamp(xmlTextWriterPtr writer, time_t timestamp) {
int err;
char *timestamp_str = timestamp_to_string(timestamp);
char *timestamp_str = linphone_timestamp_to_rfc3339_string(timestamp);
err = xmlTextWriterWriteElement(writer, (const xmlChar *)"timestamp", (const xmlChar *)timestamp_str);
if (timestamp_str) ms_free(timestamp_str);
return err;

View file

@ -34,6 +34,7 @@ extern "C" {
#include "linphonecore_utils.h"
#include "sal/sal.h"
#include "sipsetup.h"
#include "quality_reporting.h"
#include <belle-sip/object.h>
#include <belle-sip/dict.h>
@ -115,6 +116,8 @@ struct _LinphoneCallLog{
float quality;
time_t start_date_time; /**Start date of the call in seconds as expressed in a time_t */
char* call_id; /**unique id of a call*/
reporting_session_report_t * reports[2]; /**<Quality statistics of the call (rfc6035) */
bool_t video_enabled;
};
@ -191,6 +194,7 @@ struct _LinphoneCall
StunCandidate ac,vc; /*audio video ip/port discovered by STUN*/
struct _AudioStream *audiostream; /**/
struct _VideoStream *videostream;
MSAudioEndpoint *endpoint; /*used for conferencing*/
char *refer_to;
LinphoneCallParams params;
@ -399,6 +403,7 @@ struct _LinphoneProxyConfig
char *reg_proxy;
char *reg_identity;
char *reg_route;
char *reg_statistics_collector;
char *realm;
char *contact_params;
char *contact_uri_params;
@ -415,6 +420,7 @@ struct _LinphoneProxyConfig
bool_t publish;
bool_t dial_escape_plus;
bool_t send_publish;
bool_t send_statistics;
bool_t pad[3];
void* user_data;
time_t deletion_date;
@ -669,7 +675,7 @@ struct _LinphoneCore
bool_t use_preview_window;
time_t network_last_check;
bool_t network_last_status;
bool_t ringstream_autorelease;
bool_t pad[2];
@ -804,6 +810,7 @@ void linphone_configure_op(LinphoneCore *lc, SalOp *op, const LinphoneAddress *d
void linphone_call_create_op(LinphoneCall *call);
int linphone_call_prepare_ice(LinphoneCall *call, bool_t incoming_offer);
void linphone_core_notify_info_message(LinphoneCore* lc,SalOp *op, const SalBody *body);
void linphone_content_uninit(LinphoneContent * obj);
LinphoneContent *linphone_content_copy_from_sal_body(LinphoneContent *obj, const SalBody *ref);
SalBody *sal_body_from_content(SalBody *body, const LinphoneContent *lc);
SalReason linphone_reason_to_sal(LinphoneReason reason);
@ -852,11 +859,18 @@ char * linphone_get_xml_text_content(xmlparsing_context_t *xml_ctx, const char *
void linphone_free_xml_text_content(const char *text);
xmlXPathObjectPtr linphone_get_xml_xpath_object_for_node_list(xmlparsing_context_t *xml_ctx, const char *xpath_expression);
/*****************************************************************************
* OTHER UTILITY FUNCTIONS *
****************************************************************************/
char * linphone_timestamp_to_rfc3339_string(time_t timestamp);
static inline const LinphoneErrorInfo *linphone_error_info_from_sal_op(const SalOp *op){
if (op==NULL) return (LinphoneErrorInfo*)sal_error_info_none();
return (const LinphoneErrorInfo*)sal_op_get_error_info(op);
}
/** Belle Sip-based objects need unique ids
*/

View file

@ -17,7 +17,7 @@ Copyright (C) 2000 Simon MORLAT (simon.morlat@linphone.org)
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "linphonecore.h"
#include "sipsetup.h"
#include "lpconfig.h"
@ -31,7 +31,7 @@ void linphone_proxy_config_write_all_to_config_file(LinphoneCore *lc){
MSList *elem;
int i;
if (!linphone_core_ready(lc)) return;
for(elem=lc->sip_conf.proxies,i=0;elem!=NULL;elem=ms_list_next(elem),i++){
LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
linphone_proxy_config_write_to_config_file(lc->config,cfg,i);
@ -46,9 +46,10 @@ static void linphone_proxy_config_init(LinphoneCore* lc, LinphoneProxyConfig *ob
const char *identity = lc ? lp_config_get_default_string(lc->config, "proxy", "reg_identity", NULL) : NULL;
const char *proxy = lc ? lp_config_get_default_string(lc->config, "proxy", "reg_proxy", NULL) : NULL;
const char *route = lc ? lp_config_get_default_string(lc->config, "proxy", "reg_route", NULL) : NULL;
const char *statistics_collector = lc ? lp_config_get_default_string(lc->config, "proxy", "reg_statistics_collector", NULL) : NULL;
const char *contact_params = lc ? lp_config_get_default_string(lc->config, "proxy", "contact_parameters", NULL) : NULL;
const char *contact_uri_params = lc ? lp_config_get_default_string(lc->config, "proxy", "contact_uri_parameters", NULL) : NULL;
memset(obj, 0, sizeof(LinphoneProxyConfig));
obj->magic = linphone_proxy_config_magic;
obj->expires = lc ? lp_config_get_default_int(lc->config, "proxy", "reg_expires", 3600) : 3600;
@ -59,6 +60,8 @@ static void linphone_proxy_config_init(LinphoneCore* lc, LinphoneProxyConfig *ob
obj->reg_identity = identity ? ms_strdup(identity) : NULL;
obj->reg_proxy = proxy ? ms_strdup(proxy) : NULL;
obj->reg_route = route ? ms_strdup(route) : NULL;
obj->reg_statistics_collector = statistics_collector ? ms_strdup(statistics_collector) : NULL;
obj->send_statistics = lc ? lp_config_get_default_int(lc->config, "proxy", "send_statistics", 0) : 0;
obj->contact_params = contact_params ? ms_strdup(contact_params) : NULL;
obj->contact_uri_params = contact_uri_params ? ms_strdup(contact_uri_params) : NULL;
}
@ -85,7 +88,7 @@ LinphoneProxyConfig * linphone_core_create_proxy_config(LinphoneCore *lc) {
/**
* Destroys a proxy config.
*
*
* @note: LinphoneProxyConfig that have been removed from LinphoneCore with
* linphone_core_remove_proxy_config() must not be freed.
**/
@ -93,6 +96,7 @@ void linphone_proxy_config_destroy(LinphoneProxyConfig *obj){
if (obj->reg_proxy!=NULL) ms_free(obj->reg_proxy);
if (obj->reg_identity!=NULL) ms_free(obj->reg_identity);
if (obj->reg_route!=NULL) ms_free(obj->reg_route);
if (obj->reg_statistics_collector!=NULL) ms_free(obj->reg_statistics_collector);
if (obj->ssctx!=NULL) sip_setup_context_free(obj->ssctx);
if (obj->realm!=NULL) ms_free(obj->realm);
if (obj->type!=NULL) ms_free(obj->type);
@ -123,10 +127,10 @@ bool_t linphone_proxy_config_is_registered(const LinphoneProxyConfig *obj){
int linphone_proxy_config_set_server_addr(LinphoneProxyConfig *obj, const char *server_addr){
LinphoneAddress *addr=NULL;
char *modified=NULL;
if (obj->reg_proxy!=NULL) ms_free(obj->reg_proxy);
obj->reg_proxy=NULL;
if (server_addr!=NULL && strlen(server_addr)>0){
if (strstr(server_addr,"sip:")==NULL && strstr(server_addr,"sips:")==NULL){
modified=ms_strdup_printf("sip:%s",server_addr);
@ -149,7 +153,7 @@ int linphone_proxy_config_set_server_addr(LinphoneProxyConfig *obj, const char *
/**
* Sets the user identity as a SIP address.
*
* This identity is normally formed with display name, username and domain, such
* This identity is normally formed with display name, username and domain, such
* as:
* Alice <sip:alice@example.net>
* The REGISTER messages will have from and to set to this identity.
@ -297,14 +301,14 @@ LinphoneAddress *guess_contact_for_register(LinphoneProxyConfig *obj){
LinphoneAddress *ret=NULL;
LinphoneAddress *proxy=linphone_address_new(obj->reg_proxy);
const char *host;
if (proxy==NULL) return NULL;
host=linphone_address_get_domain(proxy);
if (host!=NULL){
int localport = -1;
const char *localip = NULL;
LinphoneAddress *contact=linphone_address_new(obj->reg_identity);
linphone_address_clean(contact);
if (obj->contact_params) {
@ -374,7 +378,7 @@ void linphone_proxy_config_refresh_register(LinphoneProxyConfig *obj){
/**
* Sets a dialing prefix to be automatically prepended when inviting a number with
* Sets a dialing prefix to be automatically prepended when inviting a number with
* linphone_core_invite();
* This dialing prefix shall usually be the country code of the country where the user is living.
*
@ -390,7 +394,7 @@ void linphone_proxy_config_set_dial_prefix(LinphoneProxyConfig *cfg, const char
/**
* Returns dialing prefix.
*
*
*
**/
const char *linphone_proxy_config_get_dial_prefix(const LinphoneProxyConfig *cfg){
return cfg->dial_prefix;
@ -413,6 +417,37 @@ void linphone_proxy_config_set_dial_escape_plus(LinphoneProxyConfig *cfg, bool_t
bool_t linphone_proxy_config_get_dial_escape_plus(const LinphoneProxyConfig *cfg){
return cfg->dial_escape_plus;
}
void linphone_proxy_config_enable_statistics(LinphoneProxyConfig *cfg, bool_t val){
cfg->send_statistics = val;
}
bool_t linphone_proxy_config_send_statistics_enabled(LinphoneProxyConfig *cfg){
// ensure that collector address is set too!
return cfg->send_statistics && cfg->reg_statistics_collector != NULL;
}
void linphone_proxy_config_set_statistics_collector(LinphoneProxyConfig *cfg, const char *collector){
if (collector!=NULL && strlen(collector)>0){
LinphoneAddress *addr=linphone_address_new(collector);
if (!addr || linphone_address_get_username(addr)==NULL){
ms_warning("Invalid sip collector identity: %s",collector);
if (addr)
linphone_address_destroy(addr);
} else {
if (cfg->reg_statistics_collector != NULL)
ms_free(cfg->reg_statistics_collector);
cfg->reg_statistics_collector = ms_strdup(collector);
linphone_address_destroy(addr);
}
}
}
const char *linphone_proxy_config_get_statistics_collector(const LinphoneProxyConfig *cfg){
return cfg->reg_statistics_collector;
}
/*
* http://en.wikipedia.org/wiki/Telephone_numbering_plan
* http://en.wikipedia.org/wiki/Telephone_numbers_in_Europe
@ -423,7 +458,7 @@ typedef struct dial_plan{
char ccc[8]; /*country calling code*/
int nnl; /*maximum national number length*/
const char * icp; /*international call prefix, ex: 00 in europe*/
}dial_plan_t;
/* TODO: fill with information for all countries over the world*/
@ -708,7 +743,7 @@ static void lookup_dial_plan(const char *ccc, dial_plan_t *plan){
static bool_t is_a_phone_number(const char *username){
const char *p;
for(p=username;*p!='\0';++p){
if (isdigit(*p) ||
if (isdigit(*p) ||
*p==' ' ||
*p=='.' ||
*p=='-' ||
@ -736,12 +771,12 @@ static char *flatten_number(const char *number){
static void replace_plus(const char *src, char *dest, size_t destlen, const char *icp){
int i=0;
if (icp && src[0]=='+' && (destlen>(i=strlen(icp))) ){
src++;
strcpy(dest,icp);
}
for(;(i<destlen-1) && *src!='\0';++i){
dest[i]=*src;
src++;
@ -756,7 +791,7 @@ int linphone_proxy_config_normalize_number(LinphoneProxyConfig *proxy, const cha
char *flatten;
flatten=flatten_number(username);
ms_message("Flattened number is '%s'",flatten);
if (proxy->dial_prefix==NULL || proxy->dial_prefix[0]=='\0'){
/*no prefix configured, nothing else to do*/
strncpy(result,flatten,result_len);
@ -824,7 +859,7 @@ void linphone_proxy_config_set_realm(LinphoneProxyConfig *cfg, const char *realm
int linphone_proxy_config_send_publish(LinphoneProxyConfig *proxy, LinphonePresenceModel *presence){
int err=0;
if (proxy->state==LinphoneRegistrationOk || proxy->state==LinphoneRegistrationCleared){
if (proxy->publish_op==NULL){
proxy->publish_op=sal_op_new(proxy->lc->sal);
@ -1015,7 +1050,7 @@ void linphone_core_set_default_proxy(LinphoneCore *lc, LinphoneProxyConfig *conf
lc->default_proxy=config;
if (linphone_core_ready(lc))
lp_config_set_int(lc->config,"sip","default_proxy",linphone_core_get_default_proxy(lc,NULL));
}
}
void linphone_core_set_default_proxy_index(LinphoneCore *lc, int index){
if (index<0) linphone_core_set_default_proxy(lc,NULL);
@ -1059,6 +1094,9 @@ void linphone_proxy_config_write_to_config_file(LpConfig *config, LinphoneProxyC
if (obj->reg_route!=NULL){
lp_config_set_string(config,key,"reg_route",obj->reg_route);
}
if (obj->reg_statistics_collector!=NULL){
lp_config_set_string(config,key,"reg_statistics_collector",obj->reg_statistics_collector);
}
if (obj->reg_identity!=NULL){
lp_config_set_string(config,key,"reg_identity",obj->reg_identity);
}
@ -1072,6 +1110,7 @@ void linphone_proxy_config_write_to_config_file(LpConfig *config, LinphoneProxyC
lp_config_set_int(config,key,"reg_sendregister",obj->reg_sendregister);
lp_config_set_int(config,key,"publish",obj->publish);
lp_config_set_int(config,key,"dial_escape_plus",obj->dial_escape_plus);
lp_config_set_int(config,key,"send_statistics",obj->send_statistics);
lp_config_set_string(config,key,"dial_prefix",obj->dial_prefix);
lp_config_set_int(config,key,"privacy",obj->privacy);
}
@ -1085,7 +1124,7 @@ LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(LpConfig *config
const char *proxy;
LinphoneProxyConfig *cfg;
char key[50];
sprintf(key,"proxy_%i",index);
if (!lp_config_has_section(config,key)){
@ -1094,29 +1133,33 @@ LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(LpConfig *config
cfg=linphone_proxy_config_new();
identity=lp_config_get_string(config,key,"reg_identity",NULL);
identity=lp_config_get_string(config,key,"reg_identity",NULL);
proxy=lp_config_get_string(config,key,"reg_proxy",NULL);
linphone_proxy_config_set_identity(cfg,identity);
linphone_proxy_config_set_server_addr(cfg,proxy);
tmp=lp_config_get_string(config,key,"reg_route",NULL);
if (tmp!=NULL) linphone_proxy_config_set_route(cfg,tmp);
tmp=lp_config_get_string(config,key,"reg_statistics_collector",NULL);
if (tmp!=NULL) linphone_proxy_config_set_statistics_collector(cfg,tmp);
linphone_proxy_config_enable_statistics(cfg,lp_config_get_int(config,key,"send_statistics",0));
linphone_proxy_config_set_contact_parameters(cfg,lp_config_get_string(config,key,"contact_parameters",NULL));
linphone_proxy_config_set_contact_uri_parameters(cfg,lp_config_get_string(config,key,"contact_uri_parameters",NULL));
linphone_proxy_config_expires(cfg,lp_config_get_int(config,key,"reg_expires",lp_config_get_default_int(config,"proxy","reg_expires",600)));
linphone_proxy_config_enableregister(cfg,lp_config_get_int(config,key,"reg_sendregister",0));
linphone_proxy_config_enable_publish(cfg,lp_config_get_int(config,key,"publish",0));
linphone_proxy_config_set_dial_escape_plus(cfg,lp_config_get_int(config,key,"dial_escape_plus",lp_config_get_default_int(config,"proxy","dial_escape_plus",0)));
linphone_proxy_config_set_dial_prefix(cfg,lp_config_get_string(config,key,"dial_prefix",lp_config_get_default_string(config,"proxy","dial_prefix",NULL)));
tmp=lp_config_get_string(config,key,"type",NULL);
if (tmp!=NULL && strlen(tmp)>0)
if (tmp!=NULL && strlen(tmp)>0)
linphone_proxy_config_set_sip_setup(cfg,tmp);
linphone_proxy_config_set_privacy(cfg,lp_config_get_int(config,key,"privacy",lp_config_get_default_int(config,"proxy","privacy",LinphonePrivacyDefault)));
@ -1155,7 +1198,7 @@ static void linphone_proxy_config_activate_sip_setup(LinphoneProxyConfig *cfg){
ms_error("Could not retrieve proxy uri !");
}
}
}
SipSetup *linphone_proxy_config_get_sip_setup(LinphoneProxyConfig *cfg){
@ -1170,7 +1213,7 @@ static bool_t can_register(LinphoneProxyConfig *cfg){
LinphoneCore *lc=cfg->lc;
#ifdef BUILD_UPNP
if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp){
if(lc->sip_conf.register_only_when_upnp_is_ok &&
if(lc->sip_conf.register_only_when_upnp_is_ok &&
(lc->upnp == NULL || !linphone_upnp_context_is_ready_for_register(lc->upnp))) {
return FALSE;
}
@ -1326,7 +1369,7 @@ void * linphone_proxy_config_get_user_data(LinphoneProxyConfig *cr) {
void linphone_proxy_config_set_state(LinphoneProxyConfig *cfg, LinphoneRegistrationState state, const char *message){
LinphoneCore *lc=cfg->lc;
bool_t update_friends=FALSE;
if (cfg->state!=state || state==LinphoneRegistrationOk) { /*allow multiple notification of LinphoneRegistrationOk for refreshing*/
ms_message("Proxy config [%p] for identity [%s] moving from state [%s] to [%s]" , cfg,
linphone_proxy_config_get_identity(cfg),
@ -1337,7 +1380,7 @@ void linphone_proxy_config_set_state(LinphoneProxyConfig *cfg, LinphoneRegistrat
|| (state!=LinphoneRegistrationOk && cfg->state==LinphoneRegistrationOk);
}
cfg->state=state;
if (update_friends){
linphone_core_update_friends_subscriptions(lc,cfg,TRUE);
}

524
coreapi/quality_reporting.c Normal file
View file

@ -0,0 +1,524 @@
/*
linphone
Copyright (C) 2014 - Belledonne Communications, Grenoble, France
This program 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 2
of the License, or (at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include "linphonecore.h"
#include "private.h"
#include "sal/sal.h"
#include "ortp/rtpsession.h"
#include <math.h>
/***************************************************************************
* TODO / REMINDER LIST
****************************************************************************/
// to discuss
// For codecs that are able to change sample rates, the lowest and highest sample rates MUST be reported (e.g., 8000;16000).
// moslq == moscq
// video: what happens if doing stop/resume?
// one time value: average? worst value?
// rlq value: need algo to compute it
/***************************************************************************
* END OF TODO / REMINDER LIST
****************************************************************************/
#define STR_REASSIGN(dest, src) {\
if (dest != NULL) \
ms_free(dest); \
dest = src; \
}
// since printf family functions are LOCALE dependent, float separator may differ
// depending on the user's locale (LC_NUMERIC env var).
static char * float_to_one_decimal_string(float f) {
float rounded_f = floorf(f * 10 + .5f) / 10;
int floor_part = (int) rounded_f;
int one_decimal_part = floorf (10 * (rounded_f - floor_part) + .5f);
return ms_strdup_printf(_("%d.%d"), floor_part, one_decimal_part);
}
static void append_to_buffer_valist(char **buff, size_t *buff_size, size_t *offset, const char *fmt, va_list args) {
belle_sip_error_code ret;
size_t prevoffset = *offset;
#ifndef WIN32
va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
va_copy(cap,args);
ret = belle_sip_snprintf_valist(*buff, *buff_size, offset, fmt, cap);
va_end(cap);
#else
ret = belle_sip_snprintf_valist(*buff, *buff_size, offset, fmt, args);
#endif
// if we are out of memory, we add some size to buffer
if (ret == BELLE_SIP_BUFFER_OVERFLOW) {
ms_warning("Buffer was too small to contain the whole report - doubling its size from %lu to %lu", *buff_size, 2 * *buff_size);
*buff_size += 2048;
*buff = (char *) ms_realloc(*buff, *buff_size);
*offset = prevoffset;
// recall myself since we did not write all things into the buffer but
// only a part of it
append_to_buffer_valist(buff, buff_size, offset, fmt, args);
}
}
static void append_to_buffer(char **buff, size_t *buff_size, size_t *offset, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
append_to_buffer_valist(buff, buff_size, offset, fmt, args);
va_end(args);
}
#define APPEND_IF_NOT_NULL_STR(buffer, size, offset, fmt, arg) if (arg != NULL) append_to_buffer(buffer, size, offset, fmt, arg)
#define APPEND_IF_NUM_IN_RANGE(buffer, size, offset, fmt, arg, inf, sup) if (inf <= arg && arg <= sup) append_to_buffer(buffer, size, offset, fmt, arg)
#define APPEND_IF(buffer, size, offset, fmt, arg, cond) if (cond) append_to_buffer(buffer, size, offset, fmt, arg)
#define IF_NUM_IN_RANGE(num, inf, sup, statement) if (inf <= num && num <= sup) statement
static bool_t are_metrics_filled(const reporting_content_metrics_t rm) {
IF_NUM_IN_RANGE(rm.packet_loss.network_packet_loss_rate, 0, 255, return TRUE);
IF_NUM_IN_RANGE(rm.packet_loss.jitter_buffer_discard_rate, 0, 255, return TRUE);
IF_NUM_IN_RANGE(rm.quality_estimates.moslq, 1, 5, return TRUE);
IF_NUM_IN_RANGE(rm.quality_estimates.moscq, 1, 5, return TRUE);
// since these are same values than local ones, do not check them
// if (rm.session_description.payload_type != -1) return TRUE;
// if (rm.session_description.payload_desc != NULL) return TRUE;
// if (rm.session_description.sample_rate != -1) return TRUE;
if (rm.session_description.frame_duration != -1) return TRUE;
// if (rm.session_description.fmtp != NULL) return TRUE;
if (rm.session_description.packet_loss_concealment != -1) return TRUE;
IF_NUM_IN_RANGE(rm.jitter_buffer.adaptive, 0, 3, return TRUE);
IF_NUM_IN_RANGE(rm.jitter_buffer.nominal, 0, 65535, return TRUE);
IF_NUM_IN_RANGE(rm.jitter_buffer.max, 0, 65535, return TRUE);
IF_NUM_IN_RANGE(rm.jitter_buffer.abs_max, 0, 65535, return TRUE);
IF_NUM_IN_RANGE(rm.delay.round_trip_delay, 0, 65535, return TRUE);
IF_NUM_IN_RANGE(rm.delay.end_system_delay, 0, 65535, return TRUE);
IF_NUM_IN_RANGE(rm.delay.symm_one_way_delay, 0, 65535, return TRUE);
IF_NUM_IN_RANGE(rm.delay.interarrival_jitter, 0, 65535, return TRUE);
IF_NUM_IN_RANGE(rm.delay.mean_abs_jitter, 0, 65535, return TRUE);
if (rm.signal.level != 127) return TRUE;
if (rm.signal.noise_level != 127) return TRUE;
IF_NUM_IN_RANGE(rm.quality_estimates.rlq, 1, 120, return TRUE);
IF_NUM_IN_RANGE(rm.quality_estimates.rcq, 1, 120, return TRUE);
return FALSE;
}
static void append_metrics_to_buffer(char ** buffer, size_t * size, size_t * offset, const reporting_content_metrics_t rm) {
char * timestamps_start_str = NULL;
char * timestamps_stop_str = NULL;
char * network_packet_loss_rate_str = NULL;
char * jitter_buffer_discard_rate_str = NULL;
// char * gap_loss_density_str = NULL;
char * moslq_str = NULL;
char * moscq_str = NULL;
if (rm.timestamps.start > 0)
timestamps_start_str = linphone_timestamp_to_rfc3339_string(rm.timestamps.start);
if (rm.timestamps.stop > 0)
timestamps_stop_str = linphone_timestamp_to_rfc3339_string(rm.timestamps.stop);
IF_NUM_IN_RANGE(rm.packet_loss.network_packet_loss_rate, 0, 255, network_packet_loss_rate_str = float_to_one_decimal_string(rm.packet_loss.network_packet_loss_rate / 256));
IF_NUM_IN_RANGE(rm.packet_loss.jitter_buffer_discard_rate, 0, 255, jitter_buffer_discard_rate_str = float_to_one_decimal_string(rm.packet_loss.jitter_buffer_discard_rate / 256));
// IF_NUM_IN_RANGE(rm.burst_gap_loss.gap_loss_density, 0, 10, gap_loss_density_str = float_to_one_decimal_string(rm.burst_gap_loss.gap_loss_density));
IF_NUM_IN_RANGE(rm.quality_estimates.moslq, 1, 5, moslq_str = float_to_one_decimal_string(rm.quality_estimates.moslq));
IF_NUM_IN_RANGE(rm.quality_estimates.moscq, 1, 5, moscq_str = float_to_one_decimal_string(rm.quality_estimates.moscq));
append_to_buffer(buffer, size, offset, "Timestamps:");
APPEND_IF_NOT_NULL_STR(buffer, size, offset, " START=%s", timestamps_start_str);
APPEND_IF_NOT_NULL_STR(buffer, size, offset, " STOP=%s", timestamps_stop_str);
append_to_buffer(buffer, size, offset, "\r\nSessionDesc:");
APPEND_IF(buffer, size, offset, " PT=%d", rm.session_description.payload_type, rm.session_description.payload_type != -1);
APPEND_IF_NOT_NULL_STR(buffer, size, offset, " PD=%s", rm.session_description.payload_desc);
APPEND_IF(buffer, size, offset, " SR=%d", rm.session_description.sample_rate, rm.session_description.sample_rate != -1);
APPEND_IF(buffer, size, offset, " FD=%d", rm.session_description.frame_duration, rm.session_description.frame_duration != -1);
// append_to_buffer(buffer, size, offset, " FO=%d", rm.session_description.frame_ocets);
// append_to_buffer(buffer, size, offset, " FPP=%d", rm.session_description.frames_per_sec);
// append_to_buffer(buffer, size, offset, " PPS=%d", rm.session_description.packets_per_sec);
APPEND_IF_NOT_NULL_STR(buffer, size, offset, " FMTP=\"%s\"", rm.session_description.fmtp);
APPEND_IF(buffer, size, offset, " PLC=%d", rm.session_description.packet_loss_concealment, rm.session_description.packet_loss_concealment != -1);
// APPEND_IF_NOT_NULL_STR(buffer, size, offset, " SSUP=%s", rm.session_description.silence_suppression_state);
append_to_buffer(buffer, size, offset, "\r\nJitterBuffer:");
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " JBA=%d", rm.jitter_buffer.adaptive, 0, 3);
// APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " JBR=%d", rm.jitter_buffer.rate, 0, 15);
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " JBN=%d", rm.jitter_buffer.nominal, 0, 65535);
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " JBM=%d", rm.jitter_buffer.max, 0, 65535);
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " JBX=%d", rm.jitter_buffer.abs_max, 0, 65535);
append_to_buffer(buffer, size, offset, "\r\nPacketLoss:");
APPEND_IF_NOT_NULL_STR(buffer, size, offset, " NLR=%s", network_packet_loss_rate_str);
APPEND_IF_NOT_NULL_STR(buffer, size, offset, " JDR=%s", jitter_buffer_discard_rate_str);
// append_to_buffer(buffer, size, offset, "\r\nBurstGapLoss:");
// append_to_buffer(buffer, size, offset, " BLD=%d", rm.burst_gap_loss.burst_loss_density);
// append_to_buffer(buffer, size, offset, " BD=%d", rm.burst_gap_loss.burst_duration);
// APPEND_IF_NOT_NULL_STR(buffer, size, offset, " GLD=%s", gap_loss_density_str);
// append_to_buffer(buffer, size, offset, " GD=%d", rm.burst_gap_loss.gap_duration);
// append_to_buffer(buffer, size, offset, " GMIN=%d", rm.burst_gap_loss.min_gap_threshold);
append_to_buffer(buffer, size, offset, "\r\nDelay:");
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " RTD=%d", rm.delay.round_trip_delay, 0, 65535);
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " ESD=%d", rm.delay.end_system_delay, 0, 65535);
// APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " OWD=%d", rm.delay.one_way_delay, 0, 65535);
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " SOWD=%d", rm.delay.symm_one_way_delay, 0, 65535);
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " IAJ=%d", rm.delay.interarrival_jitter, 0, 65535);
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " MAJ=%d", rm.delay.mean_abs_jitter, 0, 65535);
append_to_buffer(buffer, size, offset, "\r\nSignal:");
APPEND_IF(buffer, size, offset, " SL=%d", rm.signal.level, rm.signal.level != 127);
APPEND_IF(buffer, size, offset, " NL=%d", rm.signal.noise_level, rm.signal.noise_level != 127);
// append_to_buffer(buffer, size, offset, " RERL=%d", rm.signal.residual_echo_return_loss);
append_to_buffer(buffer, size, offset, "\r\nQualityEst:");
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " RLQ=%d", rm.quality_estimates.rlq, 1, 120);
// APPEND_IF_NOT_NULL_STR(buffer, size, offset, " RLQEstAlg=%s", rm.quality_estimates.rlqestalg);
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " RCQ=%d", rm.quality_estimates.rcq, 1, 120);
// APPEND_IF_NOT_NULL_STR(buffer, size, offset, " RCQEstAlgo=%s", rm.quality_estimates.rcqestalg);
// append_to_buffer(buffer, size, offset, " EXTRI=%d", rm.quality_estimates.extri);
// APPEND_IF_NOT_NULL_STR(buffer, size, offset, " ExtRIEstAlg=%s", rm.quality_estimates.extriestalg);
// append_to_buffer(buffer, size, offset, " EXTRO=%d", rm.quality_estimates.extro);
// APPEND_IF_NOT_NULL_STR(buffer, size, offset, " ExtROEstAlg=%s", rm.quality_estimates.extroutestalg);
APPEND_IF_NOT_NULL_STR(buffer, size, offset, " MOSLQ=%s", moslq_str);
// APPEND_IF_NOT_NULL_STR(buffer, size, offset, " MOSLQEstAlgo=%s", rm.quality_estimates.moslqestalg);
APPEND_IF_NOT_NULL_STR(buffer, size, offset, " MOSCQ=%s", moscq_str);
// APPEND_IF_NOT_NULL_STR(buffer, size, offset, " MOSCQEstAlgo=%s", rm.quality_estimates.moscqestalg);
// APPEND_IF_NOT_NULL_STR(buffer, size, offset, " QoEEstAlg=%s", rm.quality_estimates.qoestalg);
append_to_buffer(buffer, size, offset, "\r\n");
ms_free(timestamps_start_str);
ms_free(timestamps_stop_str);
ms_free(network_packet_loss_rate_str);
ms_free(jitter_buffer_discard_rate_str);
// ms_free(gap_loss_density_str);
ms_free(moslq_str);
ms_free(moscq_str);
}
static void reporting_publish(const LinphoneCall* call, const reporting_session_report_t * report) {
LinphoneContent content = {0};
LinphoneAddress *addr;
int expires = -1;
size_t offset = 0;
size_t size = 2048;
char * buffer;
// if the call was hungup too early, we might have invalid IPs information
// in that case, we abort the report since it's not useful data
if (strlen(report->info.local_addr.ip) == 0 || strlen(report->info.remote_addr.ip) == 0) {
ms_warning("The call was hang up too early (duration: %d sec) and IP could "
"not be retrieved so dropping this report", linphone_call_get_duration(call));
return;
}
buffer = (char *) ms_malloc(size);
content.type = ms_strdup("application");
content.subtype = ms_strdup("vq-rtcpxr");
append_to_buffer(&buffer, &size, &offset, "VQSessionReport: CallTerm\r\n");
append_to_buffer(&buffer, &size, &offset, "CallID: %s\r\n", report->info.call_id);
append_to_buffer(&buffer, &size, &offset, "LocalID: %s\r\n", report->info.local_id);
append_to_buffer(&buffer, &size, &offset, "RemoteID: %s\r\n", report->info.remote_id);
append_to_buffer(&buffer, &size, &offset, "OrigID: %s\r\n", report->info.orig_id);
APPEND_IF_NOT_NULL_STR(&buffer, &size, &offset, "LocalGroup: %s\r\n", report->info.local_group);
APPEND_IF_NOT_NULL_STR(&buffer, &size, &offset, "RemoteGroup: %s\r\n", report->info.remote_group);
append_to_buffer(&buffer, &size, &offset, "LocalAddr: IP=%s PORT=%d SSRC=%d\r\n", report->info.local_addr.ip, report->info.local_addr.port, report->info.local_addr.ssrc);
APPEND_IF_NOT_NULL_STR(&buffer, &size, &offset, "LocalMAC: %s\r\n", report->info.local_mac_addr);
append_to_buffer(&buffer, &size, &offset, "RemoteAddr: IP=%s PORT=%d SSRC=%d\r\n", report->info.remote_addr.ip, report->info.remote_addr.port, report->info.remote_addr.ssrc);
APPEND_IF_NOT_NULL_STR(&buffer, &size, &offset, "RemoteMAC: %s\r\n", report->info.remote_mac_addr);
append_to_buffer(&buffer, &size, &offset, "LocalMetrics:\r\n");
append_metrics_to_buffer(&buffer, &size, &offset, report->local_metrics);
if (are_metrics_filled(report->remote_metrics)) {
append_to_buffer(&buffer, &size, &offset, "RemoteMetrics:\r\n");
append_metrics_to_buffer(&buffer, &size, &offset, report->remote_metrics);
}
APPEND_IF_NOT_NULL_STR(&buffer, &size, &offset, "DialogID: %s\r\n", report->dialog_id);
content.data = buffer;
content.size = strlen((char*)content.data);
addr = linphone_address_new(call->dest_proxy->reg_statistics_collector);
if (addr != NULL) {
linphone_core_publish(call->core, addr, "vq-rtcpxr", expires, &content);
linphone_address_destroy(addr);
} else {
ms_warning("Asked to submit reporting statistics but no collector address found");
}
linphone_content_uninit(&content);
}
static const SalStreamDescription * get_media_stream_for_desc(const SalMediaDescription * smd, SalStreamType sal_stream_type) {
int count;
if (smd != NULL) {
for (count = 0; count < smd->n_total_streams; ++count) {
if (smd->streams[count].type == sal_stream_type) {
return &smd->streams[count];
}
}
}
if (smd == NULL || count == smd->n_total_streams) {
ms_warning("Could not find the associated stream of type %d", sal_stream_type);
}
return NULL;
}
static void reporting_update_ip(LinphoneCall * call, int stats_type) {
SalStreamType sal_stream_type = (stats_type == LINPHONE_CALL_STATS_AUDIO) ? SalAudio : SalVideo;
if (call->log->reports[stats_type] != NULL) {
const SalStreamDescription * local_desc = get_media_stream_for_desc(call->localdesc, sal_stream_type);
const SalStreamDescription * remote_desc = get_media_stream_for_desc(sal_call_get_remote_media_description(call->op), sal_stream_type);
// local info are always up-to-date and correct
if (local_desc != NULL) {
call->log->reports[stats_type]->info.local_addr.port = local_desc->rtp_port;
STR_REASSIGN(call->log->reports[stats_type]->info.local_addr.ip, ms_strdup(local_desc->rtp_addr));
}
if (remote_desc != NULL) {
// port is always stored in stream description struct
call->log->reports[stats_type]->info.remote_addr.port = remote_desc->rtp_port;
// for IP it can be not set if we are using a direct route
if (remote_desc->rtp_addr != NULL && strlen(remote_desc->rtp_addr) > 0) {
STR_REASSIGN(call->log->reports[stats_type]->info.remote_addr.ip, ms_strdup(remote_desc->rtp_addr));
} else {
STR_REASSIGN(call->log->reports[stats_type]->info.remote_addr.ip, ms_strdup(sal_call_get_remote_media_description(call->op)->addr));
}
}
}
}
static bool_t reporting_enabled(const LinphoneCall * call) {
return (call->dest_proxy != NULL && linphone_proxy_config_send_statistics_enabled(call->dest_proxy));
}
void linphone_reporting_update_ip(LinphoneCall * call) {
// This function can be called in two different cases:
// - 1) at start when call is starting, remote ip/port info might be the proxy ones to which callee is registered
// - 2) later, if we found a direct route between caller and callee with ICE/Stun, ip/port are updated for the direct route access
if (! reporting_enabled(call))
return;
reporting_update_ip(call, LINPHONE_CALL_STATS_AUDIO);
if (linphone_call_params_video_enabled(linphone_call_get_current_params(call))) {
reporting_update_ip(call, LINPHONE_CALL_STATS_VIDEO);
}
}
void linphone_reporting_update(LinphoneCall * call, int stats_type) {
reporting_session_report_t * report = call->log->reports[stats_type];
MediaStream * stream = NULL;
const PayloadType * local_payload = NULL;
const PayloadType * remote_payload = NULL;
const LinphoneCallParams * current_params = linphone_call_get_current_params(call);
if (! reporting_enabled(call))
return;
STR_REASSIGN(report->info.call_id, ms_strdup(call->log->call_id));
STR_REASSIGN(report->info.local_group, ms_strdup_printf(_("linphone-%s-%s-%s"), (stats_type == LINPHONE_CALL_STATS_AUDIO ? "audio" : "video"),
linphone_core_get_user_agent_name(), report->info.call_id));
STR_REASSIGN(report->info.remote_group, ms_strdup_printf(_("linphone-%s-%s-%s"), (stats_type == LINPHONE_CALL_STATS_AUDIO ? "audio" : "video"),
linphone_call_get_remote_user_agent(call), report->info.call_id));
if (call->dir == LinphoneCallIncoming) {
STR_REASSIGN(report->info.remote_id, linphone_address_as_string(call->log->from));
STR_REASSIGN(report->info.local_id, linphone_address_as_string(call->log->to));
STR_REASSIGN(report->info.orig_id, ms_strdup(report->info.remote_id));
} else {
STR_REASSIGN(report->info.remote_id, linphone_address_as_string(call->log->to));
STR_REASSIGN(report->info.local_id, linphone_address_as_string(call->log->from));
STR_REASSIGN(report->info.orig_id, ms_strdup(report->info.local_id));
}
STR_REASSIGN(report->dialog_id, sal_op_get_dialog_id(call->op));
report->local_metrics.timestamps.start = call->log->start_date_time;
report->local_metrics.timestamps.stop = call->log->start_date_time + linphone_call_get_duration(call);
//we use same timestamps for remote too
report->remote_metrics.timestamps.start = call->log->start_date_time;
report->remote_metrics.timestamps.stop = call->log->start_date_time + linphone_call_get_duration(call);
// yet we use the same payload config for local and remote, since this is the largest use case
if (stats_type == LINPHONE_CALL_STATS_AUDIO && call->audiostream != NULL) {
stream = &call->audiostream->ms;
local_payload = linphone_call_params_get_used_audio_codec(current_params);
remote_payload = local_payload;
} else if (stats_type == LINPHONE_CALL_STATS_VIDEO && call->videostream != NULL) {
stream = &call->videostream->ms;
local_payload = linphone_call_params_get_used_video_codec(current_params);
remote_payload = local_payload;
}
if (stream != NULL) {
RtpSession * session = stream->sessions.rtp_session;
report->info.local_addr.ssrc = rtp_session_get_send_ssrc(session);
report->info.remote_addr.ssrc = rtp_session_get_recv_ssrc(session);
}
if (local_payload != NULL) {
report->local_metrics.session_description.payload_type = local_payload->type;
STR_REASSIGN(report->local_metrics.session_description.payload_desc, ms_strdup(local_payload->mime_type));
report->local_metrics.session_description.sample_rate = local_payload->clock_rate;
STR_REASSIGN(report->local_metrics.session_description.fmtp, ms_strdup(local_payload->recv_fmtp));
}
if (remote_payload != NULL) {
report->remote_metrics.session_description.payload_type = remote_payload->type;
STR_REASSIGN(report->remote_metrics.session_description.payload_desc, ms_strdup(remote_payload->mime_type));
report->remote_metrics.session_description.sample_rate = remote_payload->clock_rate;
STR_REASSIGN(report->remote_metrics.session_description.fmtp, ms_strdup(remote_payload->recv_fmtp));
}
}
void linphone_reporting_call_stats_updated(LinphoneCall *call, int stats_type) {
reporting_session_report_t * report = call->log->reports[stats_type];
reporting_content_metrics_t * metrics = NULL;
LinphoneCallStats stats = call->stats[stats_type];
mblk_t *block = NULL;
if (! reporting_enabled(call))
return;
if (stats.updated == LINPHONE_CALL_STATS_RECEIVED_RTCP_UPDATE) {
metrics = &report->remote_metrics;
if (rtcp_is_XR(stats.received_rtcp) == TRUE) {
block = stats.received_rtcp;
}
} else if (stats.updated == LINPHONE_CALL_STATS_SENT_RTCP_UPDATE) {
metrics = &report->local_metrics;
if (rtcp_is_XR(stats.sent_rtcp) == TRUE) {
block = stats.sent_rtcp;
}
}
if (block != NULL) {
switch (rtcp_XR_get_block_type(block)) {
case RTCP_XR_VOIP_METRICS: {
metrics->quality_estimates.rcq = rtcp_XR_voip_metrics_get_r_factor(block);
metrics->quality_estimates.moslq = rtcp_XR_voip_metrics_get_mos_lq(block) / 10.f;
metrics->quality_estimates.moscq = rtcp_XR_voip_metrics_get_mos_cq(block) / 10.f;
metrics->jitter_buffer.nominal = rtcp_XR_voip_metrics_get_jb_nominal(block);
metrics->jitter_buffer.max = rtcp_XR_voip_metrics_get_jb_maximum(block);
metrics->jitter_buffer.abs_max = rtcp_XR_voip_metrics_get_jb_abs_max(block);
metrics->packet_loss.network_packet_loss_rate = rtcp_XR_voip_metrics_get_loss_rate(block);
metrics->packet_loss.jitter_buffer_discard_rate = rtcp_XR_voip_metrics_get_discard_rate(block);
uint8_t config = rtcp_XR_voip_metrics_get_rx_config(block);
metrics->session_description.packet_loss_concealment = (config >> 6) & 0x3;
metrics->jitter_buffer.adaptive = (config >> 4) & 0x3;
break;
} default: {
break;
}
}
}
}
void linphone_reporting_publish(LinphoneCall* call) {
if (! reporting_enabled(call))
return;
if (call->log->reports[LINPHONE_CALL_STATS_AUDIO] != NULL) {
reporting_publish(call, call->log->reports[LINPHONE_CALL_STATS_AUDIO]);
}
if (call->log->reports[LINPHONE_CALL_STATS_VIDEO] != NULL
&& linphone_call_params_video_enabled(linphone_call_get_current_params(call))) {
reporting_publish(call, call->log->reports[LINPHONE_CALL_STATS_VIDEO]);
}
}
reporting_session_report_t * linphone_reporting_new() {
int i;
reporting_session_report_t * rm = ms_new0(reporting_session_report_t,1);
reporting_content_metrics_t * metrics[2] = {&rm->local_metrics, &rm->remote_metrics};
for (i = 0; i < 2; i++) {
metrics[i]->session_description.payload_type = -1;
metrics[i]->session_description.sample_rate = -1;
metrics[i]->session_description.frame_duration = -1;
metrics[i]->packet_loss.network_packet_loss_rate = -1;
metrics[i]->packet_loss.jitter_buffer_discard_rate = -1;
metrics[i]->session_description.packet_loss_concealment = -1;
metrics[i]->jitter_buffer.adaptive = -1;
// metrics[i]->jitter_buffer.rate = -1;
metrics[i]->jitter_buffer.nominal = -1;
metrics[i]->jitter_buffer.max = -1;
metrics[i]->jitter_buffer.abs_max = -1;
metrics[i]->delay.round_trip_delay = -1;
metrics[i]->delay.end_system_delay = -1;
// metrics[i]->delay.one_way_delay = -1;
metrics[i]->delay.symm_one_way_delay = -1;
metrics[i]->delay.interarrival_jitter = -1;
metrics[i]->delay.mean_abs_jitter = -1;
metrics[i]->signal.level = 127;
metrics[i]->signal.noise_level = 127;
}
return rm;
}
void linphone_reporting_destroy(reporting_session_report_t * report) {
if (report->info.call_id != NULL) ms_free(report->info.call_id);
if (report->info.local_id != NULL) ms_free(report->info.local_id);
if (report->info.remote_id != NULL) ms_free(report->info.remote_id);
if (report->info.orig_id != NULL) ms_free(report->info.orig_id);
if (report->info.local_addr.ip != NULL) ms_free(report->info.local_addr.ip);
if (report->info.remote_addr.ip != NULL) ms_free(report->info.remote_addr.ip);
if (report->info.local_group != NULL) ms_free(report->info.local_group);
if (report->info.remote_group != NULL) ms_free(report->info.remote_group);
if (report->info.local_mac_addr != NULL) ms_free(report->info.local_mac_addr);
if (report->info.remote_mac_addr != NULL) ms_free(report->info.remote_mac_addr);
if (report->dialog_id != NULL) ms_free(report->dialog_id);
if (report->local_metrics.session_description.fmtp != NULL) ms_free(report->local_metrics.session_description.fmtp);
if (report->local_metrics.session_description.payload_desc != NULL) ms_free(report->local_metrics.session_description.payload_desc);
if (report->remote_metrics.session_description.fmtp != NULL) ms_free(report->remote_metrics.session_description.fmtp);
if (report->remote_metrics.session_description.payload_desc != NULL) ms_free(report->remote_metrics.session_description.payload_desc);
ms_free(report);
}

149
coreapi/quality_reporting.h Normal file
View file

@ -0,0 +1,149 @@
/*
linphone
Copyright (C) 2014 - Belledonne Communications, Grenoble, France
This program 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 2
of the License, or (at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef quality_reporting_h
#define quality_reporting_h
#include "linphonecore.h"
#ifdef __cplusplus
extern "C"{
#endif
typedef struct reporting_addr {
char * ip;
int port;
uint32_t ssrc;
} reporting_addr_t;
typedef struct reporting_content_metrics {
// timestamps - mandatory
struct {
time_t start;
time_t stop;
} timestamps;
// session description - optional
struct {
int payload_type;
char * payload_desc; // mime type
int sample_rate; // clock rate
int frame_duration; // to check (ptime?) - audio only
// int frame_ocets;
// int frames_per_sec;
// int packets_per_sec;
char * fmtp;
int packet_loss_concealment; // in voip metrics - audio only
// char * silence_suppression_state;
} session_description;
// jitter buffet - optional
struct {
int adaptive; // constant
// int rate; // constant
int nominal; // no may vary during the call <- average? worst score?
int max; // no may vary during the call <- average?
int abs_max; // constant
} jitter_buffer;
// packet loss - optional
struct {
float network_packet_loss_rate; // voip metrics (loss rate) + conversion
float jitter_buffer_discard_rate; //idem
} packet_loss;
// burst gap loss - optional
// (no) currently not implemented
// struct {
// int burst_loss_density;
// int burst_duration;
// float gap_loss_density;
// int gap_duration;
// int min_gap_threshold;
// } burst_gap_loss;
// delay - optional
struct {
int round_trip_delay; // no - vary
int end_system_delay; // no - not implemented yet
// int one_way_delay;
int symm_one_way_delay; // no - vary (depends on round_trip_delay) + not implemented (depends on end_system_delay)
int interarrival_jitter; // no - not implemented yet
int mean_abs_jitter; // to check
} delay;
// signal - optional
struct {
int level; // no - vary
int noise_level; // no - vary
// int residual_echo_return_loss;
} signal;
// quality estimates - optional
struct {
int rlq; // linked to moslq - in [0..120]
int rcq; //voip metrics R factor - no - vary or avg in [0..120]
float moslq; // no - vary or avg - voip metrics - in [0..4.9]
float moscq; // no - vary or avg - voip metrics - in [0..4.9]
// int extri;
// int extro;
// char * rlqestalg;
// char * rcqestalg;
// char * moslqestalg;
// char * moscqestalg;
// char * extriestalg;
// char * extroutestalg;
// char * qoestalg;
} quality_estimates;
} reporting_content_metrics_t;
typedef struct reporting_session_report {
struct {
char * call_id;
char * local_id;
char * remote_id;
char * orig_id;
reporting_addr_t local_addr;
reporting_addr_t remote_addr;
char * local_group;
char * remote_group;
char * local_mac_addr; // optional
char * remote_mac_addr; // optional
} info;
reporting_content_metrics_t local_metrics;
reporting_content_metrics_t remote_metrics; // optional
char * dialog_id; // optional
} reporting_session_report_t;
reporting_session_report_t * linphone_reporting_new();
void linphone_reporting_destroy(reporting_session_report_t * report);
void linphone_reporting_update(LinphoneCall * call, int stats_type);
void linphone_reporting_update_ip(LinphoneCall * call);
void linphone_reporting_publish(LinphoneCall* call);
void linphone_reporting_call_stats_updated(LinphoneCall *call, int stats_type);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -17,9 +17,9 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/**
/**
This header files defines the Signaling Abstraction Layer.
The purpose of this layer is too allow experiment different call signaling
The purpose of this layer is too allow experiment different call signaling
protocols and implementations under linphone, for example SIP, JINGLE...
**/
#ifdef HAVE_CONFIG_H
@ -39,7 +39,7 @@ const char* sal_transport_to_string(SalTransport transport) {
default: {
ms_fatal("Unexpected transport [%i]",transport);
return NULL;
}
}
}
}
@ -372,6 +372,14 @@ const char *sal_op_get_network_origin(const SalOp *op){
const char* sal_op_get_call_id(const SalOp *op) {
return ((SalOpBase*)op)->call_id;
}
char* sal_op_get_dialog_id(const SalOp *op) {
if (op->dialog != NULL) {
return ms_strdup_printf("%s;to-tag=%s;from-tag=%s", ((SalOpBase*)op)->call_id,
belle_sip_dialog_get_remote_tag(op->dialog), belle_sip_dialog_get_local_tag(op->dialog));
}
return NULL;
}
void __sal_op_init(SalOp *b, Sal *sal){
memset(b,0,sizeof(SalOpBase));
((SalOpBase*)b)->root=sal;
@ -401,17 +409,17 @@ void __sal_op_free(SalOp *op){
sal_address_destroy(b->to_address);
b->to_address=NULL;
}
if (b->service_route){
sal_address_destroy(b->service_route);
b->service_route=NULL;
}
if (b->origin_address){
sal_address_destroy(b->origin_address);
b->origin_address=NULL;
}
if (b->from) {
ms_free(b->from);
b->from=NULL;
@ -616,7 +624,7 @@ static int line_get_value(const char *input, const char *key, char *value, size_
int sal_lines_get_value(const char *data, const char *key, char *value, size_t value_size){
int read=0;
do{
if (line_get_value(data,key,value,value_size,&read))
return TRUE;
@ -626,7 +634,7 @@ int sal_lines_get_value(const char *data, const char *key, char *value, size_t v
}
int sal_body_has_type(const SalBody *body, const char *type, const char *subtype){
return body->type && body->subtype
return body->type && body->subtype
&& strcmp(body->type,type)==0
&& strcmp(body->subtype,subtype)==0;
}

View file

@ -17,9 +17,9 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/**
/**
This header files defines the Signaling Abstraction Layer.
The purpose of this layer is too allow experiment different call signaling
The purpose of this layer is too allow experiment different call signaling
protocols and implementations under linphone, for example SIP, JINGLE...
**/
@ -571,6 +571,7 @@ const SalAddress *sal_op_get_network_origin_address(const SalOp *op);
const char *sal_op_get_remote_ua(const SalOp *op);
void *sal_op_get_user_pointer(const SalOp *op);
const char* sal_op_get_call_id(const SalOp *op);
char* sal_op_get_dialog_id(const SalOp *op);
const SalAddress* sal_op_get_service_route(const SalOp *op);
void sal_op_set_service_route(SalOp *op,const SalAddress* service_route);

View file

@ -119,10 +119,10 @@ void liblinphone_tester_check_rtcp(LinphoneCoreManager* caller, LinphoneCoreMana
c1=linphone_core_get_current_call(caller->lc);
c2=linphone_core_get_current_call(callee->lc);
CU_ASSERT_PTR_NOT_NULL(c1);
CU_ASSERT_PTR_NOT_NULL(c2);
if (!c1 || !c2) return;
for (i=0; i<24 /*=12s need at least one exchange of SR to maybe 10s*/; i++) {
@ -339,7 +339,7 @@ static void cancelled_call(void) {
static void disable_all_codecs_except_one(LinphoneCore *lc, const char *mime){
const MSList *elem=linphone_core_get_audio_codecs(lc);
PayloadType *pt;
for(;elem!=NULL;elem=elem->next){
pt=(PayloadType*)elem->data;
linphone_core_enable_payload_type(lc,pt,FALSE);
@ -374,7 +374,7 @@ static void call_with_dns_time_out(void) {
LinphoneCoreManager* marie = linphone_core_manager_new2( "empty_rc", FALSE);
LCSipTransports transport = {9773,0,0,0};
int i;
linphone_core_set_sip_transports(marie->lc,&transport);
linphone_core_iterate(marie->lc);
sal_set_dns_timeout(marie->lc->sal,0);
@ -382,7 +382,7 @@ static void call_with_dns_time_out(void) {
for(i=0;i<10;i++){
ms_usleep(200000);
linphone_core_iterate(marie->lc);
}
}
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallOutgoingInit,1);
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallOutgoingProgress,1);
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallError,1);
@ -395,21 +395,21 @@ static void early_cancelled_call(void) {
LinphoneCoreManager* pauline = linphone_core_manager_new2( "empty_rc",FALSE);
LinphoneCall* out_call = linphone_core_invite_address(pauline->lc,marie->identity);
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallOutgoingInit,1));
linphone_core_terminate_call(pauline->lc,out_call);
/*since everything is executed in a row, no response can be received from the server, thus the CANCEL cannot be sent.
It will ring at Marie's side.*/
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallEnd,1));
CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallEnd,1);
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallIncomingReceived,1));
/* now the CANCEL should have been sent and the the call at marie's side should terminate*/
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallEnd,1));
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallReleased,1));
linphone_core_manager_destroy(marie);
@ -449,7 +449,7 @@ static void early_declined_call(void) {
CU_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallError,1,33000));
CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallError,1);
/* FIXME http://git.linphone.org/mantis/view.php?id=757
CU_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonBusy);
*/
if (ms_list_size(linphone_core_get_call_logs(pauline->lc))>0) {
@ -489,7 +489,7 @@ static void call_declined(void) {
static void call_terminated_by_caller(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
CU_ASSERT_TRUE(call(pauline,marie));
/*just to sleep*/
linphone_core_terminate_all_calls(pauline->lc);
@ -503,7 +503,7 @@ static void call_terminated_by_caller(void) {
static void call_with_no_sdp(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
linphone_core_enable_sdp_200_ack(marie->lc,TRUE);
CU_ASSERT_TRUE(call(marie,pauline));
@ -523,7 +523,7 @@ static bool_t check_ice(LinphoneCoreManager* caller, LinphoneCoreManager* callee
c1=linphone_core_get_current_call(caller->lc);
c2=linphone_core_get_current_call(callee->lc);
CU_ASSERT_PTR_NOT_NULL(c1);
CU_ASSERT_PTR_NOT_NULL(c2);
@ -556,7 +556,7 @@ static bool_t check_ice(LinphoneCoreManager* caller, LinphoneCoreManager* callee
static void _call_with_ice(bool_t caller_with_ice, bool_t callee_with_ice, bool_t random_ports) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
if (callee_with_ice){
linphone_core_set_firewall_policy(marie->lc,LinphonePolicyUseIce);
linphone_core_set_stun_server(marie->lc,"stun.linphone.org");
@ -565,7 +565,7 @@ static void _call_with_ice(bool_t caller_with_ice, bool_t callee_with_ice, bool_
linphone_core_set_firewall_policy(pauline->lc,LinphonePolicyUseIce);
linphone_core_set_stun_server(pauline->lc,"stun.linphone.org");
}
if (random_ports){
linphone_core_set_audio_port(marie->lc,-1);
linphone_core_set_video_port(marie->lc,-1);
@ -580,10 +580,10 @@ static void _call_with_ice(bool_t caller_with_ice, bool_t callee_with_ice, bool_
/*wait for the ICE reINVITE to complete*/
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,2));
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,2));
CU_ASSERT_TRUE(check_ice(pauline,marie,LinphoneIceStateHostConnection));
}
liblinphone_tester_check_rtcp(marie,pauline);
/*then close the call*/
linphone_core_terminate_all_calls(pauline->lc);
@ -629,17 +629,17 @@ static void call_with_custom_headers(void) {
ms_free(tmp);
linphone_address_destroy(marie->identity);
marie->identity=marie_identity;
params=linphone_core_create_default_call_parameters(marie->lc);
linphone_call_params_add_custom_header(params,"Weather","bad");
linphone_call_params_add_custom_header(params,"Working","yes");
CU_ASSERT_TRUE(call_with_caller_params(pauline,marie,params));
linphone_call_params_destroy(params);
call_marie=linphone_core_get_current_call(marie->lc);
call_pauline=linphone_core_get_current_call(pauline->lc);
CU_ASSERT_PTR_NOT_NULL(call_marie);
CU_ASSERT_PTR_NOT_NULL(call_pauline);
@ -824,12 +824,12 @@ static void call_with_video_added(void) {
static void call_with_video_added_random_ports(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
linphone_core_set_audio_port(marie->lc,-1);
linphone_core_set_video_port(marie->lc,-1);
linphone_core_set_audio_port(pauline->lc,-1);
linphone_core_set_video_port(pauline->lc,-1);
CU_ASSERT_TRUE(call(pauline,marie));
CU_ASSERT_TRUE(add_video(pauline,marie));
@ -922,17 +922,17 @@ static void _call_with_media_relay(bool_t random_ports) {
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
linphone_core_set_user_agent(marie->lc,"Natted Linphone",NULL);
linphone_core_set_user_agent(pauline->lc,"Natted Linphone",NULL);
if (random_ports){
linphone_core_set_audio_port(marie->lc,-1);
linphone_core_set_video_port(marie->lc,-1);
linphone_core_set_audio_port(pauline->lc,-1);
linphone_core_set_video_port(pauline->lc,-1);
}
CU_ASSERT_TRUE(call(pauline,marie));
liblinphone_tester_check_rtcp(pauline,marie);
#ifdef VIDEO_ENABLED
CU_ASSERT_TRUE(add_video(pauline,marie));
liblinphone_tester_check_rtcp(pauline,marie);
@ -1018,7 +1018,7 @@ static void call_with_privacy2(void) {
LinphoneProxyConfig* pauline_proxy;
params=linphone_core_create_default_call_parameters(pauline->lc);
linphone_call_params_set_privacy(params,LinphonePrivacyId);
linphone_core_get_default_proxy(pauline->lc,&pauline_proxy);
linphone_proxy_config_edit(pauline_proxy);
linphone_proxy_config_enable_register(pauline_proxy,FALSE);
@ -1212,8 +1212,6 @@ static void simple_conference(void) {
ms_list_free(lcs);
}
static void srtp_call() {
call_base(LinphoneMediaEncryptionSRTP,FALSE,FALSE,LinphonePolicyNoFirewall);
}
@ -1349,19 +1347,19 @@ static void early_media_call(void) {
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallIncomingEarlyMedia,1);
CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallOutgoingEarlyMedia,1);
wait_for_until(pauline->lc,marie->lc,NULL,0,1000);
/*added because a bug related to early-media caused the Connected state to be reached two times*/
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallConnected,1);
/*just to sleep*/
linphone_core_terminate_all_calls(marie->lc);
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallEnd,1));
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallEnd,1));
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
@ -1378,7 +1376,7 @@ static void early_media_call_with_ringing(void){
/*
Marie calls Pauline, and after the call has rung, transitions to an early_media session
*/
/*use playfile for callee to avoid locking on capture card*/
linphone_core_use_files (pauline->lc,TRUE);
snprintf(hellopath,sizeof(hellopath), "%s/sounds/hello8000.wav", liblinphone_tester_file_prefix);
@ -1414,7 +1412,7 @@ static void early_media_call_with_ringing(void){
ms_list_free(lcs);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
@ -1537,9 +1535,9 @@ static void simple_call_transfer(void) {
CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallPaused,1,2000));
/*marie calling laure*/
CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallOutgoingProgress,1,2000));
CU_ASSERT_PTR_NOT_NULL(linphone_call_get_transfer_target_call(marie_calling_pauline));
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneTransferCallOutgoingInit,1,2000));
CU_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallIncomingReceived,1,2000));
CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallOutgoingRinging,1,2000));
@ -1549,11 +1547,11 @@ static void simple_call_transfer(void) {
CU_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallStreamsRunning,1,2000));
CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallConnected,1,2000));
CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallStreamsRunning,1,2000));
marie_calling_laure=linphone_core_get_current_call(marie->lc);
CU_ASSERT_PTR_NOT_NULL_FATAL(marie_calling_laure);
CU_ASSERT_TRUE(linphone_call_get_transferer_call(marie_calling_laure)==marie_calling_pauline);
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneTransferCallConnected,1,2000));
/*terminate marie to pauline call*/
@ -1587,11 +1585,11 @@ static void unattended_call_transfer(void) {
linphone_core_transfer_call(marie->lc,pauline_called_by_marie,laure_identity);
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallRefered,1,2000));
/*marie ends the call */
linphone_core_terminate_call(marie->lc,pauline_called_by_marie);
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,2000));
/*Pauline starts the transfer*/
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallOutgoingInit,1,2000));
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallOutgoingProgress,1,2000));
@ -1628,21 +1626,21 @@ static void unattended_call_transfer_with_error(void) {
linphone_core_transfer_call(marie->lc,pauline_called_by_marie,"unknown_user");
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallRefered,1,2000));
/*Pauline starts the transfer*/
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallOutgoingInit,1,2000));
/* and immediately get an error*/
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallError,1,2000));
/*the error must be reported back to marie*/
CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneTransferCallError,1,2000));
/*and pauline should resume the call automatically*/
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallResuming,1,2000));
/*and call should be resumed*/
CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallStreamsRunning,1,2000));
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
ms_list_free(lcs);
@ -1796,7 +1794,7 @@ static void call_established_with_rejected_incoming_reinvite(void) {
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
CU_ASSERT_TRUE(call(pauline,marie));
/*wait for ACK to be transmitted before going to reINVITE*/
wait_for_until(marie->lc,pauline->lc,NULL,0,1000);
@ -1841,7 +1839,7 @@ static void call_redirect(void){
/*
Marie calls Pauline, which will redirect the call to Laure via a 302
*/
/*use playfile for callee to avoid locking on capture card*/
linphone_core_use_files (pauline->lc,TRUE);
linphone_core_use_files (laure->lc,TRUE);
@ -1879,7 +1877,7 @@ static void call_redirect(void){
CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,1,1000));
ms_list_free(lcs);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
linphone_core_manager_destroy(laure);
@ -1968,6 +1966,98 @@ static void call_rejected_without_403_because_wrong_credentials_no_auth_req_cb()
call_rejected_because_wrong_credentials_with_params("tester-no-403",FALSE);
}
void create_call_for_statistics_tests(
LinphoneCoreManager* marie,
LinphoneCoreManager* pauline,
LinphoneCall** call_marie,
LinphoneCall** call_pauline) {
CU_ASSERT_TRUE(call(pauline,marie));
*call_marie = linphone_core_get_current_call(marie->lc);
*call_pauline = linphone_core_get_current_call(pauline->lc);
CU_ASSERT_PTR_NOT_NULL(*call_marie);
CU_ASSERT_PTR_NOT_NULL(*call_pauline);
}
static void statistics_not_used_without_config() {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
LinphoneCall* call_marie = NULL;
LinphoneCall* call_pauline = NULL;
create_call_for_statistics_tests(marie, pauline, &call_marie, &call_pauline);
// marie has stats collection enabled since pauline has not
CU_ASSERT_TRUE(linphone_proxy_config_send_statistics_enabled(call_marie->dest_proxy));
CU_ASSERT_FALSE(linphone_proxy_config_send_statistics_enabled(call_pauline->dest_proxy));
CU_ASSERT_EQUAL(strcmp("sip:collector@sip.example.org",
linphone_proxy_config_get_statistics_collector(call_marie->dest_proxy)), 0);
// this field should be already filled
CU_ASSERT_PTR_NOT_NULL(call_marie->log->reports[0]->info.local_addr.ip);
CU_ASSERT_PTR_NULL(call_pauline->log->reports[0]->info.local_addr.ip);
// but not this one since it is updated at the end of call
CU_ASSERT_PTR_NULL(call_marie->log->reports[0]->dialog_id);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
static void statistics_not_sent_if_call_not_started() {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
LinphoneCallLog* out_call_log;
LinphoneCall* out_call;
linphone_core_set_max_calls(pauline->lc,0);
out_call = linphone_core_invite(marie->lc,"pauline");
linphone_call_ref(out_call);
CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallError,1));
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallError,1);
if (ms_list_size(linphone_core_get_call_logs(marie->lc))>0) {
CU_ASSERT_PTR_NOT_NULL(out_call_log=(LinphoneCallLog*)(linphone_core_get_call_logs(marie->lc)->data));
CU_ASSERT_EQUAL(linphone_call_log_get_status(out_call_log),LinphoneCallAborted);
}
linphone_call_unref(out_call);
// wait a few time...
wait_for(marie->lc,NULL,NULL,0);
// since the callee was busy, there should be no publish to do
CU_ASSERT_EQUAL(marie->stat.number_of_LinphonePublishProgress,0);
CU_ASSERT_EQUAL(marie->stat.number_of_LinphonePublishOk,0);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
static void statistics_sent_at_call_termination() {
// int return_code = -1;
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
LinphoneCall* call_marie = NULL;
LinphoneCall* call_pauline = NULL;
create_call_for_statistics_tests(marie, pauline, &call_marie, &call_pauline);
linphone_core_terminate_all_calls(marie->lc);
CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallReleased,1));
CU_ASSERT_TRUE(wait_for(pauline->lc,NULL,&pauline->stat.number_of_LinphoneCallReleased,1));
CU_ASSERT_PTR_NULL(linphone_core_get_current_call(marie->lc));
CU_ASSERT_PTR_NULL(linphone_core_get_current_call(pauline->lc));
// now dialog id should be filled
CU_ASSERT_PTR_NOT_NULL(call_marie->log->reports[0]->dialog_id);
// PUBLISH submission to the collector should be ok
CU_ASSERT_TRUE(wait_for(marie->lc,NULL,&marie->stat.number_of_LinphonePublishProgress,1));
CU_ASSERT_TRUE(wait_for(marie->lc,NULL,&marie->stat.number_of_LinphonePublishOk,1));
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
#ifdef VIDEO_ENABLED
#endif
@ -2027,7 +2117,10 @@ test_t call_tests[] = {
{ "Call established with rejected RE-INVITE",call_established_with_rejected_reinvite},
{ "Call established with rejected incoming RE-INVITE", call_established_with_rejected_incoming_reinvite },
{ "Call established with rejected RE-INVITE in error", call_established_with_rejected_reinvite_with_error},
{ "Call redirected by callee", call_redirect}
{ "Call redirected by callee", call_redirect},
{ "Call statistics not used if no config", statistics_not_used_without_config},
{ "Call statistics not sent if call did not start", statistics_not_sent_if_call_not_started},
{ "Call statistics sent if call ended normally", statistics_sent_at_call_termination},
};
test_suite_t call_test_suite = {
@ -2037,4 +2130,3 @@ test_suite_t call_test_suite = {
sizeof(call_tests) / sizeof(call_tests[0]),
call_tests
};

View file

@ -22,6 +22,8 @@ reg_expires=3600
reg_sendregister=1
publish=0
dial_escape_plus=0
reg_statistics_collector=sip:collector@sip.example.org
send_statistics=1
[friend_0]
url="Paupoche" <sip:pauline@sip.example.org>