Merge branch 'master' of git.linphone.org:linphone into dev_filetransfer

This commit is contained in:
Johan Pascal 2014-06-09 13:40:06 +02:00
commit 3c918dfd8b
53 changed files with 3074 additions and 1360 deletions

View file

@ -924,7 +924,7 @@ printf "* %-30s %s\n" "Account assistant" $build_wizard
printf "* %-30s %s\n" "Console interface" $console_ui
printf "* %-30s %s\n" "Tools" $build_tools
printf "* %-30s %s\n" "Message storage" $enable_msg_storage
printf "* %-30s %s\n" "zRTP encryption (GPLv3)" $zrtp
printf "* %-30s %s\n" "zRTP encryption" $zrtp
printf "* %-30s %s\n" "uPnP support" $build_upnp
printf "* %-30s %s\n" "LDAP support" $enable_ldap

View file

@ -30,82 +30,6 @@
using namespace belledonnecomm;
using namespace ::std;
#ifndef USE_BELLESIP
Mutex TunnelManager::sMutex;
int TunnelManager::eXosipSendto(int fd,const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen,void* userdata){
TunnelManager* lTunnelMgr=(TunnelManager*)userdata;
sMutex.lock();
if (lTunnelMgr->mSipSocket==NULL){
sMutex.unlock();
return len;
}
lTunnelMgr->mSipSocket->sendto(buf,len,to,tolen);
sMutex.unlock();
//ignore the error in all cases, retransmissions might be successful.
return len;
}
int TunnelManager::eXosipRecvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen,void* userdata){
TunnelManager* lTunnelMgr=(TunnelManager*)userdata;
int err;
sMutex.lock();
if (lTunnelMgr->mSipSocket==NULL){
sMutex.unlock();
return 0;//let ignore the error
}
err=lTunnelMgr->mSipSocket->recvfrom(buf,len,from,*fromlen);
sMutex.unlock();
return err;
}
int TunnelManager::eXosipSelect(int max_fds, fd_set *s1, fd_set *s2, fd_set *s3, struct timeval *tv,void* userdata){
struct timeval begin,cur;
TunnelManager* lTunnelMgr=(TunnelManager*)userdata;
if (s1 && tv!=0 && tv->tv_sec){
/*this is the select from udp.c, the one that is interesting to us*/
NativeSocket udp_fd=(NativeSocket)eXosip_get_udp_socket();
NativeSocket controlfd=(NativeSocket)eXosip_get_control_fd();
FD_ZERO(s1);
gettimeofday(&begin,NULL);
do{
struct timeval abit;
abit.tv_sec=0;
abit.tv_usec=20000;
sMutex.lock();
if (lTunnelMgr->mSipSocket!=NULL){
if (lTunnelMgr->mSipSocket->hasData()) {
sMutex.unlock();
/* we make exosip believe that it has udp data to read*/
FD_SET(udp_fd,s1);
return 1;
}
}
sMutex.unlock();
gettimeofday(&cur,NULL);
if (cur.tv_sec-begin.tv_sec>tv->tv_sec) {
FD_SET(controlfd,s1);
FD_SET(udp_fd,s1);
return 0;
}
FD_ZERO(s1);
FD_SET(controlfd,s1);
if (select(max_fds,s1,s2,s3,&abit)==1) {
return 1;
}
}while(1);
}else{
/*select called from other places, only the control fd is present */
return select(max_fds,s1,s2,s3,tv);
}
}
#endif
void TunnelManager::addServer(const char *ip, int port,unsigned int udpMirrorPort,unsigned int delay) {
if (ip == NULL) {
@ -186,10 +110,6 @@ void TunnelManager::start() {
mTunnelClient->setHttpProxy(mHttpProxyHost.c_str(), mHttpProxyPort, mHttpUserName.c_str(), mHttpPasswd.c_str());
}
mTunnelClient->start();
#ifndef USE_BELLESIP
if (mSipSocket == NULL) mSipSocket =mTunnelClient->createSocket(5060);
#endif
}
bool TunnelManager::isStarted() {
@ -217,9 +137,6 @@ int TunnelManager::customRecvfrom(struct _RtpTransport *t, mblk_t *msg, int flag
TunnelManager::TunnelManager(LinphoneCore* lc) :TunnelClientController()
,mCore(lc)
#ifndef USE_BELLESIP
,mSipSocket(NULL)
#endif
,mCallback(NULL)
,mEnabled(false)
,mTunnelClient(NULL)
@ -227,12 +144,6 @@ TunnelManager::TunnelManager(LinphoneCore* lc) :TunnelClientController()
,mReady(false)
,mHttpProxyPort(0){
#ifndef USE_BELLESIP
mExosipTransport.data=this;
mExosipTransport.recvfrom=eXosipRecvfrom;
mExosipTransport.sendto=eXosipSendto;
mExosipTransport.select=eXosipSelect;
#endif
linphone_core_add_iterate_hook(mCore,(LinphoneCoreIterateHook)sOnIterate,this);
mTransportFactories.audio_rtcp_func=sCreateRtpTransport;
mTransportFactories.audio_rtcp_func_data=this;
@ -249,17 +160,7 @@ TunnelManager::~TunnelManager(){
}
void TunnelManager::stopClient(){
#ifdef USE_BELLESIP
sal_disable_tunnel(mCore->sal);
#else
eXosip_transport_hook_register(NULL);
if (mSipSocket != NULL){
sMutex.lock();
mTunnelClient->closeSocket(mSipSocket);
mSipSocket = NULL;
sMutex.unlock();
}
#endif
if (mTunnelClient){
delete mTunnelClient;
mTunnelClient=NULL;
@ -279,16 +180,11 @@ void TunnelManager::processTunnelEvent(const Event &ev){
//register
if (lProxy) {
linphone_proxy_config_done(lProxy);
linphone_proxy_config_refresh_register(lProxy);
}
mReady=true;
}else if (mEnabled && !mTunnelClient->isReady()){
/* we got disconnected from the tunnel */
if (lProxy && linphone_proxy_config_is_registered(lProxy)) {
/*forces de-registration so that we register again when the tunnel is up*/
linphone_proxy_config_edit(lProxy);
linphone_core_iterate(mCore);
}
mReady=false;
}
}
@ -299,6 +195,8 @@ void TunnelManager::waitUnRegistration(){
if (lProxy && linphone_proxy_config_get_state(lProxy)==LinphoneRegistrationOk) {
int i=0;
linphone_proxy_config_edit(lProxy);
linphone_proxy_config_enable_register(lProxy,FALSE);
linphone_proxy_config_done(lProxy);
//make sure unregister is sent and authenticated
do{
linphone_core_iterate(mCore);
@ -348,6 +246,8 @@ void TunnelManager::enable(bool isEnable) {
LinphoneProxyConfig* lProxy;
linphone_core_get_default_proxy(mCore, &lProxy);
if (lProxy) {
linphone_proxy_config_edit(lProxy);
linphone_proxy_config_enable_register(lProxy,TRUE);
linphone_proxy_config_done(lProxy);
}

View file

@ -342,7 +342,6 @@ static void process_response_event(void *user_ctx, const belle_sip_response_even
break;
case 401:
case 407:
/*belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),NULL);*//*remove op from trans*/
if (op->state == SalOpStateTerminating && strcmp("BYE",belle_sip_request_get_method(request))!=0) {
/*only bye are completed*/
belle_sip_message("Op is in state terminating, nothing else to do ");
@ -396,7 +395,7 @@ static void process_transaction_terminated(void *user_ctx, const belle_sip_trans
if(client_transaction)
trans=BELLE_SIP_TRANSACTION(client_transaction);
else
trans=BELLE_SIP_TRANSACTION(server_transaction);
trans=BELLE_SIP_TRANSACTION(server_transaction);
op = (SalOp*)belle_sip_transaction_get_application_data(trans);
if (op && op->callbacks && op->callbacks->process_transaction_terminated) {
@ -404,7 +403,7 @@ static void process_transaction_terminated(void *user_ctx, const belle_sip_trans
} else {
ms_message("Unhandled transaction terminated [%p]",trans);
}
if (op && client_transaction) sal_op_unref(op); /*because every client transaction ref op*/
if (op) sal_op_unref(op); /*because every transaction ref op*/
}
@ -423,6 +422,8 @@ static void process_auth_requested(void *sal, belle_sip_auth_event_t *event) {
Sal * sal_init(){
belle_sip_listener_callbacks_t listener_callbacks;
Sal * sal=ms_new0(Sal,1);
/*belle_sip_object_enable_marshal_check(TRUE);*/
sal->auto_contacts=TRUE;
/*first create the stack, which initializes the belle-sip object's pool for this thread*/
@ -451,6 +452,7 @@ Sal * sal_init(){
sal->tls_verify=TRUE;
sal->tls_verify_cn=TRUE;
sal->refresher_retry_after=60000; /*default value in ms*/
sal->enable_sip_update=TRUE;
return sal;
}
@ -999,3 +1001,7 @@ void sal_cancel_timer(Sal *sal, belle_sip_source_t *timer) {
belle_sip_main_loop_t *ml = belle_sip_stack_get_main_loop(sal->stack);
belle_sip_main_loop_remove_source(ml, timer);
}
void sal_enable_sip_update_method(Sal *ctx,bool_t value) {
ctx->enable_sip_update=value;
}

View file

@ -48,6 +48,7 @@ struct Sal{
bool_t auto_contacts;
bool_t enable_test_features;
bool_t no_initial_route;
bool_t enable_sip_update; /*true by default*/
};
typedef enum SalOpState {

View file

@ -70,7 +70,7 @@ static void sdp_process(SalOp *h){
strcpy(h->result->streams[i].rtcp_addr,h->base.remote_media->streams[i].rtcp_addr);
h->result->streams[i].rtcp_port=h->base.remote_media->streams[i].rtcp_port;
if (h->result->streams[i].proto == SalProtoRtpSavp) {
if ((h->result->streams[i].proto == SalProtoRtpSavpf) || (h->result->streams[i].proto == SalProtoRtpSavp)) {
h->result->streams[i].crypto[0] = h->base.remote_media->streams[i].crypto[0];
}
}
@ -152,6 +152,10 @@ static void process_dialog_terminated(void *ctx, const belle_sip_dialog_terminat
static void handle_sdp_from_response(SalOp* op,belle_sip_response_t* response) {
belle_sdp_session_description_t* sdp;
SalReason reason;
if (op->base.remote_media){
sal_media_description_unref(op->base.remote_media);
op->base.remote_media=NULL;
}
if (extract_sdp(BELLE_SIP_MESSAGE(response),&sdp,&reason)==0) {
if (sdp){
op->base.remote_media=sal_media_description_new();
@ -174,6 +178,7 @@ static int vfu_retry (void *user_data, unsigned int events) {
sal_op_unref(op);
return BELLE_SIP_STOP;
}
static void call_process_response(void *op_base, const belle_sip_response_event_t *event){
SalOp* op = (SalOp*)op_base;
belle_sip_request_t* ack;
@ -183,17 +188,17 @@ static void call_process_response(void *op_base, const belle_sip_response_event_
belle_sip_response_t* response=belle_sip_response_event_get_response(event);
int code = belle_sip_response_get_status_code(response);
belle_sip_header_content_type_t *header_content_type=NULL;
belle_sip_dialog_t *dialog=belle_sip_response_event_get_dialog(event);
if (!client_transaction) {
ms_warning("Discarding stateless response [%i] on op [%p]",code,op);
return;
}
req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction));
set_or_update_dialog(op,belle_sip_response_event_get_dialog(event));
dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL;
set_or_update_dialog(op,dialog);
dialog_state=dialog ? belle_sip_dialog_get_state(dialog) : BELLE_SIP_DIALOG_NULL;
ms_message("Op [%p] receiving call response [%i], dialog is [%p] in state [%s]",op,code,op->dialog,belle_sip_dialog_state_to_string(dialog_state));
ms_message("Op [%p] receiving call response [%i], dialog is [%p] in state [%s]",op,code,dialog,belle_sip_dialog_state_to_string(dialog_state));
switch(dialog_state) {
case BELLE_SIP_DIALOG_NULL:
@ -215,14 +220,18 @@ static void call_process_response(void *op_base, const belle_sip_response_event_
if (op->dialog==NULL) call_set_released(op);
}
}
} else if (code >= 180 && code<300) {
handle_sdp_from_response(op,response);
op->base.root->callbacks.call_ringing(op);
} else if (code >= 180 && code<200) {
belle_sip_response_t *prev_response=belle_sip_object_data_get(BELLE_SIP_OBJECT(dialog),"early_response");
if (!prev_response || code>belle_sip_response_get_status_code(prev_response)){
handle_sdp_from_response(op,response);
op->base.root->callbacks.call_ringing(op);
}
belle_sip_object_data_set(BELLE_SIP_OBJECT(dialog),"early_response",belle_sip_object_ref(response),belle_sip_object_unref);
} else if (code>=300){
call_set_error(op,response);
if (op->dialog==NULL) call_set_released(op);
}
} else if ( code >=200
} else if (code >=200
&& code<300
&& strcmp("UPDATE",belle_sip_request_get_method(req))==0) {
handle_sdp_from_response(op,response);
@ -418,8 +427,7 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
if (strcmp("ACK",method)!=0){ /*ACK does'nt create srv transaction*/
server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,belle_sip_request_event_get_request(event));
belle_sip_object_ref(server_transaction);
belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(server_transaction),op);
sal_op_ref(op);
belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(server_transaction),sal_op_ref(op));
}
if (strcmp("INVITE",method)==0) {
@ -561,6 +569,9 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
} else if (strcmp("MESSAGE",method)==0){
sal_process_incoming_message(op,event);
} else if (strcmp("UPDATE",method)==0) {
/*FIXME jehan: It might be better to silently accept UPDATE which do not modify either the number or the nature of streams*/
/*rfc 3311
* 5.2 Receiving an UPDATE
* ...
@ -568,8 +579,9 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
* the request with a 504 response.
*/
resp=sal_op_create_response_from_request(op,req,504);
belle_sip_message_add_header( BELLE_SIP_MESSAGE(resp)
,belle_sip_header_create( "Warning", "Cannot change the session parameters without prompting the user"));
belle_sip_response_set_reason_phrase(resp,"Cannot change the session parameters without prompting the user");
/*belle_sip_message_add_header( BELLE_SIP_MESSAGE(resp)
,belle_sip_header_create( "Warning", "Cannot change the session parameters without prompting the user"));*/
belle_sip_server_transaction_send_response(server_transaction,resp);
return;
}else{
@ -607,14 +619,16 @@ int sal_call_set_local_media_description(SalOp *op, SalMediaDescription *desc){
return 0;
}
static belle_sip_header_allow_t *create_allow(){
static belle_sip_header_allow_t *create_allow(bool_t enable_update){
belle_sip_header_allow_t* header_allow;
header_allow = belle_sip_header_allow_create("INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE");
char allow [256];
snprintf(allow,sizeof(allow),"INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO%s",(enable_update?", UPDATE":""));
header_allow = belle_sip_header_allow_create(allow);
return header_allow;
}
static void sal_op_fill_invite(SalOp *op, belle_sip_request_t* invite) {
belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),BELLE_SIP_HEADER(create_allow()));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),BELLE_SIP_HEADER(create_allow(op->base.root->enable_sip_update)));
if (op->base.root->session_expires!=0){
belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),belle_sip_header_create( "Session-expires", "200"));
@ -742,7 +756,7 @@ int sal_call_accept(SalOp*h){
ms_error("Fail to build answer for call");
return -1;
}
belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(create_allow()));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(create_allow(h->base.root->enable_sip_update)));
if (h->base.root->session_expires!=0){
if (h->supports_session_timers) {
belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),belle_sip_header_create("Supported", "timer"));

View file

@ -560,20 +560,28 @@ const SalErrorInfo *sal_op_get_error_info(const SalOp *op){
return &op->error_info;
}
static void unlink_op_with_dialog(SalOp *op, belle_sip_dialog_t* dialog){
belle_sip_dialog_set_application_data(dialog,NULL);
sal_op_unref(op);
belle_sip_object_unref(dialog);
}
static belle_sip_dialog_t *link_op_with_dialog(SalOp *op, belle_sip_dialog_t* dialog){
belle_sip_dialog_set_application_data(dialog,sal_op_ref(op));
belle_sip_object_ref(dialog);
return dialog;
}
void set_or_update_dialog(SalOp* op, belle_sip_dialog_t* dialog) {
/*check if dialog has changed*/
if (dialog && dialog != op->dialog) {
ms_message("Dialog set from [%p] to [%p] for op [%p]",op->dialog,dialog,op);
/*fixme, shouldn't we cancel previous dialog*/
if (op->dialog) {
belle_sip_dialog_set_application_data(op->dialog,NULL);
belle_sip_object_unref(op->dialog);
sal_op_unref(op);
if (dialog==NULL) return;
ms_message("op [%p] : set_or_update_dialog() current=[%p] new=[%p]",op,op->dialog,dialog);
if (dialog && op->dialog!=dialog){
if (op->dialog){
/*FIXME: shouldn't we delete unconfirmed dialogs ?*/
unlink_op_with_dialog(op,op->dialog);
op->dialog=NULL;
}
op->dialog=dialog;
belle_sip_dialog_set_application_data(op->dialog,op);
sal_op_ref(op);
belle_sip_object_ref(op->dialog);
op->dialog=link_op_with_dialog(op,dialog);
}
}
/*return reffed op*/

View file

@ -68,6 +68,71 @@ static void add_ice_remote_candidates(belle_sdp_media_description_t *md, const S
if (buffer[0] != '\0') belle_sdp_media_description_add_attribute(md,belle_sdp_attribute_create("remote-candidates",buffer));
}
static bool_t is_rtcp_fb_trr_int_the_same_for_all_payloads(const SalStreamDescription *stream, uint8_t *trr_int) {
MSList *pt_it;
bool_t first = TRUE;
for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
PayloadType *pt = (PayloadType *)pt_it->data;
if (payload_type_get_flags(pt) & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED) {
if (first == TRUE) {
*trr_int = payload_type_get_avpf_params(pt).trr_interval;
first = FALSE;
} else if (payload_type_get_avpf_params(pt).trr_interval != *trr_int) {
return FALSE;
}
}
}
return TRUE;
}
static void add_rtcp_fb_trr_int_attribute(belle_sdp_media_description_t *media_desc, int8_t id, uint8_t trr_int) {
belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new();
belle_sdp_rtcp_fb_attribute_set_id(attribute, id);
belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_TRR_INT);
belle_sdp_rtcp_fb_attribute_set_trr_int(attribute, trr_int);
belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute));
}
static void add_rtcp_fb_nack_attribute(belle_sdp_media_description_t *media_desc, int8_t id, belle_sdp_rtcp_fb_val_param_t param) {
belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new();
belle_sdp_rtcp_fb_attribute_set_id(attribute, id);
belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_NACK);
belle_sdp_rtcp_fb_attribute_set_param(attribute, param);
belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute));
}
static void add_rtcp_fb_attributes(belle_sdp_media_description_t *media_desc, const SalMediaDescription *md, const SalStreamDescription *stream) {
MSList *pt_it;
PayloadType *pt;
PayloadTypeAvpfParams avpf_params;
bool_t general_trr_int;
uint8_t trr_int = 0;
general_trr_int = is_rtcp_fb_trr_int_the_same_for_all_payloads(stream, &trr_int);
if (general_trr_int == TRUE) {
add_rtcp_fb_trr_int_attribute(media_desc, -1, trr_int);
}
for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
pt = (PayloadType *)pt_it->data;
/* AVPF/SAVPF profile is used so enable AVPF for all paylad types. */
payload_type_set_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
avpf_params = payload_type_get_avpf_params(pt);
/* Add rtcp-fb attributes according to the AVPF features of the payload types. */
if (avpf_params.features & PAYLOAD_TYPE_AVPF_PLI) {
add_rtcp_fb_nack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_PLI);
}
if (avpf_params.features & PAYLOAD_TYPE_AVPF_SLI) {
add_rtcp_fb_nack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_SLI);
}
if (avpf_params.features & PAYLOAD_TYPE_AVPF_RPSI) {
add_rtcp_fb_nack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_RPSI);
}
}
}
static belle_sdp_attribute_t * create_rtcp_xr_attribute(const OrtpRtcpXrConfiguration *config) {
belle_sdp_rtcp_xr_attribute_t *attribute = belle_sdp_rtcp_xr_attribute_new();
if (config->rcvr_rtt_mode != OrtpRtcpXrRcvrRttNone) {
@ -100,12 +165,12 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session
int rtp_port;
int rtcp_port;
bool_t different_rtp_and_rtcp_addr;
rtp_addr=stream->rtp_addr;
rtcp_addr=stream->rtcp_addr;
rtp_port=stream->rtp_port;
rtcp_port=stream->rtcp_port;
media_desc = belle_sdp_media_description_create ( sal_stream_description_get_type_as_string(stream)
,stream->rtp_port
,1
@ -139,43 +204,22 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session
}else inet6=FALSE;
belle_sdp_media_description_set_connection(media_desc,belle_sdp_connection_create("IN", inet6 ? "IP6" : "IP4", rtp_addr));
}
if ( stream->bandwidth>0 )
belle_sdp_media_description_set_bandwidth ( media_desc,"AS",stream->bandwidth );
if ( stream->proto == SalProtoRtpSavp ) {
if ((stream->proto == SalProtoRtpSavpf) || (stream->proto == SalProtoRtpSavp)) {
/* add crypto lines */
for ( j=0; j<SAL_CRYPTO_ALGO_MAX; j++ ) {
const char *enc_name=NULL;
switch ( stream->crypto[j].algo ) {
case MS_AES_128_SHA1_80:
enc_name="AES_CM_128_HMAC_SHA1_80";
break;
case MS_AES_128_SHA1_32:
enc_name="AES_CM_128_HMAC_SHA1_32";
break;
case MS_AES_256_SHA1_32:
enc_name="AES_CM_256_HMAC_SHA1_32";
break;
case MS_AES_256_SHA1_80:
enc_name="AES_CM_256_HMAC_SHA1_32";
break;
case MS_AES_128_NO_AUTH:
ms_warning ( "Unsupported crypto suite: AES_128_NO_AUTH" );
break;
case MS_NO_CIPHER_SHA1_80:
ms_warning ( "Unsupported crypto suite: NO_CIPHER_SHA1_80" );
break;
default:
j = SAL_CRYPTO_ALGO_MAX;
/* no break */
}
if (enc_name){
snprintf ( buffer, sizeof ( buffer )-1, "%d %s inline:%s",
stream->crypto[j].tag, enc_name, stream->crypto[j].master_key );
belle_sdp_media_description_add_attribute ( media_desc,belle_sdp_attribute_create ( "crypto",buffer ) );
}
MSCryptoSuiteNameParams desc;
if (ms_crypto_suite_to_name_params(stream->crypto[j].algo,&desc)==0){
if (desc.params)
snprintf ( buffer, sizeof ( buffer )-1, "%d %s inline:%s %s", stream->crypto[j].tag, desc.name, stream->crypto[j].master_key,desc.params);
else
snprintf ( buffer, sizeof ( buffer )-1, "%d %s inline:%s", stream->crypto[j].tag, desc.name, stream->crypto[j].master_key );
belle_sdp_media_description_add_attribute( media_desc,belle_sdp_attribute_create ("crypto", buffer));
}else break;
}
}
switch ( stream->dir ) {
@ -222,7 +266,11 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session
}
}
if (stream->rtcp_xr.enabled == TRUE) {
if ((rtp_port != 0) && ((stream->proto == SalProtoRtpAvpf) || (stream->proto == SalProtoRtpSavpf))) {
add_rtcp_fb_attributes(media_desc, md, stream);
}
if ((rtp_port != 0) && (stream->rtcp_xr.enabled == TRUE)) {
char sastr[1024] = {0};
char mastr[1024] = {0};
size_t saoff = 0;
@ -305,9 +353,11 @@ belle_sdp_session_description_t * media_description_to_sdp ( const SalMediaDescr
static void sdp_parse_payload_types(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
PayloadType *pt;
PayloadTypeAvpfParams avpf_params;
belle_sip_list_t* mime_param_it=NULL;
belle_sdp_mime_parameter_t* mime_param;
belle_sip_list_t* mime_params=belle_sdp_media_description_build_mime_parameters ( media_desc );
memset(&avpf_params, 0, sizeof(avpf_params));
for ( mime_param_it=mime_params
; mime_param_it!=NULL
; mime_param_it=mime_param_it->next ) {
@ -319,6 +369,7 @@ static void sdp_parse_payload_types(belle_sdp_media_description_t *media_desc, S
pt->mime_type=ms_strdup ( belle_sdp_mime_parameter_get_type ( mime_param ) );
pt->channels=belle_sdp_mime_parameter_get_channel_count ( mime_param );
payload_type_set_send_fmtp ( pt,belle_sdp_mime_parameter_get_parameters ( mime_param ) );
payload_type_set_avpf_params(pt, avpf_params);
stream->payloads=ms_list_append ( stream->payloads,pt );
stream->ptime=belle_sdp_mime_parameter_get_ptime ( mime_param );
ms_message ( "Found payload %s/%i fmtp=%s",pt->mime_type,pt->clock_rate,
@ -330,7 +381,7 @@ static void sdp_parse_payload_types(belle_sdp_media_description_t *media_desc, S
static void sdp_parse_media_crypto_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
belle_sip_list_t *attribute_it;
belle_sdp_attribute_t *attribute;
char tmp[256], tmp2[256];
char tmp[256], tmp2[256], parameters[256]={0};
int valid_count = 0;
int nb;
@ -341,42 +392,39 @@ static void sdp_parse_media_crypto_parameters(belle_sdp_media_description_t *med
attribute=BELLE_SDP_ATTRIBUTE ( attribute_it->data );
if ( keywordcmp ( "crypto",belle_sdp_attribute_get_name ( attribute ) ) ==0 && belle_sdp_attribute_get_value ( attribute ) !=NULL ) {
nb = sscanf ( belle_sdp_attribute_get_value ( attribute ), "%d %256s inline:%256s",
nb = sscanf ( belle_sdp_attribute_get_value ( attribute ), "%d %256s inline:%256s %256s",
&stream->crypto[valid_count].tag,
tmp,
tmp2 );
ms_message ( "Found valid crypto line (tag:%d algo:'%s' key:'%s'",
stream->crypto[valid_count].tag,
tmp,
tmp2 );
if ( nb == 3 ) {
if ( keywordcmp ( "AES_CM_128_HMAC_SHA1_80",tmp ) == 0 ){
stream->crypto[valid_count].algo = MS_AES_128_SHA1_80;
}else if ( keywordcmp ( "AES_CM_128_HMAC_SHA1_32",tmp ) == 0 ){
stream->crypto[valid_count].algo = MS_AES_128_SHA1_32;
}else if ( keywordcmp ( "AES_CM_256_HMAC_SHA1_32",tmp ) == 0 ){
stream->crypto[valid_count].algo = MS_AES_256_SHA1_32;
}else if ( keywordcmp ( "AES_CM_256_HMAC_SHA1_80",tmp ) == 0 ){
stream->crypto[valid_count].algo = MS_AES_256_SHA1_80;
}else {
tmp2, parameters );
if ( nb >= 3 ) {
MSCryptoSuite cs;
MSCryptoSuiteNameParams np;
np.name=tmp;
np.params=parameters[0]!='\0' ? parameters : NULL;
cs=ms_crypto_suite_build_from_name_params(&np);
if (cs==MS_CRYPTO_SUITE_INVALID){
ms_warning ( "Failed to parse crypto-algo: '%s'", tmp );
stream->crypto[valid_count].algo = 0;
}
if ( stream->crypto[valid_count].algo ) {
strncpy ( stream->crypto[valid_count].master_key, tmp2, 41 );
stream->crypto[valid_count].master_key[40] = '\0';
}else{
char *sep;
strncpy ( stream->crypto[valid_count].master_key, tmp2, sizeof(stream->crypto[valid_count].master_key)-1 );
sep=strchr(stream->crypto[valid_count].master_key,'|');
if (sep) *sep='\0';
stream->crypto[valid_count].algo = cs;
ms_message ( "Found valid crypto line (tag:%d algo:'%s' key:'%s'",
stream->crypto[valid_count].tag,
tmp,
stream->crypto[valid_count].master_key );
valid_count++;
}
} else {
}else{
ms_warning ( "sdp has a strange a= line (%s) nb=%i",belle_sdp_attribute_get_value ( attribute ),nb );
}
}
}
ms_message ( "Found: %d valid crypto lines", valid_count );
ms_message("Found: %d valid crypto lines", valid_count );
}
static void sdp_parse_media_ice_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
@ -425,6 +473,89 @@ static void sdp_parse_media_ice_parameters(belle_sdp_media_description_t *media_
}
}
static void enable_avpf_for_stream(SalStreamDescription *stream) {
MSList *pt_it;
PayloadType *pt;
PayloadTypeAvpfParams avpf_params;
for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
pt = (PayloadType *)pt_it->data;
avpf_params = payload_type_get_avpf_params(pt);
payload_type_set_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
if (stream->type == SalVideo) {
avpf_params.features |= PAYLOAD_TYPE_AVPF_FIR;
}
avpf_params.trr_interval = 0;
payload_type_set_avpf_params(pt, avpf_params);
}
}
static void apply_rtcp_fb_attribute_to_payload(belle_sdp_rtcp_fb_attribute_t *fb_attribute, PayloadType *pt) {
PayloadTypeAvpfParams avpf_params = payload_type_get_avpf_params(pt);
switch (belle_sdp_rtcp_fb_attribute_get_type(fb_attribute)) {
case BELLE_SDP_RTCP_FB_NACK:
switch (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute)) {
case BELLE_SDP_RTCP_FB_PLI:
avpf_params.features |= PAYLOAD_TYPE_AVPF_PLI;
break;
case BELLE_SDP_RTCP_FB_SLI:
avpf_params.features |= PAYLOAD_TYPE_AVPF_SLI;
break;
case BELLE_SDP_RTCP_FB_RPSI:
avpf_params.features |= PAYLOAD_TYPE_AVPF_RPSI;
break;
default:
break;
}
break;
case BELLE_SDP_RTCP_FB_TRR_INT:
avpf_params.trr_interval = (unsigned char)belle_sdp_rtcp_fb_attribute_get_trr_int(fb_attribute);
break;
case BELLE_SDP_RTCP_FB_ACK:
default:
break;
}
payload_type_set_avpf_params(pt, avpf_params);
}
static void sdp_parse_rtcp_fb_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
belle_sip_list_t *it;
belle_sdp_attribute_t *attribute;
belle_sdp_rtcp_fb_attribute_t *fb_attribute;
MSList *pt_it;
PayloadType *pt;
int8_t pt_num;
/* Handle rtcp-fb attributes that concern all payload types. */
for (it = belle_sdp_media_description_get_attributes(media_desc); it != NULL; it = it->next) {
attribute = BELLE_SDP_ATTRIBUTE(it->data);
if (keywordcmp("rtcp-fb", belle_sdp_attribute_get_name(attribute)) == 0) {
fb_attribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute);
if (belle_sdp_rtcp_fb_attribute_get_id(fb_attribute) == -1) {
for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
pt = (PayloadType *)pt_it->data;
apply_rtcp_fb_attribute_to_payload(fb_attribute, pt);
}
}
}
}
/* Handle rtcp-fb attributes that are specefic to a payload type. */
for (it = belle_sdp_media_description_get_attributes(media_desc); it != NULL; it = it->next) {
attribute = BELLE_SDP_ATTRIBUTE(it->data);
if (keywordcmp("rtcp-fb", belle_sdp_attribute_get_name(attribute)) == 0) {
fb_attribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute);
pt_num = belle_sdp_rtcp_fb_attribute_get_id(fb_attribute);
for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
pt = (PayloadType *)pt_it->data;
if (payload_type_get_number(pt) == (int)pt_num) {
apply_rtcp_fb_attribute_to_payload(fb_attribute, pt);
}
}
}
}
}
static void sal_init_rtcp_xr_description(OrtpRtcpXrConfiguration *config) {
config->enabled = FALSE;
config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttNone;
@ -450,7 +581,7 @@ static void sdp_parse_rtcp_xr_parameters(const belle_sdp_attribute_t *attribute,
}
config->stat_summary_enabled = (belle_sdp_rtcp_xr_attribute_has_stat_summary(xr_attr) != 0);
if (config->stat_summary_enabled) {
belle_sip_list_t *stat_summary_flag_it;
const belle_sip_list_t *stat_summary_flag_it;
for (stat_summary_flag_it = belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(xr_attr); stat_summary_flag_it != NULL; stat_summary_flag_it = stat_summary_flag_it->next ) {
const char *flag = (const char *)stat_summary_flag_it->data;
if (flag != NULL) {
@ -493,11 +624,15 @@ static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md,
proto = belle_sdp_media_get_protocol ( media );
stream->proto=SalProtoOther;
if ( proto ) {
if ( strcasecmp ( proto,"RTP/AVP" ) ==0 )
stream->proto=SalProtoRtpAvp;
else if ( strcasecmp ( proto,"RTP/SAVP" ) ==0 ) {
stream->proto=SalProtoRtpSavp;
}else{
if (strcasecmp(proto, "RTP/AVP") == 0) {
stream->proto = SalProtoRtpAvp;
} else if (strcasecmp(proto, "RTP/SAVP") == 0) {
stream->proto = SalProtoRtpSavp;
} else if (strcasecmp(proto, "RTP/AVPF") == 0) {
stream->proto = SalProtoRtpAvpf;
} else if (strcasecmp(proto, "RTP/SAVPF") == 0) {
stream->proto = SalProtoRtpSavpf;
} else {
strncpy(stream->proto_other,proto,sizeof(stream->proto_other)-1);
}
}
@ -556,13 +691,19 @@ static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md,
}
/* Read crypto lines if any */
if ( stream->proto == SalProtoRtpSavp ) {
if ((stream->proto == SalProtoRtpSavpf) || (stream->proto == SalProtoRtpSavp)) {
sdp_parse_media_crypto_parameters(media_desc, stream);
}
/* Get ICE candidate attributes if any */
sdp_parse_media_ice_parameters(media_desc, stream);
/* Get RTCP-FB attributes if any */
if ((stream->proto == SalProtoRtpAvpf) || (stream->proto == SalProtoRtpSavpf)) {
enable_avpf_for_stream(stream);
sdp_parse_rtcp_fb_parameters(media_desc, stream);
}
/* Get RTCP-XR attributes if any */
stream->rtcp_xr = md->rtcp_xr; // Use session parameters if no stream parameters are defined
sdp_parse_media_rtcp_xr_parameters(media_desc, &stream->rtcp_xr);

View file

@ -72,6 +72,32 @@ void linphone_core_update_streams_destinations(LinphoneCore *lc, LinphoneCall *c
#endif
}
static void _clear_early_media_destinations(LinphoneCall *call, MediaStream *ms){
RtpSession *session=ms->sessions.rtp_session;
rtp_session_clear_aux_remote_addr(session);
if (!call->ice_session) rtp_session_set_symmetric_rtp(session,TRUE);/*restore symmetric rtp if ICE is not used*/
}
static void clear_early_media_destinations(LinphoneCall *call){
if (call->audiostream){
_clear_early_media_destinations(call,(MediaStream*)call->audiostream);
}
if (call->videostream){
_clear_early_media_destinations(call,(MediaStream*)call->videostream);
}
}
static void prepare_early_media_forking(LinphoneCall *call){
/*we need to disable symmetric rtp otherwise our outgoing streams will be switching permanently between the multiple destinations*/
if (call->audiostream){
rtp_session_set_symmetric_rtp(call->audiostream->ms.sessions.rtp_session,FALSE);
}
if (call->videostream){
rtp_session_set_symmetric_rtp(call->videostream->ms.sessions.rtp_session,FALSE);
}
}
void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md){
SalMediaDescription *oldmd=call->resultdesc;
bool_t all_muted=FALSE;
@ -98,6 +124,7 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
call->expect_media_in_ack=FALSE;
call->resultdesc=new_md;
if ((call->audiostream && call->audiostream->ms.state==MSStreamStarted) || (call->videostream && call->videostream->ms.state==MSStreamStarted)){
clear_early_media_destinations(call);
/* we already started media: check if we really need to restart it*/
if (oldmd){
int md_changed = media_parameters_changed(call, oldmd, new_md);
@ -146,6 +173,9 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
if ((call->state==LinphoneCallIncomingEarlyMedia || call->state==LinphoneCallOutgoingEarlyMedia) && !call->params.real_early_media){
all_muted=TRUE;
}
if (call->params.real_early_media && call->state==LinphoneCallOutgoingEarlyMedia){
prepare_early_media_forking(call);
}
linphone_call_start_media_streams(call,all_muted,send_ringbacktone);
if (call->state==LinphoneCallPausing && call->paused_by_app && ms_list_size(lc->calls)==1){
linphone_core_play_named_tone(lc,LinphoneToneCallOnHold);
@ -276,6 +306,38 @@ static void call_received(SalOp *h){
linphone_core_notify_incoming_call(lc,call);
}
static void try_early_media_forking(LinphoneCall *call, SalMediaDescription *md){
SalMediaDescription *cur_md=call->resultdesc;
int i;
SalStreamDescription *ref_stream,*new_stream;
ms_message("Early media response received from another branch, checking if media can be forked to this new destination.");
for (i=0;i<cur_md->n_active_streams;++i){
ref_stream=&cur_md->streams[i];
new_stream=&md->streams[i];
if (ref_stream->type==new_stream->type && ref_stream->payloads && new_stream->payloads){
PayloadType *refpt, *newpt;
refpt=(PayloadType*)ref_stream->payloads->data;
newpt=(PayloadType*)new_stream->payloads->data;
if (strcmp(refpt->mime_type,newpt->mime_type)==0 && refpt->clock_rate==newpt->clock_rate
&& payload_type_get_number(refpt)==payload_type_get_number(newpt)){
MediaStream *ms=NULL;
if (ref_stream->type==SalAudio){
ms=(MediaStream*)call->audiostream;
}else if (ref_stream->type==SalVideo){
ms=(MediaStream*)call->videostream;
}
if (ms){
RtpSession *session=ms->sessions.rtp_session;
const char *rtp_addr=new_stream->rtp_addr[0]!='\0' ? new_stream->rtp_addr : md->addr;
const char *rtcp_addr=new_stream->rtcp_addr[0]!='\0' ? new_stream->rtcp_addr : md->addr;
rtp_session_add_aux_remote_addr_full(session,rtp_addr,new_stream->rtp_port,rtcp_addr,new_stream->rtcp_port);
}
}
}
}
}
static void call_ringing(SalOp *h){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(h);
@ -309,7 +371,7 @@ static void call_ringing(SalOp *h){
/*accept early media */
if (call->audiostream && audio_stream_started(call->audiostream)){
/*streams already started */
ms_message("Early media already started.");
try_early_media_forking(call,md);
return;
}
if (lc->vtable.show) lc->vtable.show(lc);
@ -460,6 +522,8 @@ static void call_accept_update(LinphoneCore *lc, LinphoneCall *call){
}
static void call_resumed(LinphoneCore *lc, LinphoneCall *call){
/*when we are resumed, increment session id, because sdp is changed (a=recvonly disapears)*/
linphone_call_increment_local_media_description(call);
call_accept_update(lc,call);
if(lc->vtable.display_status)
lc->vtable.display_status(lc,_("We have been resumed."));
@ -467,6 +531,8 @@ static void call_resumed(LinphoneCore *lc, LinphoneCall *call){
}
static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){
/*when we are resumed, increment session id, because sdp is changed (a=recvonly appears)*/
linphone_call_increment_local_media_description(call);
call_accept_update(lc,call);
/* we are being paused */
if(lc->vtable.display_status)
@ -666,24 +732,32 @@ static void call_failure(SalOp *op){
break;
case SalReasonUnsupportedContent: /*<this is for compatibility: linphone sent 415 because of SDP offer answer failure*/
case SalReasonNotAcceptable:
//media_encryption_mandatory
if (call->params.media_encryption == LinphoneMediaEncryptionSRTP &&
!linphone_core_is_media_encryption_mandatory(lc)) {
ms_message("Outgoing call [%p] failed with SRTP and/or AVPF enabled", call);
if ((call->state == LinphoneCallOutgoingInit)
|| (call->state == LinphoneCallOutgoingProgress)
|| (call->state == LinphoneCallOutgoingRinging) /* Push notification case */
|| (call->state == LinphoneCallOutgoingEarlyMedia)) {
int i;
ms_message("Outgoing call [%p] failed with SRTP (SAVP) enabled",call);
if (call->state==LinphoneCallOutgoingInit
|| call->state==LinphoneCallOutgoingProgress
|| call->state==LinphoneCallOutgoingRinging /*push case*/
|| call->state==LinphoneCallOutgoingEarlyMedia){
ms_message("Retrying call [%p] with AVP",call);
/* clear SRTP local params */
call->params.media_encryption = LinphoneMediaEncryptionNone;
for(i=0; i<call->localdesc->n_active_streams; i++) {
call->localdesc->streams[i].proto = SalProtoRtpAvp;
memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto));
for (i = 0; i < call->localdesc->n_active_streams; i++) {
if (call->params.media_encryption == LinphoneMediaEncryptionSRTP) {
if (call->params.avpf_enabled == TRUE) {
if (i == 0) ms_message("Retrying call [%p] with SAVP", call);
call->params.avpf_enabled = FALSE;
linphone_core_restart_invite(lc, call);
return;
} else if (!linphone_core_is_media_encryption_mandatory(lc)) {
if (i == 0) ms_message("Retrying call [%p] with AVP", call);
call->params.media_encryption = LinphoneMediaEncryptionNone;
memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto));
linphone_core_restart_invite(lc, call);
return;
}
} else if (call->params.avpf_enabled == TRUE) {
if (i == 0) ms_message("Retrying call [%p] with AVP", call);
call->params.avpf_enabled = FALSE;
linphone_core_restart_invite(lc, call);
return;
}
linphone_core_restart_invite(lc, call);
return;
}
}
msg=_("Incompatible media parameters.");

View file

@ -407,7 +407,7 @@ void linphone_chat_room_message_received(LinphoneChatRoom *cr, LinphoneCore *lc,
/**
* Retrieve an existing chat room whose peer is the supplied address, if exists.
* @param lc the linphone core
* @param add a linphone address.
* @param addr a linphone address.
* @returns the matching chatroom, or NULL if no such chatroom exists.
**/
LinphoneChatRoom *linphone_core_get_chat_room(LinphoneCore *lc, const LinphoneAddress *addr){
@ -606,11 +606,19 @@ bool_t linphone_chat_room_is_remote_composing(const LinphoneChatRoom *cr) {
/**
* Returns back pointer to LinphoneCore object.
* @deprecated use linphone_chat_room_get_core()
**/
LinphoneCore* linphone_chat_room_get_lc(LinphoneChatRoom *cr){
return cr->lc;
}
/**
* Returns back pointer to LinphoneCore object.
**/
LinphoneCore* linphone_chat_room_get_core(LinphoneChatRoom *cr){
return cr->lc;
}
/**
* Assign a user pointer to the chat room.
**/
@ -891,6 +899,36 @@ void linphone_chat_message_set_external_body_url(LinphoneChatMessage* message,co
message->external_body_url=url?ms_strdup(url):NULL;
}
/**
* Linphone message has an app-specific field that can store a text. The application might want
* to use it for keeping data over restarts, like thumbnail image path.
* @param message #LinphoneChatMessage
* @return the application-specific data or NULL if none has been stored.
*/
const char* linphone_chat_message_get_appdata(const LinphoneChatMessage* message){
return message->appdata;
}
/**
* Linphone message has an app-specific field that can store a text. The application might want
* to use it for keeping data over restarts, like thumbnail image path.
*
* Invoking this function will attempt to update the message storage to reflect the changeif it is
* enabled.
*
* @param message #LinphoneChatMessage
* @param data the data to store into the message
*/
void linphone_chat_message_set_appdata(LinphoneChatMessage* message, const char* data){
if( message->appdata ){
ms_free(message->appdata);
}
message->appdata = data? ms_strdup(data) : NULL;
linphone_chat_message_store_appdata(message);
}
/**
* Get the file_transfer_information (used by call backs to recover informations during a rcs file transfer)
*
@ -1109,6 +1147,7 @@ LinphoneChatMessage* linphone_chat_message_clone(const LinphoneChatMessage* msg)
};*/
LinphoneChatMessage* new_message = linphone_chat_room_create_message(msg->chat_room,msg->message);
if (msg->external_body_url) new_message->external_body_url=ms_strdup(msg->external_body_url);
if (msg->appdata) new_message->appdata = ms_strdup(msg->appdata);
new_message->cb=msg->cb;
new_message->cb_ud=msg->cb_ud;
new_message->message_userdata=msg->message_userdata;
@ -1135,6 +1174,7 @@ static void _linphone_chat_message_destroy(LinphoneChatMessage* msg) {
if (msg->op) sal_op_release(msg->op);
if (msg->message) ms_free(msg->message);
if (msg->external_body_url) ms_free(msg->external_body_url);
if (msg->appdata) ms_free(msg->appdata);
if (msg->from) linphone_address_destroy(msg->from);
if (msg->to) linphone_address_destroy(msg->to);
if (msg->custom_headers) sal_custom_header_free(msg->custom_headers);

View file

@ -200,7 +200,8 @@ int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call){
params->has_video=FALSE;
if (call->audiostream || call->videostream){
linphone_call_stop_media_streams (call); /*free the audio & video local resources*/
linphone_call_stop_media_streams(call); /*free the audio & video local resources*/
linphone_call_init_media_streams(call);
}
if (call==lc->current_call){
lc->current_call=NULL;

View file

@ -97,12 +97,12 @@ typedef void (*LinphoneCoreNotifyReceivedCb)(LinphoneCore *lc, LinphoneEvent *le
/**
* Callback prototype for notifying the application about changes of subscription states, including arrival of new subscriptions.
**/
**/
typedef void (*LinphoneCoreSubscriptionStateChangedCb)(LinphoneCore *lc, LinphoneEvent *lev, LinphoneSubscriptionState state);
/**
* Callback prototype for notifying the application about changes of publish states.
**/
**/
typedef void (*LinphoneCorePublishStateChangedCb)(LinphoneCore *lc, LinphoneEvent *lev, LinphonePublishState state);
/**
@ -125,7 +125,6 @@ LINPHONE_PUBLIC LinphoneEvent *linphone_core_subscribe(LinphoneCore *lc, const L
* @param resource the destination resource
* @param event the event name
* @param expires the whished duration of the subscription
* @param body an optional body, may be NULL.
* @return a LinphoneEvent holding the context of the created subcription.
**/
LINPHONE_PUBLIC LinphoneEvent *linphone_core_create_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires);
@ -258,7 +257,7 @@ LINPHONE_PUBLIC const char *linphone_event_get_custom_header(LinphoneEvent *ev,
/**
* Terminate an incoming or outgoing subscription that was previously acccepted, or a previous publication.
* This function does not unref the object. The core will unref() if it does not need this object anymore.
*
*
* For subscribed event, when the subscription is terminated normally or because of an error, the core will unref.
* For published events, no unref is performed. This is because it is allowed to re-publish an expired publish, as well as retry it in case of error.
**/
@ -270,7 +269,7 @@ LINPHONE_PUBLIC void linphone_event_terminate(LinphoneEvent *lev);
* By default LinphoneEvents created by the core are owned by the core only.
* An application that wishes to retain a reference to it must call linphone_event_ref().
* When this reference is no longer needed, linphone_event_unref() must be called.
*
*
**/
LINPHONE_PUBLIC LinphoneEvent *linphone_event_ref(LinphoneEvent *lev);

View file

@ -22,7 +22,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef LINPHONETUNNEL_H
#define LINPHONETUNNEL_H
@ -86,7 +86,7 @@ LINPHONE_PUBLIC void linphone_tunnel_config_set_port(LinphoneTunnelConfig *tunne
LINPHONE_PUBLIC int linphone_tunnel_config_get_port(const LinphoneTunnelConfig *tunnel);
/**
* Set the remote port on the tunnel server side used to test udp reachability.
* Set the remote port on the tunnel server side used to test udp reachability.
*
* @param tunnel configuration object
* @param remote_udp_mirror_port remote port on the tunnel server side used to test udp reachability, set to -1 to disable the feature
@ -110,7 +110,7 @@ LINPHONE_PUBLIC void linphone_tunnel_config_set_delay(LinphoneTunnelConfig *tunn
/**
* Get the udp packet round trip delay in ms for a tunnel configuration.
*
*
* @param tunnel configuration object
*/
LINPHONE_PUBLIC int linphone_tunnel_config_get_delay(const LinphoneTunnelConfig *tunnel);
@ -132,7 +132,7 @@ LINPHONE_PUBLIC void linphone_tunnel_add_server(LinphoneTunnel *tunnel, Linphone
/**
* Remove tunnel server configuration
*
*
* @param tunnel object
* @param tunnel_config object
*/
@ -208,7 +208,7 @@ LINPHONE_PUBLIC bool_t linphone_tunnel_auto_detect_enabled(LinphoneTunnel *tunne
* @param host Http proxy host.
* @param port http proxy port.
* @param username optional http proxy username if the proxy request authentication. Currently only basic authentication is supported. Use NULL if not needed.
* @param password optional http proxy password. Use NULL if not needed.
* @param passwd optional http proxy password. Use NULL if not needed.
**/
LINPHONE_PUBLIC void linphone_tunnel_set_http_proxy(LinphoneTunnel *tunnel, const char *host, int port, const char* username,const char* passwd);
@ -218,7 +218,7 @@ LINPHONE_PUBLIC void linphone_tunnel_set_http_proxy(LinphoneTunnel *tunnel, cons
* @param host Http proxy host.
* @param port http proxy port.
* @param username optional http proxy username if the proxy request authentication. Currently only basic authentication is supported. Use NULL if not needed.
* @param password optional http proxy password. Use NULL if not needed.
* @param passwd optional http proxy password. Use NULL if not needed.
**/
LINPHONE_PUBLIC void linphone_tunnel_get_http_proxy(LinphoneTunnel*tunnel,const char **host, int *port, const char **username, const char **passwd);

View file

@ -37,6 +37,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "mediastreamer2/mseventqueue.h"
#include "mediastreamer2/mssndcard.h"
static void linphone_call_stats_uninit(LinphoneCallStats *stats);
#ifdef VIDEO_ENABLED
static MSWebCam *get_nowebcam_device(){
return ms_web_cam_manager_get_cam(ms_web_cam_manager_get(),"StaticImage: Static picture");
@ -94,27 +96,63 @@ bool_t linphone_call_get_authentication_token_verified(LinphoneCall *call){
return call->auth_token_verified;
}
static bool_t linphone_call_are_all_streams_encrypted(LinphoneCall *call) {
static bool_t linphone_call_all_streams_encrypted(const LinphoneCall *call) {
int number_of_encrypted_stream = 0;
int number_of_active_stream = 0;
if (call) {
if (call->audiostream && media_stream_get_state((MediaStream *)call->audiostream) == MSStreamStarted) {
number_of_active_stream++;
if(media_stream_is_secured((MediaStream *)call->audiostream))
if(media_stream_secured((MediaStream *)call->audiostream))
number_of_encrypted_stream++;
}
if (call->videostream && media_stream_get_state((MediaStream *)call->videostream) == MSStreamStarted) {
number_of_active_stream++;
if (media_stream_is_secured((MediaStream *)call->videostream))
if (media_stream_secured((MediaStream *)call->videostream))
number_of_encrypted_stream++;
}
}
return number_of_active_stream>0 && number_of_active_stream==number_of_encrypted_stream;
}
static bool_t linphone_call_all_streams_avpf_enabled(const LinphoneCall *call) {
int nb_active_streams = 0;
int nb_avpf_enabled_streams = 0;
if (call) {
if (call->audiostream && media_stream_get_state((MediaStream *)call->audiostream) == MSStreamStarted) {
nb_active_streams++;
if (media_stream_avpf_enabled((MediaStream *)call->audiostream))
nb_avpf_enabled_streams++;
}
if (call->videostream && media_stream_get_state((MediaStream *)call->videostream) == MSStreamStarted) {
nb_active_streams++;
if (media_stream_avpf_enabled((MediaStream *)call->videostream))
nb_avpf_enabled_streams++;
}
}
return ((nb_active_streams > 0) && (nb_active_streams == nb_avpf_enabled_streams));
}
static uint8_t linphone_call_get_avpf_rr_interval(const LinphoneCall *call) {
uint8_t rr_interval = 0;
uint8_t stream_rr_interval;
if (call) {
if (call->audiostream && media_stream_get_state((MediaStream *)call->audiostream) == MSStreamStarted) {
stream_rr_interval = media_stream_get_avpf_rr_interval((MediaStream *)call->audiostream);
if (stream_rr_interval > rr_interval) rr_interval = stream_rr_interval;
}
if (call->videostream && media_stream_get_state((MediaStream *)call->videostream) == MSStreamStarted) {
stream_rr_interval = media_stream_get_avpf_rr_interval((MediaStream *)call->videostream);
if (stream_rr_interval > rr_interval) rr_interval = stream_rr_interval;
}
} else {
rr_interval = 5;
}
return rr_interval;
}
static void propagate_encryption_changed(LinphoneCall *call){
LinphoneCore *lc=call->core;
if (!linphone_call_are_all_streams_encrypted(call)) {
if (!linphone_call_all_streams_encrypted(call)) {
ms_message("Some streams are not encrypted");
call->current_params.media_encryption=LinphoneMediaEncryptionNone;
if (lc->vtable.call_encryption_changed)
@ -197,7 +235,6 @@ static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandw
if (max_sample_rate) *max_sample_rate=0;
for(it=codecs;it!=NULL;it=it->next){
PayloadType *pt=(PayloadType*)it->data;
payload_type_unset_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED); /* Disable AVPF for the moment. */
if (pt->flags & PAYLOAD_TYPE_ENABLED){
if (bandwidth_limit>0 && !linphone_core_is_payload_type_usable_for_bandwidth(lc,pt,bandwidth_limit)){
ms_message("Codec %s/%i eliminated because of audio bandwidth constraint of %i kbit/s",
@ -232,35 +269,81 @@ static void update_media_description_from_stun(SalMediaDescription *md, const St
}
}
static int setup_encryption_key(SalSrtpCryptoAlgo *crypto, MSCryptoSuite suite, unsigned int tag){
int keylen=0;
crypto->tag=tag;
crypto->algo=suite;
switch(suite){
case MS_AES_128_SHA1_80:
case MS_AES_128_SHA1_32:
case MS_AES_128_NO_AUTH:
case MS_NO_CIPHER_SHA1_80: /*not sure for this one*/
keylen=30;
break;
case MS_AES_256_SHA1_80:
case MS_AES_256_SHA1_32:
keylen=46;
break;
case MS_CRYPTO_SUITE_INVALID:
break;
}
if (keylen==0 || !generate_b64_crypto_key(30, crypto->master_key, SAL_SRTP_KEY_SIZE)){
ms_error("Could not generate SRTP key.");
crypto->algo = 0;
return -1;
}
return 0;
}
static void setup_encryption_keys(LinphoneCall *call, SalMediaDescription *md){
LinphoneCore *lc=call->core;
int i;
int i,j;
SalMediaDescription *old_md=call->localdesc;
bool_t keep_srtp_keys=lp_config_get_int(lc->config,"sip","keep_srtp_keys",1);
for(i=0; i<md->n_active_streams; i++) {
if (md->streams[i].proto == SalProtoRtpSavp) {
if (keep_srtp_keys && old_md && old_md->streams[i].proto==SalProtoRtpSavp){
if (stream_description_has_srtp(&md->streams[i]) == TRUE) {
if (keep_srtp_keys && old_md && stream_description_has_srtp(&old_md->streams[i]) == TRUE){
int j;
ms_message("Keeping same crypto keys.");
for(j=0;j<SAL_CRYPTO_ALGO_MAX;++j){
memcpy(&md->streams[i].crypto[j],&old_md->streams[i].crypto[j],sizeof(SalSrtpCryptoAlgo));
}
}else{
md->streams[i].crypto[0].tag = 1;
md->streams[i].crypto[0].algo = MS_AES_128_SHA1_80;
if (!generate_b64_crypto_key(30, md->streams[i].crypto[0].master_key, SAL_SRTP_KEY_SIZE))
md->streams[i].crypto[0].algo = 0;
md->streams[i].crypto[1].tag = 2;
md->streams[i].crypto[1].algo = MS_AES_128_SHA1_32;
if (!generate_b64_crypto_key(30, md->streams[i].crypto[1].master_key, SAL_SRTP_KEY_SIZE))
md->streams[i].crypto[1].algo = 0;
md->streams[i].crypto[2].algo = 0;
const MSCryptoSuite *suites=linphone_core_get_srtp_crypto_suites(lc);
for(j=0;suites!=NULL && suites[j]!=MS_CRYPTO_SUITE_INVALID && j<SAL_CRYPTO_ALGO_MAX;++j){
setup_encryption_key(&md->streams[i].crypto[j],suites[j],j+1);
}
}
}
}
}
static void setup_rtcp_fb(LinphoneCall *call, SalMediaDescription *md) {
MSList *pt_it;
PayloadType *pt;
PayloadTypeAvpfParams avpf_params;
int i;
for (i = 0; i < md->n_active_streams; i++) {
for (pt_it = md->streams[i].payloads; pt_it != NULL; pt_it = pt_it->next) {
pt = (PayloadType *)pt_it->data;
if (call->params.avpf_enabled == TRUE) {
payload_type_set_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
avpf_params = payload_type_get_avpf_params(pt);
avpf_params.trr_interval = call->params.avpf_rr_interval;
if (md->streams[i].type == SalVideo) {
avpf_params.features |= PAYLOAD_TYPE_AVPF_FIR;
}
} else {
payload_type_unset_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
memset(&avpf_params, 0, sizeof(avpf_params));
}
payload_type_set_avpf_params(pt, avpf_params);
}
}
}
static void setup_rtcp_xr(LinphoneCall *call, SalMediaDescription *md) {
LinphoneCore *lc = call->core;
int i;
@ -285,6 +368,18 @@ static void setup_rtcp_xr(LinphoneCall *call, SalMediaDescription *md) {
}
}
void linphone_call_increment_local_media_description(LinphoneCall *call){
SalMediaDescription *md=call->localdesc;
md->session_ver++;
}
static SalMediaProto get_proto_from_call_params(const LinphoneCallParams *params) {
if ((params->media_encryption == LinphoneMediaEncryptionSRTP) && params->avpf_enabled) return SalProtoRtpSavpf;
if (params->media_encryption == LinphoneMediaEncryptionSRTP) return SalProtoRtpSavp;
if (params->avpf_enabled) return SalProtoRtpAvpf;
return SalProtoRtpAvp;
}
void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call){
MSList *l;
PayloadType *pt;
@ -323,8 +418,7 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *
strncpy(md->streams[0].name,"Audio",sizeof(md->streams[0].name)-1);
md->streams[0].rtp_port=call->media_ports[0].rtp_port;
md->streams[0].rtcp_port=call->media_ports[0].rtcp_port;
md->streams[0].proto=(call->params.media_encryption == LinphoneMediaEncryptionSRTP) ?
SalProtoRtpSavp : SalProtoRtpAvp;
md->streams[0].proto=get_proto_from_call_params(&call->params);
md->streams[0].type=SalAudio;
if (call->params.down_ptime)
md->streams[0].ptime=call->params.down_ptime;
@ -360,6 +454,7 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *
}
setup_encryption_keys(call,md);
setup_rtcp_fb(call, md);
setup_rtcp_xr(call, md);
update_media_description_from_stun(md,&call->ac,&call->vc);
@ -623,11 +718,17 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro
call->current_params.privacy=(LinphonePrivacyMask)sal_op_get_privacy(call->op);
/*set video support */
md=sal_call_get_remote_media_description(op);
call->params.has_video = !!lc->video_policy.automatically_accept;
call->params.has_video = lc->video_policy.automatically_accept;
if (md) {
// It is licit to receive an INVITE without SDP
// In this case WE chose the media parameters according to policy.
call->params.has_video &= linphone_core_media_description_contains_video_stream(md);
/* Handle AVPF and SRTP. */
call->params.avpf_enabled = media_description_has_avpf(md);
if ((media_description_has_srtp(md) == TRUE) && (media_stream_srtp_supported() == TRUE)) {
call->params.media_encryption = LinphoneMediaEncryptionSRTP;
}
}
fpol=linphone_core_get_firewall_policy(call->core);
/*create the ice session now if ICE is required*/
@ -686,7 +787,8 @@ static void linphone_call_set_terminated(LinphoneCall *call){
linphone_call_delete_upnp_session(call);
linphone_call_delete_ice_session(call);
linphone_core_update_allocated_audio_bandwidth(lc);
linphone_call_stats_uninit(&call->stats[0]);
linphone_call_stats_uninit(&call->stats[1]);
call->owns_call_log=FALSE;
linphone_call_log_completed(call);
@ -805,7 +907,7 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const
if (cstate==LinphoneCallEnd){
if (call->log->status == LinphoneCallSuccess)
linphone_reporting_publish(call);
linphone_reporting_publish_session_report(call);
}
if (cstate==LinphoneCallReleased){
@ -922,7 +1024,7 @@ const LinphoneCallParams * linphone_call_get_current_params(LinphoneCall *call){
}
#endif
if (linphone_call_are_all_streams_encrypted(call)) {
if (linphone_call_all_streams_encrypted(call)) {
if (linphone_call_get_authentication_token(call)) {
call->current_params.media_encryption=LinphoneMediaEncryptionZRTP;
} else {
@ -931,14 +1033,16 @@ const LinphoneCallParams * linphone_call_get_current_params(LinphoneCall *call){
} else {
call->current_params.media_encryption=LinphoneMediaEncryptionNone;
}
call->current_params.avpf_enabled = linphone_call_all_streams_avpf_enabled(call);
if (call->current_params.avpf_enabled == TRUE) {
call->current_params.avpf_rr_interval = linphone_call_get_avpf_rr_interval(call);
} else {
call->current_params.avpf_rr_interval = 0;
}
return &call->current_params;
}
static bool_t is_video_active(const SalStreamDescription *sd){
return sd->rtp_port!=0 && sd->dir!=SalStreamInactive;
}
/**
* Returns call parameters proposed by remote.
*
@ -950,19 +1054,20 @@ const LinphoneCallParams * linphone_call_get_remote_params(LinphoneCall *call){
memset(cp,0,sizeof(*cp));
if (call->op){
SalMediaDescription *md=sal_call_get_remote_media_description(call->op);
if (md){
SalStreamDescription *asd,*vsd,*secure_asd,*secure_vsd;
if (md) {
SalStreamDescription *sd;
unsigned int i;
unsigned int nb_audio_streams = sal_media_description_nb_active_streams_of_type(md, SalAudio);
unsigned int nb_video_streams = sal_media_description_nb_active_streams_of_type(md, SalVideo);
asd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalAudio);
vsd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalVideo);
secure_asd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalAudio);
secure_vsd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalVideo);
if (secure_vsd){
cp->has_video=is_video_active(secure_vsd);
if (secure_asd || asd==NULL)
cp->media_encryption=LinphoneMediaEncryptionSRTP;
}else if (vsd){
cp->has_video=is_video_active(vsd);
for (i = 0; i < nb_video_streams; i++) {
sd = sal_media_description_get_active_stream_of_type(md, SalVideo, i);
if (is_video_active(sd) == TRUE) cp->has_video = TRUE;
if (stream_description_has_srtp(sd) == TRUE) cp->media_encryption = LinphoneMediaEncryptionSRTP;
}
for (i = 0; i < nb_audio_streams; i++) {
sd = sal_media_description_get_active_stream_of_type(md, SalAudio, i);
if (stream_description_has_srtp(sd) == TRUE) cp->media_encryption = LinphoneMediaEncryptionSRTP;
}
if (!cp->has_video){
if (md->bandwidth>0 && md->bandwidth<=linphone_core_get_edge_bw(call->core)){
@ -1217,6 +1322,10 @@ MSVideoSize linphone_call_params_get_received_video_size(const LinphoneCallParam
return cp->recv_vsize;
}
const char * linphone_call_params_get_rtp_profile(const LinphoneCallParams *cp) {
return sal_media_proto_to_string(get_proto_from_call_params(cp));
}
/**
* @ingroup call_control
* Use to know if this call has been configured in low bandwidth mode.
@ -1338,7 +1447,7 @@ void _linphone_call_params_copy(LinphoneCallParams *ncp, const LinphoneCallParam
* Set requested level of privacy for the call.
* \xmlonly <language-tags>javascript</language-tags> \endxmlonly
* @param params the call parameters to be modified
* @param LinphonePrivacy to configure privacy
* @param privacy LinphonePrivacy to configure privacy
* */
void linphone_call_params_set_privacy(LinphoneCallParams *params, LinphonePrivacyMask privacy) {
params->privacy=privacy;
@ -1408,7 +1517,6 @@ static void rendercb(void *data, const MSPicture *local, const MSPicture *remote
#ifdef VIDEO_ENABLED
static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const unsigned int event_id, const void *args){
LinphoneCall* call = (LinphoneCall*) user_pointer;
ms_warning("In linphonecall.c: video_stream_event_cb");
switch (event_id) {
case MS_VIDEO_DECODER_DECODING_ERRORS:
ms_warning("Case is MS_VIDEO_DECODER_DECODING_ERRORS");
@ -1422,6 +1530,11 @@ static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const u
if (call->nextVideoFrameDecoded._func != NULL)
call->nextVideoFrameDecoded._func(call, call->nextVideoFrameDecoded._user_data);
break;
case MS_VIDEO_DECODER_SEND_PLI:
case MS_VIDEO_DECODER_SEND_SLI:
case MS_VIDEO_DECODER_SEND_RPSI:
/* Handled internally by mediastreamer2. */
break;
default:
ms_warning("Unhandled event %i", event_id);
break;
@ -1469,7 +1582,7 @@ int linphone_call_prepare_ice(LinphoneCall *call, bool_t incoming_offer){
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);
has_video=call->params.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);
@ -1711,7 +1824,7 @@ static int get_ideal_audio_bw(LinphoneCall *call, const SalMediaDescription *md,
const LinphoneCallParams *params=&call->params;
bool_t will_use_video=linphone_core_media_description_contains_video_stream(md);
bool_t forced=FALSE;
if (desc->bandwidth>0) remote_bw=desc->bandwidth;
else if (md->bandwidth>0) {
/*case where b=AS is given globally, not per stream*/
@ -1723,7 +1836,7 @@ static int get_ideal_audio_bw(LinphoneCall *call, const SalMediaDescription *md,
}else upload_bw=total_upload_bw;
upload_bw=get_min_bandwidth(upload_bw,remote_bw);
if (!will_use_video || forced) return upload_bw;
if (bandwidth_is_greater(upload_bw,512)){
upload_bw=100;
}else if (bandwidth_is_greater(upload_bw,256)){
@ -1756,13 +1869,13 @@ static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *m
LinphoneCore *lc=call->core;
int up_ptime=0;
const LinphoneCallParams *params=&call->params;
*used_pt=-1;
if (desc->type==SalAudio)
bw=get_ideal_audio_bw(call,md,desc);
else if (desc->type==SalVideo)
bw=get_video_bw(call,md,desc);
for(elem=desc->payloads;elem!=NULL;elem=elem->next){
PayloadType *pt=(PayloadType*)elem->data;
int number;
@ -1835,12 +1948,10 @@ static void configure_rtp_session_for_rtcp_xr(LinphoneCore *lc, LinphoneCall *ca
const SalStreamDescription *localstream;
const SalStreamDescription *remotestream;
localstream = sal_media_description_find_stream(call->localdesc, SalProtoRtpSavp, type);
if (!localstream) localstream = sal_media_description_find_stream(call->localdesc, SalProtoRtpAvp, type);
localstream = sal_media_description_find_best_stream(call->localdesc, type);
if (!localstream) return;
localconfig = &localstream->rtcp_xr;
remotestream = sal_media_description_find_stream(sal_call_get_remote_media_description(call->op), SalProtoRtpSavp, type);
if (!remotestream) remotestream = sal_media_description_find_stream(sal_call_get_remote_media_description(call->op), SalProtoRtpAvp, type);
remotestream = sal_media_description_find_best_stream(sal_call_get_remote_media_description(call->op), type);
if (!remotestream) return;
remoteconfig = &remotestream->rtcp_xr;
@ -1859,15 +1970,6 @@ static void configure_rtp_session_for_rtcp_xr(LinphoneCore *lc, LinphoneCall *ca
session = call->videostream->ms.sessions.rtp_session;
}
rtp_session_configure_rtcp_xr(session, &currentconfig);
if (currentconfig.rcvr_rtt_mode != OrtpRtcpXrRcvrRttNone) {
rtp_session_set_rtcp_xr_rcvr_rtt_interval(session, lp_config_get_int(lc->config, "rtp", "rtcp_xr_rcvr_rtt_interval_duration", 5000));
}
if (currentconfig.stat_summary_enabled == TRUE) {
rtp_session_set_rtcp_xr_stat_summary_interval(session, lp_config_get_int(lc->config, "rtp", "rtcp_xr_stat_summary_interval_duration", 5000));
}
if (currentconfig.voip_metrics_enabled == TRUE) {
rtp_session_set_rtcp_xr_voip_metrics_interval(session, lp_config_get_int(lc->config, "rtp", "rtcp_xr_voip_metrics_interval_duration", 5000));
}
}
static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cname, bool_t muted, bool_t send_ringbacktone, bool_t use_arc){
@ -1885,14 +1987,8 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna
int crypto_idx;
snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
/* look for savp stream first */
stream=sal_media_description_find_stream(call->resultdesc,
SalProtoRtpSavp,SalAudio);
/* no savp audio stream, use avp */
if (!stream)
stream=sal_media_description_find_stream(call->resultdesc,
SalProtoRtpAvp,SalAudio);
stream = sal_media_description_find_best_stream(call->resultdesc, SalAudio);
if (stream && stream->dir!=SalStreamInactive && stream->rtp_port!=0){
playcard=lc->sound_conf.lsd_card ?
lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
@ -1948,8 +2044,8 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna
call->current_params.record_file=ms_strdup(call->params.record_file);
}
/* valid local tags are > 0 */
if (stream->proto == SalProtoRtpSavp) {
local_st_desc=sal_media_description_find_stream(call->localdesc,SalProtoRtpSavp,SalAudio);
if (stream_description_has_srtp(stream) == TRUE) {
local_st_desc=sal_media_description_find_stream(call->localdesc,stream->proto,SalAudio);
crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag);
if (crypto_idx >= 0) {
@ -1960,6 +2056,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna
}
}
configure_rtp_session_for_rtcp_xr(lc, call, SalAudio);
audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool);
audio_stream_start_full(
call->audiostream,
call->audio_profile,
@ -1986,7 +2083,6 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna
if (send_ringbacktone){
setup_ring_player(lc,call);
}
audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool);
if (call->params.in_conference){
/*transform the graph to connect it to the conference filter */
@ -2003,17 +2099,10 @@ 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);
char rtcp_tool[128]={0};
snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
const SalStreamDescription *vstream;
/* no savp audio stream, use avp */
if (!vstream)
vstream=sal_media_description_find_stream(call->resultdesc,
SalProtoRtpAvp,SalVideo);
snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
/* shutdown preview */
if (lc->previewstream!=NULL) {
@ -2021,6 +2110,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna
lc->previewstream=NULL;
}
vstream = sal_media_description_find_best_stream(call->resultdesc, SalVideo);
if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->rtp_port!=0) {
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;
@ -2071,7 +2161,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna
cam=get_nowebcam_device();
}
if (!is_inactive){
if (vstream->proto == SalProtoRtpSavp) {
if (stream_description_has_srtp(vstream) == TRUE) {
int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, vstream->crypto_local_tag);
if (crypto_idx >= 0) {
media_stream_set_srtp_recv_key(&call->videostream->ms,vstream->crypto[0].algo,vstream->crypto[0].master_key);
@ -2084,12 +2174,12 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna
video_stream_set_direction (call->videostream, dir);
ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation);
video_stream_set_device_rotation(call->videostream, lc->device_rotation);
video_stream_set_rtcp_information(call->videostream, cname, rtcp_tool);
video_stream_start(call->videostream,
call->video_profile, rtp_addr, vstream->rtp_port,
rtcp_addr,
linphone_core_rtcp_enabled(lc) ? (vstream->rtcp_port ? vstream->rtcp_port : vstream->rtp_port+1) : 0,
used_pt, linphone_core_get_video_jittcomp(lc), cam);
video_stream_set_rtcp_information(call->videostream, cname,rtcp_tool);
}
}else ms_warning("No video stream accepted.");
}else{
@ -2104,8 +2194,7 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut
char *cname;
bool_t use_arc=linphone_core_adaptive_rate_control_enabled(lc);
#ifdef VIDEO_ENABLED
const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc,
SalProtoRtpAvp,SalVideo);
const SalStreamDescription *vstream=sal_media_description_find_best_stream(call->resultdesc,SalVideo);
#endif
call->current_params.audio_codec = NULL;
@ -2142,14 +2231,14 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut
params.zid_file=lc->zrtp_secrets_cache;
audio_stream_enable_zrtp(call->audiostream,&params);
#if VIDEO_ENABLED
if (media_stream_is_secured((MediaStream *)call->audiostream) && media_stream_get_state((MediaStream *)call->videostream) == MSStreamStarted) {
if (media_stream_secured((MediaStream *)call->audiostream) && media_stream_get_state((MediaStream *)call->videostream) == MSStreamStarted) {
/*audio stream is already encrypted and video stream is active*/
memset(&params,0,sizeof(OrtpZrtpParams));
video_stream_enable_zrtp(call->videostream,call->audiostream,&params);
}
#endif
}else{
call->current_params.media_encryption=linphone_call_are_all_streams_encrypted(call) ?
call->current_params.media_encryption=linphone_call_all_streams_encrypted(call) ?
LinphoneMediaEncryptionSRTP : LinphoneMediaEncryptionNone;
}
@ -2192,17 +2281,17 @@ void linphone_call_update_crypto_parameters(LinphoneCall *call, SalMediaDescript
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);
local_st_desc = sal_media_description_find_secure_stream_of_type(call->localdesc, SalAudio);
old_stream = sal_media_description_find_secure_stream_of_type(old_md, SalAudio);
new_stream = sal_media_description_find_secure_stream_of_type(new_md, SalAudio);
if (call->audiostream && local_st_desc && old_stream && new_stream &&
update_stream_crypto_params(call,local_st_desc,old_stream,new_stream,&call->audiostream->ms)){
}
#ifdef VIDEO_ENABLED
local_st_desc = sal_media_description_find_stream(call->localdesc, SalProtoRtpSavp, SalVideo);
old_stream = sal_media_description_find_stream(old_md, SalProtoRtpSavp, SalVideo);
new_stream = sal_media_description_find_stream(new_md, SalProtoRtpSavp, SalVideo);
local_st_desc = sal_media_description_find_secure_stream_of_type(call->localdesc, SalVideo);
old_stream = sal_media_description_find_secure_stream_of_type(old_md, SalVideo);
new_stream = sal_media_description_find_secure_stream_of_type(new_md, SalVideo);
if (call->videostream && local_st_desc && old_stream && new_stream &&
update_stream_crypto_params(call,local_st_desc,old_stream,new_stream,&call->videostream->ms)){
}
@ -2250,7 +2339,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);
linphone_reporting_update_media_info(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);
@ -2279,7 +2368,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);
linphone_reporting_update_media_info(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);
@ -2663,20 +2752,14 @@ static void report_bandwidth(LinphoneCall *call, MediaStream *as, MediaStream *v
}
static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){
char temp[256];
char temp[256]={0};
char *from=NULL;
if(call)
from = linphone_call_get_remote_address_as_string(call);
if (from)
{
snprintf(temp,sizeof(temp),"Remote end %s seems to have disconnected, the call is going to be closed.",from);
ms_free(from);
}
else
{
snprintf(temp,sizeof(temp),"Remote end seems to have disconnected, the call is going to be closed.");
}
ms_message("On call [%p] %s",call,temp);
from = linphone_call_get_remote_address_as_string(call);
snprintf(temp,sizeof(temp)-1,"Remote end %s seems to have disconnected, the call is going to be closed.",from ? from : "");
if (from) ms_free(from);
ms_message("On call [%p]: %s",call,temp);
if (lc->vtable.display_warning!=NULL)
lc->vtable.display_warning(lc,temp);
linphone_core_terminate_call(lc,call);
@ -2784,11 +2867,22 @@ void linphone_call_stats_fill(LinphoneCallStats *stats, MediaStream *ms, OrtpEve
}
}
void linphone_call_stats_uninit(LinphoneCallStats *stats){
if (stats->received_rtcp) {
freemsg(stats->received_rtcp);
stats->received_rtcp=NULL;
}
if (stats->sent_rtcp){
freemsg(stats->sent_rtcp);
stats->sent_rtcp=NULL;
}
}
void linphone_call_notify_stats_updated(LinphoneCall *call, int stream_index){
LinphoneCallStats *stats=&call->stats[stream_index];
LinphoneCore *lc=call->core;
if (stats->updated){
linphone_reporting_call_stats_updated(call, stream_index);
linphone_reporting_on_rtcp_received(call, stream_index);
if (lc->vtable.call_stats_updated)
lc->vtable.call_stats_updated(lc, call, stats);
stats->updated = 0;
@ -2918,8 +3012,8 @@ void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState stat
if (state != call->transfer_state) {
LinphoneCore* lc = call->core;
ms_message("Transfer state for call [%p] changed from [%s] to [%s]",call
,linphone_call_state_to_string(call->transfer_state)
,linphone_call_state_to_string(state));
,linphone_call_state_to_string(call->transfer_state)
,linphone_call_state_to_string(state));
call->transfer_state = state;
if (lc->vtable.transfer_state_changed)
lc->vtable.transfer_state_changed(lc, call, state);

View file

@ -754,6 +754,7 @@ static void sip_config_read(LinphoneCore *lc)
linphone_core_enable_keep_alive(lc, (lc->sip_conf.keepalive_period > 0));
sal_use_one_matching_codec_policy(lc->sal,lp_config_get_int(lc->config,"sip","only_one_codec",0));
sal_use_dates(lc->sal,lp_config_get_int(lc->config,"sip","put_date",0));
sal_enable_sip_update_method(lc->sal,lp_config_get_int(lc->config,"sip","sip_update",1));
}
static void rtp_config_read(LinphoneCore *lc)
@ -2038,6 +2039,7 @@ int linphone_core_get_sip_transports(LinphoneCore *lc, LCSipTransports *tr){
* A zero value means that the transport is not activated.
* If LC_SIP_TRANSPORT_RANDOM was passed to linphone_core_set_sip_transports(), the random port choosed by the system is returned.
* @ingroup network_parameters
* @param lc the LinphoneCore
* @param tr a LCSipTransports structure.
**/
void linphone_core_get_sip_transports_used(LinphoneCore *lc, LCSipTransports *tr){
@ -2827,6 +2829,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const
char *real_url=NULL;
LinphoneCall *call;
bool_t defer = FALSE;
LinphoneCallParams *cp = linphone_call_params_copy(params);
linphone_core_preempt_sound_resources(lc);
@ -2839,20 +2842,24 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const
real_url=linphone_address_as_string(addr);
proxy=linphone_core_lookup_known_proxy(lc,addr);
if (proxy!=NULL)
if (proxy!=NULL) {
from=linphone_proxy_config_get_identity(proxy);
cp->avpf_enabled = linphone_proxy_config_avpf_enabled(proxy);
cp->avpf_rr_interval = linphone_proxy_config_get_avpf_rr_interval(proxy);
}
/* if no proxy or no identity defined for this proxy, default to primary contact*/
if (from==NULL) from=linphone_core_get_primary_contact(lc);
parsed_url2=linphone_address_new(from);
call=linphone_call_new_outgoing(lc,parsed_url2,linphone_address_clone(addr),params,proxy);
call=linphone_call_new_outgoing(lc,parsed_url2,linphone_address_clone(addr),cp,proxy);
if(linphone_core_add_call(lc,call)!= 0)
{
ms_warning("we had a problem in adding the call into the invite ... weird");
linphone_call_unref(call);
linphone_call_params_destroy(cp);
return NULL;
}
@ -2897,6 +2904,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const
if (defer==FALSE) linphone_core_start_invite(lc,call,NULL);
if (real_url!=NULL) ms_free(real_url);
linphone_call_params_destroy(cp);
return call;
}
@ -2909,7 +2917,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const
*
* 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.
* The notified states are #LinphoneCallOutgoingInit , #LinphoneCallOutgoingProgress, #LinphoneCallOutgoingRinging and #LinphoneCallConnected.
**/
int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char *url)
{
@ -2968,21 +2976,8 @@ bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){
return FALSE;
}
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){
return FALSE;
}
}
return TRUE;
}
bool_t linphone_core_incompatible_security(LinphoneCore *lc, SalMediaDescription *md){
return linphone_core_is_media_encryption_mandatory(lc) && linphone_core_get_media_encryption(lc)==LinphoneMediaEncryptionSRTP && !linphone_core_media_description_has_srtp(md);
return linphone_core_is_media_encryption_mandatory(lc) && linphone_core_get_media_encryption(lc)==LinphoneMediaEncryptionSRTP && !media_description_has_srtp(md);
}
void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){
@ -3152,26 +3147,37 @@ int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){
**/
int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){
int err=0;
#ifdef VIDEO_ENABLED
#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP)
bool_t has_video = FALSE;
#endif
switch(call->state){
case LinphoneCallIncomingEarlyMedia:
case LinphoneCallIncomingReceived:
case LinphoneCallStreamsRunning:
/*these states are allowed for linphone_core_update_call()*/
break;
default:
ms_error("linphone_core_update_call() is not allowed in [%s] state",linphone_call_state_to_string(call->state));
return -1;
}
if (params!=NULL){
linphone_call_set_state(call,LinphoneCallUpdating,"Updating call");
#ifdef VIDEO_ENABLED
#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP)
has_video = call->params.has_video;
// Video removing
if((call->videostream != NULL) && !params->has_video) {
#ifdef BUILD_UPNP
if(call->upnp_session != NULL) {
if (linphone_core_update_upnp(lc, call)<0) {
/* uPnP port mappings failed, proceed with the call anyway. */
linphone_call_delete_upnp_session(call);
}
}
#endif //BUILD_UPNP
}
#endif /* VIDEO_ENABLED */
#endif /* defined(VIDEO_ENABLED) && defined(BUILD_UPNP) */
_linphone_call_params_copy(&call->params,params);
err=linphone_call_prepare_ice(call,FALSE);
@ -3180,10 +3186,9 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho
return 0;
}
#ifdef VIDEO_ENABLED
#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP)
// Video adding
if (!has_video && call->params.has_video) {
#ifdef BUILD_UPNP
if(call->upnp_session != NULL) {
ms_message("Defer call update to add uPnP port mappings");
video_stream_prepare_video(call->videostream);
@ -3194,9 +3199,8 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho
return err;
}
}
#endif //BUILD_UPNP
}
#endif //VIDEO_ENABLED
#endif //defined(VIDEO_ENABLED) && defined(BUILD_UPNP)
err = linphone_core_start_update_call(lc, call);
}else{
#ifdef VIDEO_ENABLED
@ -3424,7 +3428,15 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call,
_linphone_call_params_copy(&call->params,params);
// There might not be a md if the INVITE was lacking an SDP
// In this case we use the parameters as is.
if (md) call->params.has_video &= linphone_core_media_description_contains_video_stream(md);
if (md) {
call->params.has_video &= linphone_core_media_description_contains_video_stream(md);
/* Handle AVPF and SRTP. */
call->params.avpf_enabled = media_description_has_avpf(md);
if ((media_description_has_srtp(md) == TRUE) && (media_stream_srtp_supported() == TRUE)) {
call->params.media_encryption = LinphoneMediaEncryptionSRTP;
}
}
linphone_call_prepare_ice(call,TRUE);
linphone_call_make_local_media_description(lc,call);
sal_call_set_local_media_description(call->op,call->localdesc);
sal_op_set_sent_custom_header(call->op,params->custom_headers);
@ -5605,6 +5617,7 @@ void linphone_core_set_rtp_transport_factories(LinphoneCore* lc, LinphoneRtpTran
/**
* Retrieve RTP statistics regarding current call.
* @param lc the LinphoneCore
* @param local RTP statistics computed locally.
* @param remote RTP statistics computed by far end (obtained via RTCP feedback).
*
@ -5666,7 +5679,7 @@ void sip_config_uninit(LinphoneCore *lc)
if (lc->network_reachable) {
for(elem=config->proxies;elem!=NULL;elem=ms_list_next(elem)){
LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)(elem->data);
linphone_proxy_config_edit(cfg); /* to unregister */
_linphone_proxy_config_unregister(cfg); /* to unregister without changing the stored flag enable_register */
}
ms_message("Unregistration started.");
@ -5740,6 +5753,7 @@ void rtp_config_uninit(LinphoneCore *lc)
lp_config_set_int(lc->config,"rtp","nortp_timeout",config->nortp_timeout);
lp_config_set_int(lc->config,"rtp","audio_adaptive_jitt_comp_enabled",config->audio_adaptive_jitt_comp_enabled);
lp_config_set_int(lc->config,"rtp","video_adaptive_jitt_comp_enabled",config->video_adaptive_jitt_comp_enabled);
ms_free(config->srtp_suites);
}
static void sound_config_uninit(LinphoneCore *lc)
@ -5882,6 +5896,15 @@ static void linphone_core_uninit(LinphoneCore *lc)
}
#endif //BUILD_UPNP
if (lc->chatrooms){
MSList *cr=ms_list_copy(lc->chatrooms);
MSList *elem;
for(elem=cr;elem!=NULL;elem=elem->next){
linphone_chat_room_destroy((LinphoneChatRoom*)elem->data);
}
ms_list_free(cr);
}
if (lp_config_needs_commit(lc->config)) lp_config_sync(lc->config);
lp_config_destroy(lc->config);
lc->config = NULL; /* Mark the config as NULL to block further calls */
@ -6432,6 +6455,7 @@ void linphone_core_init_default_params(LinphoneCore*lc, LinphoneCallParams *para
params->media_encryption=linphone_core_get_media_encryption(lc);
params->in_conference=FALSE;
params->privacy=LinphonePrivacyDefault;
params->avpf_enabled=FALSE;
}
void linphone_core_set_device_identifier(LinphoneCore *lc,const char* device_id) {

View file

@ -431,6 +431,13 @@ LINPHONE_PUBLIC MSVideoSize linphone_call_params_get_sent_video_size(const Linph
*/
LINPHONE_PUBLIC MSVideoSize linphone_call_params_get_received_video_size(const LinphoneCallParams *cp);
/**
* Gets the RTP profile being used.
* @param[in] cp #LinphoneCallParams object
* @returns The RTP profile.
*/
LINPHONE_PUBLIC const char * linphone_call_params_get_rtp_profile(const LinphoneCallParams *cp);
/*
* Note for developers: this enum must be kept synchronized with the SalPrivacy enum declared in sal.h
@ -814,7 +821,7 @@ LINPHONE_PUBLIC void linphone_proxy_config_enable_publish(LinphoneProxyConfig *o
/**
* Set the publish expiration time in second.
* @param obj proxy config
* @param exires in second
* @param expires in second
* */
LINPHONE_PUBLIC void linphone_proxy_config_set_publish_expires(LinphoneProxyConfig *obj, int expires);
@ -829,16 +836,54 @@ LINPHONE_PUBLIC int linphone_proxy_config_get_publish_expires(const LinphoneProx
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
*
/**
* Indicates whether quality statistics during call should be stored and sent to a collector according to RFC 6035.
* @param[in] cfg #LinphoneProxyConfig object
* @param[in] enable True to sotre quality statistics and sent them to the collector, false to disable it.
*/
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);
LINPHONE_PUBLIC void linphone_proxy_config_enable_quality_reporting(LinphoneProxyConfig *cfg, bool_t enable);
/**
* Indicates whether quality statistics during call should be stored and sent to a collector according to RFC 6035.
* @param[in] cfg #LinphoneProxyConfig object
* @return True if quality repotring is enabled, false otherwise.
*/
LINPHONE_PUBLIC bool_t linphone_proxy_config_quality_reporting_enabled(LinphoneProxyConfig *cfg);
/**
* Set the SIP address of the collector end-point when using quality reporting. This SIP address
* should be used on server-side to process packets directly then discard packets. Collector address
* should be a non existing account and should not received any packets.
* @param[in] cfg #LinphoneProxyConfig object
* @param[in] collector SIP address of the collector end-point.
*/
LINPHONE_PUBLIC void linphone_proxy_config_set_quality_reporting_collector(LinphoneProxyConfig *cfg, const char *collector);
/**
* Get the SIP address of the collector end-point when using quality reporting. This SIP address
* should be used on server-side to process packets directly then discard packets. Collector address
* should be a non existing account and should not received any packets.
* @param[in] cfg #LinphoneProxyConfig object
* @return The SIP address of the collector end-point.
*/
LINPHONE_PUBLIC const char *linphone_proxy_config_get_quality_reporting_collector(const LinphoneProxyConfig *cfg);
/**
* Set the interval between 2 interval reports sending when using quality reporting. If call exceed interval size, an
* interval report will be sent to the collector. On call termination, a session report will be sent
* for the remaining period. Value must be 0 (disabled) or positive.
* @param[in] cfg #LinphoneProxyConfig object
* @param[in] interval The interval in seconds, 0 means interval reports are disabled.
*/
void linphone_proxy_config_set_quality_reporting_interval(LinphoneProxyConfig *cfg, uint8_t interval);
/**
* Get the interval between interval reports when using quality reporting.
* @param[in] cfg #LinphoneProxyConfig object
* @return The interval in seconds, 0 means interval reports are disabled.
*/
int linphone_proxy_config_get_quality_reporting_interval(LinphoneProxyConfig *cfg);
/**
* Get the registration state of the given proxy config.
@ -921,7 +966,7 @@ LINPHONE_PUBLIC void * linphone_proxy_config_get_user_data(LinphoneProxyConfig *
/**
* Set default privacy policy for all calls routed through this proxy.
* @param params to be modified
* @param LinphonePrivacy to configure privacy
* @param privacy LinphonePrivacy to configure privacy
* */
LINPHONE_PUBLIC void linphone_proxy_config_set_privacy(LinphoneProxyConfig *params, LinphonePrivacyMask privacy);
/**
@ -943,6 +988,34 @@ LINPHONE_PUBLIC void linphone_proxy_config_set_file_transfert_server(LinphonePro
* */
LINPHONE_PUBLIC const char* linphone_proxy_config_get_file_transfer_server(const LinphoneProxyConfig *params);
/**
* Indicates whether AVPF/SAVPF must be used for calls using this proxy config.
* @param[in] cfg #LinphoneProxyConfig object
* @param[in] enable True to enable AVPF/SAVF, false to disable it.
*/
LINPHONE_PUBLIC void linphone_proxy_config_enable_avpf(LinphoneProxyConfig *cfg, bool_t enable);
/**
* Indicates whether AVPF/SAVPF is being used for calls using this proxy config.
* @param[in] cfg #LinphoneProxyConfig object
* @return True if AVPF/SAVPF is enabled, false otherwise.
*/
LINPHONE_PUBLIC bool_t linphone_proxy_config_avpf_enabled(LinphoneProxyConfig *cfg);
/**
* Set the interval between regular RTCP reports when using AVPF/SAVPF.
* @param[in] cfg #LinphoneProxyConfig object
* @param[in] interval The interval in seconds (between 0 and 5 seconds).
*/
LINPHONE_PUBLIC void linphone_proxy_config_set_avpf_rr_interval(LinphoneProxyConfig *cfg, uint8_t interval);
/**
* Get the interval between regular RTCP reports when using AVPF/SAVPF.
* @param[in] cfg #LinphoneProxyConfig object
* @return The interval in seconds.
*/
LINPHONE_PUBLIC uint8_t linphone_proxy_config_get_avpf_rr_interval(const LinphoneProxyConfig *cfg);
/**
* @}
**/
@ -1108,6 +1181,7 @@ LINPHONE_PUBLIC bool_t linphone_chat_room_is_remote_composing(const LinphoneChat
LINPHONE_PUBLIC int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr);
LINPHONE_PUBLIC LinphoneCore* linphone_chat_room_get_lc(LinphoneChatRoom *cr);
LINPHONE_PUBLIC LinphoneCore* linphone_chat_room_get_core(LinphoneChatRoom *cr);
LINPHONE_PUBLIC void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void * ud);
LINPHONE_PUBLIC void * linphone_chat_room_get_user_data(LinphoneChatRoom *cr);
LINPHONE_PUBLIC MSList* linphone_core_get_chat_rooms(LinphoneCore *lc);
@ -1127,7 +1201,9 @@ LINPHONE_PUBLIC const char* linphone_chat_message_get_external_body_url(const Li
LINPHONE_PUBLIC void linphone_chat_message_set_external_body_url(LinphoneChatMessage* message,const char* url);
LINPHONE_PUBLIC const LinphoneContent* linphone_chat_message_get_file_transfer_information(const LinphoneChatMessage* message);
LINPHONE_PUBLIC void linphone_chat_message_start_file_download(const LinphoneChatMessage*message);
LINPHONE_PUBLIC const char * linphone_chat_message_get_text(const LinphoneChatMessage* message);
LINPHONE_PUBLIC const char* linphone_chat_message_get_appdata(const LinphoneChatMessage* message);
LINPHONE_PUBLIC void linphone_chat_message_set_appdata(LinphoneChatMessage* message, const char* data);
LINPHONE_PUBLIC const char* linphone_chat_message_get_text(const LinphoneChatMessage* message);
LINPHONE_PUBLIC time_t linphone_chat_message_get_time(const LinphoneChatMessage* message);
LINPHONE_PUBLIC void* linphone_chat_message_get_user_data(const LinphoneChatMessage* message);
LINPHONE_PUBLIC void linphone_chat_message_set_user_data(LinphoneChatMessage* message,void*);
@ -1571,7 +1647,7 @@ LINPHONE_PUBLIC int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneC
/**
* @ingroup media_parameters
* Get default call parameters reflecting current linphone core configuration
* @param LinphoneCore object
* @param lc LinphoneCore object
* @return LinphoneCallParams
*/
LINPHONE_PUBLIC LinphoneCallParams *linphone_core_create_default_call_parameters(LinphoneCore *lc);
@ -1659,7 +1735,7 @@ LINPHONE_PUBLIC bool_t linphone_core_payload_type_is_vbr(LinphoneCore *lc, const
* @param[in] lc the #LinphoneCore object
* @param[in] pt the #PayloadType to modify.
* @param[in] bitrate the IP bitrate in kbit/s.
* @ingroup media_parameters
* @ingroup media_parameters
**/
LINPHONE_PUBLIC void linphone_core_set_payload_type_bitrate(LinphoneCore *lc, PayloadType *pt, int bitrate);
@ -1668,7 +1744,7 @@ LINPHONE_PUBLIC void linphone_core_set_payload_type_bitrate(LinphoneCore *lc, Pa
* @param[in] lc the #LinphoneCore object
* @param[in] pt the #PayloadType to modify.
* @return bitrate the IP bitrate in kbit/s, or -1 if an error occured.
* @ingroup media_parameters
* @ingroup media_parameters
**/
LINPHONE_PUBLIC int linphone_core_get_payload_type_bitrate(LinphoneCore *lc, const PayloadType *pt);
@ -2013,7 +2089,7 @@ LINPHONE_PUBLIC void linphone_core_mute_mic(LinphoneCore *lc, bool_t muted);
/**
* Get mic state.
* @deprecated Use #linphone_core_is_mic_enabled instead
* @deprecated Use #linphone_core_mic_enabled instead
**/
LINPHONE_PUBLIC bool_t linphone_core_is_mic_muted(LinphoneCore *lc);

View file

@ -121,7 +121,13 @@ static void linphone_android_ortp_log_handler(OrtpLogLevel lev, const char *fmt,
}
if (handler_obj){
JNIEnv *env=ms_get_jni_env();
env->CallVoidMethod(handler_obj,loghandler_id,env->NewStringUTF(LogDomain),(jint)lev,env->NewStringUTF(levname),env->NewStringUTF(str),NULL);
jstring jdomain=env->NewStringUTF(LogDomain);
jstring jlevname=env->NewStringUTF(levname);
jstring jstr=env->NewStringUTF(str);
env->CallVoidMethod(handler_obj,loghandler_id,jdomain,(jint)lev,jlevname,jstr,NULL);
if (jdomain) env->DeleteLocalRef(jdomain);
if (jlevname) env->DeleteLocalRef(jlevname);
if (jstr) env->DeleteLocalRef(jstr);
}else
linphone_android_log_handler(prio, str);
}
@ -2624,6 +2630,15 @@ static void chat_room_impl_callback(LinphoneChatMessage* msg, LinphoneChatMessag
linphone_chat_message_set_user_data(msg,NULL);
}
}
extern "C" jobject Java_org_linphone_core_LinphoneChatRoomImpl_getCore(JNIEnv* env
,jobject thiz
,jlong chatroom_ptr){
LinphoneCore *lc=linphone_chat_room_get_core((LinphoneChatRoom*)chatroom_ptr);
LinphoneCoreData *lcd=(LinphoneCoreData*)linphone_core_get_user_data(lc);
return lcd->core;
}
extern "C" void Java_org_linphone_core_LinphoneChatRoomImpl_sendMessage2(JNIEnv* env
,jobject thiz
,jlong chatroom_ptr
@ -2938,6 +2953,22 @@ extern "C" jint Java_org_linphone_core_LinphoneProxyConfigImpl_getPrivacy(JNIEnv
return linphone_proxy_config_get_privacy((LinphoneProxyConfig *) ptr);
}
JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_enableAvpf(JNIEnv *env, jobject thiz, jlong ptr, jboolean enable) {
linphone_proxy_config_enable_avpf((LinphoneProxyConfig *)ptr, (bool)enable);
}
JNIEXPORT jboolean JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_avpfEnabled(JNIEnv *env, jobject thiz, jlong ptr) {
return linphone_proxy_config_avpf_enabled((LinphoneProxyConfig *)ptr);
}
JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_setAvpfRRInterval(JNIEnv *env, jobject thiz, jlong ptr, jint interval) {
linphone_proxy_config_set_avpf_rr_interval((LinphoneProxyConfig *)ptr, (uint8_t)interval);
}
JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_getAvpfRRInterval(JNIEnv *env, jobject thiz, jlong ptr) {
return (jint)linphone_proxy_config_get_avpf_rr_interval((LinphoneProxyConfig *)ptr);
}
extern "C" jint Java_org_linphone_core_LinphoneCallImpl_getDuration(JNIEnv* env,jobject thiz,jlong ptr) {
return (jint)linphone_call_get_duration((LinphoneCall *) ptr);
}
@ -3647,6 +3678,12 @@ JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCoreFactoryImpl__1setLogHa
}
}
JNIEXPORT jobject JNICALL Java_org_linphone_core_LinphoneEventImpl_getCore(JNIEnv *env, jobject jobj, jlong evptr){
LinphoneCore *lc=linphone_event_get_core((LinphoneEvent*)evptr);
LinphoneCoreData *lcd=(LinphoneCoreData*)linphone_core_get_user_data(lc);
return lcd->core;
}
/*
* Class: org_linphone_core_LinphoneEventImpl
* Method: getEventName

View file

@ -426,7 +426,7 @@ LINPHONE_PUBLIC int linphone_presence_model_clear_persons(LinphonePresenceModel
*
* The created presence service has the basic status 'closed'.
*/
LINPHONE_PUBLIC LinphonePresenceService * linphone_presence_service_new(const char *id, LinphonePresenceBasicStatus, const char *contact);
LINPHONE_PUBLIC LinphonePresenceService * linphone_presence_service_new(const char *id, LinphonePresenceBasicStatus basic_status, const char *contact);
/**
* Gets the id of a presence service.

View file

@ -49,6 +49,7 @@
typedef struct _LpItem{
char *key;
char *value;
int is_comment;
} LpItem;
typedef struct _LpSectionParam{
@ -78,6 +79,13 @@ LpItem * lp_item_new(const char *key, const char *value){
return item;
}
LpItem * lp_comment_new(const char *comment){
LpItem *item=lp_new0(LpItem,1);
item->value=ortp_strdup(comment);
item->is_comment=TRUE;
return item;
}
LpSectionParam *lp_section_param_new(const char *key, const char *value){
LpSectionParam *param = lp_new0(LpSectionParam, 1);
param->key = ortp_strdup(key);
@ -93,7 +101,7 @@ LpSection *lp_section_new(const char *name){
void lp_item_destroy(void *pitem){
LpItem *item=(LpItem*)pitem;
ortp_free(item->key);
if (item->key) ortp_free(item->key);
ortp_free(item->value);
free(item);
}
@ -138,6 +146,14 @@ static bool_t is_first_char(const char *start, const char *pos){
return TRUE;
}
static int is_a_comment(const char *str){
while (*str==' '){
str++;
}
if (*str=='#') return 1;
return 0;
}
LpSection *lp_config_find_section(const LpConfig *lpconfig, const char *name){
LpSection *sec;
MSList *elem;
@ -170,7 +186,7 @@ LpItem *lp_section_find_item(const LpSection *sec, const char *name){
/*printf("Looking for item %s\n",name);*/
for (elem=sec->items;elem!=NULL;elem=ms_list_next(elem)){
item=(LpItem*)elem->data;
if (strcmp(item->key,name)==0) {
if (!item->is_comment && strcmp(item->key,name)==0) {
/*printf("Item %s found\n",name);*/
return item;
}
@ -182,9 +198,10 @@ static LpSection* lp_config_parse_line(LpConfig* lpconfig, const char* line, LpS
LpSectionParam *params = NULL;
char *pos1,*pos2;
int nbs;
static char secname[MAX_LEN];
static char key[MAX_LEN];
static char value[MAX_LEN];
int size=strlen(line)+1;
char *secname=ms_malloc(size);
char *key=ms_malloc(size);
char *value=ms_malloc(size);
LpItem *item;
pos1=strchr(line,'[');
@ -230,43 +247,53 @@ static LpSection* lp_config_parse_line(LpConfig* lpconfig, const char* line, LpS
}
}
}else {
pos1=strchr(line,'=');
if (pos1!=NULL){
key[0]='\0';
if (is_a_comment(line)){
if (cur){
LpItem *comment=lp_comment_new(line);
lp_section_add_item(cur,comment);
}
}else{
pos1=strchr(line,'=');
if (pos1!=NULL){
key[0]='\0';
*pos1='\0';
if (sscanf(line,"%s",key)>0){
*pos1='\0';
if (sscanf(line,"%s",key)>0){
pos1++;
pos2=strchr(pos1,'\r');
if (pos2==NULL)
pos2=strchr(pos1,'\n');
if (pos2==NULL) pos2=pos1+strlen(pos1);
else {
*pos2='\0'; /*replace the '\n' */
}
/* remove ending white spaces */
for (; pos2>pos1 && pos2[-1]==' ';pos2--) pos2[-1]='\0';
pos1++;
pos2=strchr(pos1,'\r');
if (pos2==NULL)
pos2=strchr(pos1,'\n');
if (pos2==NULL) pos2=pos1+strlen(pos1);
else {
*pos2='\0'; /*replace the '\n' */
}
/* remove ending white spaces */
for (; pos2>pos1 && pos2[-1]==' ';pos2--) pos2[-1]='\0';
if (pos2-pos1>=0){
/* found a pair key,value */
if (pos2-pos1>=0){
/* found a pair key,value */
if (cur!=NULL){
item=lp_section_find_item(cur,key);
if (item==NULL){
lp_section_add_item(cur,lp_item_new(key,pos1));
if (cur!=NULL){
item=lp_section_find_item(cur,key);
if (item==NULL){
lp_section_add_item(cur,lp_item_new(key,pos1));
}else{
ortp_free(item->value);
item->value=ortp_strdup(pos1);
}
/*ms_message("Found %s=%s",key,pos1);*/
}else{
ortp_free(item->value);
item->value=ortp_strdup(pos1);
ms_warning("found key,item but no sections");
}
/*ms_message("Found %s=%s",key,pos1);*/
}else{
ms_warning("found key,item but no sections");
}
}
}
}
}
ms_free(key);
ms_free(value);
ms_free(secname);
return cur;
}
@ -425,13 +452,16 @@ bool_t lp_config_get_range(const LpConfig *lpconfig, const char *section, const
}
}
int lp_config_get_int(const LpConfig *lpconfig,const char *section, const char *key, int default_value){
const char *str=lp_config_get_string(lpconfig,section,key,NULL);
if (str!=NULL) {
int ret=0;
if (strstr(str,"0x")==str){
sscanf(str,"%x",&ret);
}else ret=atoi(str);
}else
sscanf(str,"%i",&ret);
return ret;
}
else return default_value;
@ -510,7 +540,10 @@ void lp_config_set_float(LpConfig *lpconfig,const char *section, const char *key
}
void lp_item_write(LpItem *item, FILE *file){
fprintf(file,"%s=%s\n",item->key,item->value);
if (item->is_comment)
fprintf(file,"%s",item->value);
else
fprintf(file,"%s=%s\n",item->key,item->value);
}
void lp_section_param_write(LpSectionParam *param, FILE *file){
@ -566,7 +599,8 @@ void lp_config_for_each_entry(const LpConfig *lpconfig, const char *section, voi
if (sec!=NULL){
for (elem=sec->items;elem!=NULL;elem=ms_list_next(elem)){
item=(LpItem*)elem->data;
callback(item->key, ctx);
if (!item->is_comment)
callback(item->key, ctx);
}
}
}

View file

@ -1,370 +1,502 @@
/*
message_storage.c
Copyright (C) 2012 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.
*/
#include "private.h"
#include "linphonecore.h"
#ifdef WIN32
static inline char *my_ctime_r(const time_t *t, char *buf){
strcpy(buf,ctime(t));
return buf;
}
#else
#define my_ctime_r ctime_r
#endif
#ifdef MSG_STORAGE_ENABLED
#include "sqlite3.h"
static const char *days[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
static const char *months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
static inline LinphoneChatMessage* get_transient_message(LinphoneChatRoom* cr, unsigned int storage_id){
MSList* transients = cr->transient_messages;
LinphoneChatMessage* chat;
while( transients ){
chat = (LinphoneChatMessage*)transients->data;
if(chat->storage_id == storage_id){
return linphone_chat_message_ref(chat);
}
transients = transients->next;
}
return NULL;
}
static void create_chat_message(char **argv, void *data){
LinphoneChatRoom *cr = (LinphoneChatRoom *)data;
LinphoneAddress *from;
struct tm ret={0};
char tmp1[80]={0};
char tmp2[80]={0};
unsigned int storage_id = atoi(argv[0]);
// check if the message exists in the transient list, in which case we should return that one.
LinphoneChatMessage* new_message = get_transient_message(cr, storage_id);
if( new_message == NULL ){
new_message = linphone_chat_room_create_message(cr, argv[4]);
if(atoi(argv[3])==LinphoneChatMessageIncoming){
new_message->dir=LinphoneChatMessageIncoming;
from=linphone_address_new(argv[2]);
} else {
new_message->dir=LinphoneChatMessageOutgoing;
from=linphone_address_new(argv[1]);
}
linphone_chat_message_set_from(new_message,from);
linphone_address_destroy(from);
if(argv[5]!=NULL){
int i,j;
sscanf(argv[5],"%3c %3c%d%d:%d:%d %d",tmp1,tmp2,&ret.tm_mday,
&ret.tm_hour,&ret.tm_min,&ret.tm_sec,&ret.tm_year);
ret.tm_year-=1900;
for(i=0;i<7;i++) {
if(strcmp(tmp1,days[i])==0) ret.tm_wday=i;
}
for(j=0;j<12;j++) {
if(strcmp(tmp2,months[j])==0) ret.tm_mon=j;
}
ret.tm_isdst=-1;
}
new_message->time=argv[5]!=NULL ? mktime(&ret) : time(NULL);
new_message->is_read=atoi(argv[6]);
new_message->state=atoi(argv[7]);
new_message->storage_id=storage_id;
new_message->external_body_url=argv[8]?ms_strdup(argv[8]):NULL;
}
cr->messages_hist=ms_list_prepend(cr->messages_hist,new_message);
}
// Called when fetching all conversations from database
static int callback_all(void *data, int argc, char **argv, char **colName){
LinphoneCore* lc = (LinphoneCore*) data;
char* address = argv[0];
linphone_core_create_chat_room(lc, address);
return 0;
}
static int callback(void *data, int argc, char **argv, char **colName){
create_chat_message(argv,data);
return 0;
}
void linphone_sql_request_message(sqlite3 *db,const char *stmt,LinphoneChatRoom *cr){
char* errmsg=NULL;
int ret;
ret=sqlite3_exec(db,stmt,callback,cr,&errmsg);
if(ret != SQLITE_OK) {
ms_error("Error in creation: %s.\n", errmsg);
sqlite3_free(errmsg);
}
}
void linphone_sql_request(sqlite3* db,const char *stmt){
char* errmsg=NULL;
int ret;
ret=sqlite3_exec(db,stmt,NULL,NULL,&errmsg);
if(ret != SQLITE_OK) {
ms_error("linphone_sql_request: error sqlite3_exec(): %s.\n", errmsg);
sqlite3_free(errmsg);
}
}
// Process the request to fetch all chat contacts
void linphone_sql_request_all(sqlite3* db,const char *stmt, LinphoneCore* lc){
char* errmsg=NULL;
int ret;
ret=sqlite3_exec(db,stmt,callback_all,lc,&errmsg);
if(ret != SQLITE_OK) {
ms_error("linphone_sql_request_all: error sqlite3_exec(): %s.\n", errmsg);
sqlite3_free(errmsg);
}
}
unsigned int linphone_chat_message_store(LinphoneChatMessage *msg){
LinphoneCore *lc=linphone_chat_room_get_lc(msg->chat_room);
int id=0;
if (lc->db){
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(msg->chat_room));
char *local_contact=linphone_address_as_string_uri_only(linphone_chat_message_get_local_address(msg));
char datebuf[26];
char *buf=sqlite3_mprintf("insert into history values(NULL,%Q,%Q,%i,%Q,%Q,%i,%i,%Q);",
local_contact,peer,msg->dir,msg->message,my_ctime_r(&msg->time,datebuf),msg->is_read,msg->state,msg->external_body_url);
linphone_sql_request(lc->db,buf);
sqlite3_free(buf);
ms_free(local_contact);
ms_free(peer);
id = (unsigned int) sqlite3_last_insert_rowid (lc->db);
}
return id;
}
void linphone_chat_message_store_state(LinphoneChatMessage *msg){
LinphoneCore *lc=msg->chat_room->lc;
if (lc->db){
char time_str[26];
char *buf=sqlite3_mprintf("update history set status=%i where message = %Q and time = %Q;",
msg->state,msg->message,my_ctime_r(&msg->time,time_str));
linphone_sql_request(lc->db,buf);
sqlite3_free(buf);
}
if( msg->state == LinphoneChatMessageStateDelivered
|| msg->state == LinphoneChatMessageStateNotDelivered ){
// message is not transient anymore, we can remove it from our transient list:
msg->chat_room->transient_messages = ms_list_remove(msg->chat_room->transient_messages, msg);
linphone_chat_message_unref(msg);
}
}
void linphone_chat_room_mark_as_read(LinphoneChatRoom *cr){
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
int read=1;
if (lc->db==NULL) return ;
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
char *buf=sqlite3_mprintf("update history set read=%i where remoteContact = %Q;",
read,peer);
linphone_sql_request(lc->db,buf);
sqlite3_free(buf);
ms_free(peer);
}
void linphone_chat_room_update_url(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
if (lc->db==NULL) return ;
char *buf=sqlite3_mprintf("update history set url=%Q where id=%i;",msg->external_body_url,msg->storage_id);
linphone_sql_request(lc->db,buf);
sqlite3_free(buf);
}
int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr){
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
int numrows=0;
if (lc->db==NULL) return 0;
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
char *buf=sqlite3_mprintf("select count(*) from history where remoteContact = %Q and read = 0;",peer);
sqlite3_stmt *selectStatement;
int returnValue = sqlite3_prepare_v2(lc->db,buf,-1,&selectStatement,NULL);
if (returnValue == SQLITE_OK){
if(sqlite3_step(selectStatement) == SQLITE_ROW){
numrows= sqlite3_column_int(selectStatement, 0);
}
}
sqlite3_finalize(selectStatement);
sqlite3_free(buf);
ms_free(peer);
return numrows;
}
void linphone_chat_room_delete_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
LinphoneCore *lc=cr->lc;
if (lc->db==NULL) return ;
char *buf=sqlite3_mprintf("delete from history where id = %i;", msg->storage_id);
linphone_sql_request(lc->db,buf);
sqlite3_free(buf);
}
void linphone_chat_room_delete_history(LinphoneChatRoom *cr){
LinphoneCore *lc=cr->lc;
if (lc->db==NULL) return ;
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
char *buf=sqlite3_mprintf("delete from history where remoteContact = %Q;",peer);
linphone_sql_request(lc->db,buf);
sqlite3_free(buf);
ms_free(peer);
}
MSList *linphone_chat_room_get_history(LinphoneChatRoom *cr,int nb_message){
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
MSList *ret;
char *buf;
char *peer;
if (lc->db==NULL) return NULL;
peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
cr->messages_hist = NULL;
if (nb_message > 0)
buf=sqlite3_mprintf("select * from history where remoteContact = %Q order by id DESC limit %i ;",peer,nb_message);
else
buf=sqlite3_mprintf("select * from history where remoteContact = %Q order by id DESC;",peer);
linphone_sql_request_message(lc->db,buf,cr);
sqlite3_free(buf);
ret=cr->messages_hist;
cr->messages_hist=NULL;
ms_free(peer);
return ret;
}
void linphone_close_storage(sqlite3* db){
sqlite3_close(db);
}
void linphone_create_table(sqlite3* db){
char* errmsg=NULL;
int ret;
ret=sqlite3_exec(db,"CREATE TABLE if not exists history (id INTEGER PRIMARY KEY AUTOINCREMENT, localContact TEXT NOT NULL, remoteContact TEXT NOT NULL, direction INTEGER, message TEXT, time TEXT NOT NULL, read INTEGER, status INTEGER);",
0,0,&errmsg);
if(ret != SQLITE_OK) {
ms_error("Error in creation: %s.\n", errmsg);
sqlite3_free(errmsg);
}
}
void linphone_update_table(sqlite3* db) {
char* errmsg=NULL;
int ret;
ret=sqlite3_exec(db,"ALTER TABLE history ADD COLUMN url TEXT;",NULL,NULL,&errmsg);
if(ret != SQLITE_OK) {
ms_warning("Table already up to date: %s.\n", errmsg);
sqlite3_free(errmsg);
} else {
ms_debug("Table updated successfuly.");
}
}
void linphone_message_storage_init_chat_rooms(LinphoneCore *lc) {
char *buf;
if (lc->db==NULL) return;
buf=sqlite3_mprintf("SELECT remoteContact FROM history Group By remoteContact;");
linphone_sql_request_all(lc->db,buf,lc);
sqlite3_free(buf);
}
void linphone_core_message_storage_init(LinphoneCore *lc){
int ret;
const char *errmsg;
sqlite3 *db;
ret=sqlite3_open(lc->chat_db_file,&db);
if(ret != SQLITE_OK) {
errmsg=sqlite3_errmsg(db);
ms_error("Error in the opening: %s.\n", errmsg);
sqlite3_close(db);
}
linphone_create_table(db);
linphone_update_table(db);
lc->db=db;
// Create a chatroom for each contact in the chat history
linphone_message_storage_init_chat_rooms(lc);
}
void linphone_core_message_storage_close(LinphoneCore *lc){
if (lc->db){
sqlite3_close(lc->db);
lc->db=NULL;
}
}
#else
unsigned int linphone_chat_message_store(LinphoneChatMessage *cr){
return 0;
}
void linphone_chat_message_store_state(LinphoneChatMessage *cr){
}
void linphone_chat_room_mark_as_read(LinphoneChatRoom *cr){
}
MSList *linphone_chat_room_get_history(LinphoneChatRoom *cr,int nb_message){
return NULL;
}
void linphone_chat_room_delete_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
}
void linphone_chat_room_delete_history(LinphoneChatRoom *cr){
}
void linphone_message_storage_init_chat_rooms(LinphoneCore *lc) {
}
void linphone_core_message_storage_init(LinphoneCore *lc){
}
void linphone_core_message_storage_close(LinphoneCore *lc){
}
void linphone_chat_room_update_url(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
}
int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr){
return 0;
}
#endif
/*
message_storage.c
Copyright (C) 2012 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.
*/
#include "private.h"
#include "linphonecore.h"
#ifdef MSG_STORAGE_ENABLED
#include "sqlite3.h"
static inline LinphoneChatMessage* get_transient_message(LinphoneChatRoom* cr, unsigned int storage_id){
MSList* transients = cr->transient_messages;
LinphoneChatMessage* chat;
while( transients ){
chat = (LinphoneChatMessage*)transients->data;
if(chat->storage_id == storage_id){
return linphone_chat_message_ref(chat);
}
transients = transients->next;
}
return NULL;
}
/* DB layout:
* | 0 | storage_id
* | 1 | localContact
* | 2 | remoteContact
* | 3 | direction flag
* | 4 | message
* | 5 | time (unused now, used to be string-based timestamp)
* | 6 | read flag
* | 7 | status
* | 8 | external body url
* | 9 | utc timestamp
* | 10 | app data text
*/
static void create_chat_message(char **argv, void *data){
LinphoneChatRoom *cr = (LinphoneChatRoom *)data;
LinphoneAddress *from;
unsigned int storage_id = atoi(argv[0]);
// check if the message exists in the transient list, in which case we should return that one.
LinphoneChatMessage* new_message = get_transient_message(cr, storage_id);
if( new_message == NULL ){
new_message = linphone_chat_room_create_message(cr, argv[4]);
if(atoi(argv[3])==LinphoneChatMessageIncoming){
new_message->dir=LinphoneChatMessageIncoming;
from=linphone_address_new(argv[2]);
} else {
new_message->dir=LinphoneChatMessageOutgoing;
from=linphone_address_new(argv[1]);
}
linphone_chat_message_set_from(new_message,from);
linphone_address_destroy(from);
if( argv[9] != NULL ){
new_message->time = (time_t)atol(argv[9]);
} else {
new_message->time = time(NULL);
}
new_message->is_read=atoi(argv[6]);
new_message->state=atoi(argv[7]);
new_message->storage_id=storage_id;
new_message->external_body_url= argv[8] ? ms_strdup(argv[8]) : NULL;
new_message->appdata = argv[10]? ms_strdup(argv[10]) : NULL;
}
cr->messages_hist=ms_list_prepend(cr->messages_hist,new_message);
}
// Called when fetching all conversations from database
static int callback_all(void *data, int argc, char **argv, char **colName){
LinphoneCore* lc = (LinphoneCore*) data;
char* address = argv[0];
linphone_core_get_or_create_chat_room(lc, address);
return 0;
}
static int callback(void *data, int argc, char **argv, char **colName){
create_chat_message(argv,data);
return 0;
}
void linphone_sql_request_message(sqlite3 *db,const char *stmt,LinphoneChatRoom *cr){
char* errmsg=NULL;
int ret;
ret=sqlite3_exec(db,stmt,callback,cr,&errmsg);
if(ret != SQLITE_OK) {
ms_error("Error in creation: %s.\n", errmsg);
sqlite3_free(errmsg);
}
}
int linphone_sql_request(sqlite3* db,const char *stmt){
char* errmsg=NULL;
int ret;
ret=sqlite3_exec(db,stmt,NULL,NULL,&errmsg);
if(ret != SQLITE_OK) {
ms_error("linphone_sql_request: error sqlite3_exec(): %s.\n", errmsg);
sqlite3_free(errmsg);
}
return ret;
}
// Process the request to fetch all chat contacts
void linphone_sql_request_all(sqlite3* db,const char *stmt, LinphoneCore* lc){
char* errmsg=NULL;
int ret;
ret=sqlite3_exec(db,stmt,callback_all,lc,&errmsg);
if(ret != SQLITE_OK) {
ms_error("linphone_sql_request_all: error sqlite3_exec(): %s.\n", errmsg);
sqlite3_free(errmsg);
}
}
unsigned int linphone_chat_message_store(LinphoneChatMessage *msg){
LinphoneCore *lc=linphone_chat_room_get_lc(msg->chat_room);
int id=0;
if (lc->db){
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(msg->chat_room));
char *local_contact=linphone_address_as_string_uri_only(linphone_chat_message_get_local_address(msg));
char *buf=sqlite3_mprintf("INSERT INTO history VALUES(NULL,%Q,%Q,%i,%Q,%Q,%i,%i,%Q,%i,%Q);",
local_contact,
peer,
msg->dir,
msg->message,
"-1", /* use UTC field now */
msg->is_read,
msg->state,
msg->external_body_url,
msg->time,
msg->appdata);
linphone_sql_request(lc->db,buf);
sqlite3_free(buf);
ms_free(local_contact);
ms_free(peer);
id = (unsigned int) sqlite3_last_insert_rowid (lc->db);
}
return id;
}
void linphone_chat_message_store_state(LinphoneChatMessage *msg){
LinphoneCore *lc=msg->chat_room->lc;
if (lc->db){
char *buf=sqlite3_mprintf("UPDATE history SET status=%i WHERE (message = %Q OR url = %Q) AND utc = %i;",
msg->state,msg->message,msg->external_body_url,msg->time);
linphone_sql_request(lc->db,buf);
sqlite3_free(buf);
}
if( msg->state == LinphoneChatMessageStateDelivered
|| msg->state == LinphoneChatMessageStateNotDelivered ){
// message is not transient anymore, we can remove it from our transient list:
msg->chat_room->transient_messages = ms_list_remove(msg->chat_room->transient_messages, msg);
linphone_chat_message_unref(msg);
}
}
void linphone_chat_message_store_appdata(LinphoneChatMessage* msg){
LinphoneCore *lc=msg->chat_room->lc;
if (lc->db){
char *buf=sqlite3_mprintf("UPDATE history SET appdata=%Q WHERE id=%i;",
msg->appdata,msg->storage_id);
linphone_sql_request(lc->db,buf);
sqlite3_free(buf);
}
}
void linphone_chat_room_mark_as_read(LinphoneChatRoom *cr){
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
int read=1;
if (lc->db==NULL) return ;
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
char *buf=sqlite3_mprintf("UPDATE history SET read=%i WHERE remoteContact = %Q;",
read,peer);
linphone_sql_request(lc->db,buf);
sqlite3_free(buf);
ms_free(peer);
}
void linphone_chat_room_update_url(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
if (lc->db==NULL) return ;
char *buf=sqlite3_mprintf("UPDATE history SET url=%Q WHERE id=%i;",msg->external_body_url,msg->storage_id);
linphone_sql_request(lc->db,buf);
sqlite3_free(buf);
}
int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr){
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
int numrows=0;
if (lc->db==NULL) return 0;
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
char *buf=sqlite3_mprintf("SELECT count(*) FROM history WHERE remoteContact = %Q AND read = 0;",peer);
sqlite3_stmt *selectStatement;
int returnValue = sqlite3_prepare_v2(lc->db,buf,-1,&selectStatement,NULL);
if (returnValue == SQLITE_OK){
if(sqlite3_step(selectStatement) == SQLITE_ROW){
numrows= sqlite3_column_int(selectStatement, 0);
}
}
sqlite3_finalize(selectStatement);
sqlite3_free(buf);
ms_free(peer);
return numrows;
}
void linphone_chat_room_delete_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
LinphoneCore *lc=cr->lc;
if (lc->db==NULL) return ;
char *buf=sqlite3_mprintf("DELETE FROM history WHERE id = %i;", msg->storage_id);
linphone_sql_request(lc->db,buf);
sqlite3_free(buf);
}
void linphone_chat_room_delete_history(LinphoneChatRoom *cr){
LinphoneCore *lc=cr->lc;
if (lc->db==NULL) return ;
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
char *buf=sqlite3_mprintf("DELETE FROM history WHERE remoteContact = %Q;",peer);
linphone_sql_request(lc->db,buf);
sqlite3_free(buf);
ms_free(peer);
}
MSList *linphone_chat_room_get_history(LinphoneChatRoom *cr,int nb_message){
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
MSList *ret;
char *buf;
char *peer;
uint64_t begin,end;
if (lc->db==NULL) return NULL;
peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
cr->messages_hist = NULL;
if (nb_message > 0)
buf=sqlite3_mprintf("SELECT * FROM history WHERE remoteContact = %Q ORDER BY id DESC LIMIT %i ;",peer,nb_message);
else
buf=sqlite3_mprintf("SELECT * FROM history WHERE remoteContact = %Q ORDER BY id DESC;",peer);
begin=ortp_get_cur_time_ms();
linphone_sql_request_message(lc->db,buf,cr);
end=ortp_get_cur_time_ms();
ms_message("linphone_chat_room_get_history(): completed in %i ms",(int)(end-begin));
sqlite3_free(buf);
ret=cr->messages_hist;
cr->messages_hist=NULL;
ms_free(peer);
return ret;
}
void linphone_close_storage(sqlite3* db){
sqlite3_close(db);
}
void linphone_create_table(sqlite3* db){
char* errmsg=NULL;
int ret;
ret=sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS history ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"localContact TEXT NOT NULL,"
"remoteContact TEXT NOT NULL,"
"direction INTEGER,"
"message TEXT,"
"time TEXT NOT NULL,"
"read INTEGER,"
"status INTEGER"
");",
0,0,&errmsg);
if(ret != SQLITE_OK) {
ms_error("Error in creation: %s.\n", errmsg);
sqlite3_free(errmsg);
}
}
static const char *days[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
static const char *months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
static time_t parse_time_from_db( const char* time ){
/* messages used to be stored in the DB by using string-based time */
struct tm ret={0};
char tmp1[80]={0};
char tmp2[80]={0};
int i,j;
time_t parsed = 0;
if( sscanf(time,"%3c %3c%d%d:%d:%d %d",tmp1,tmp2,&ret.tm_mday,
&ret.tm_hour,&ret.tm_min,&ret.tm_sec,&ret.tm_year) == 7 ){
ret.tm_year-=1900;
for(i=0;i<7;i++) {
if(strcmp(tmp1,days[i])==0) ret.tm_wday=i;
}
for(j=0;j<12;j++) {
if(strcmp(tmp2,months[j])==0) ret.tm_mon=j;
}
ret.tm_isdst=-1;
parsed = mktime(&ret);
}
return parsed;
}
static int migrate_messages_timestamp(void* data,int argc, char** argv, char** column_names) {
time_t new_time = parse_time_from_db(argv[1]);
if( new_time ){
/* replace 'time' by -1 and set 'utc' to the timestamp */
char *buf = sqlite3_mprintf("UPDATE history SET utc=%i,time='-1' WHERE id=%i;", new_time, atoi(argv[0]));
if( buf) {
linphone_sql_request((sqlite3*)data, buf);
sqlite3_free(buf);
}
} else {
ms_warning("Cannot parse time %s from id %s", argv[1], argv[0]);
}
return 0;
}
static void linphone_migrate_timestamps(sqlite3* db){
int ret;
char* errmsg = NULL;
uint64_t begin=ortp_get_cur_time_ms();
linphone_sql_request(db,"BEGIN TRANSACTION");
ret = sqlite3_exec(db,"SELECT id,time,direction FROM history WHERE time != '-1';", migrate_messages_timestamp, db, &errmsg);
if( ret != SQLITE_OK ){
ms_warning("Error migrating outgoing messages: %s.\n", errmsg);
sqlite3_free(errmsg);
linphone_sql_request(db, "ROLLBACK");
} else {
linphone_sql_request(db, "COMMIT");
uint64_t end=ortp_get_cur_time_ms();
ms_message("Migrated message timestamps to UTC in %i ms",(int)(end-begin));
}
}
void linphone_update_table(sqlite3* db) {
char* errmsg=NULL;
int ret;
// for image url storage
ret=sqlite3_exec(db,"ALTER TABLE history ADD COLUMN url TEXT;",NULL,NULL,&errmsg);
if(ret != SQLITE_OK) {
ms_message("Table already up to date: %s.", errmsg);
sqlite3_free(errmsg);
} else {
ms_debug("Table updated successfully for URL.");
}
// for UTC timestamp storage
ret = sqlite3_exec(db, "ALTER TABLE history ADD COLUMN utc INTEGER;", NULL,NULL,&errmsg);
if( ret != SQLITE_OK ){
ms_message("Table already up to date: %s.", errmsg);
sqlite3_free(errmsg);
} else {
ms_debug("Table updated successfully for UTC.");
// migrate from old text-based timestamps to unix time-based timestamps
linphone_migrate_timestamps(db);
}
// new field for app-specific storage
ret=sqlite3_exec(db,"ALTER TABLE history ADD COLUMN appdata TEXT;",NULL,NULL,&errmsg);
if(ret != SQLITE_OK) {
ms_message("Table already up to date: %s.", errmsg);
sqlite3_free(errmsg);
} else {
ms_debug("Table updated successfully for app-specific data.");
}
}
void linphone_message_storage_init_chat_rooms(LinphoneCore *lc) {
char *buf;
if (lc->db==NULL) return;
buf=sqlite3_mprintf("SELECT remoteContact FROM history GROUP BY remoteContact;");
linphone_sql_request_all(lc->db,buf,lc);
sqlite3_free(buf);
}
static void _linphone_message_storage_profile(void*data,const char*statement, sqlite3_uint64 duration){
ms_warning("SQL statement '%s' took %" PRIu64 " microseconds", statement, (uint64_t)(duration / 1000LL) );
}
static void linphone_message_storage_activate_debug(sqlite3* db, bool_t debug){
if( debug ){
sqlite3_profile(db, _linphone_message_storage_profile, NULL );
} else {
sqlite3_profile(db, NULL, NULL );
}
}
void linphone_core_message_storage_set_debug(LinphoneCore *lc, bool_t debug){
lc->debug_storage = debug;
if( lc->db ){
linphone_message_storage_activate_debug(lc->db, debug);
}
}
void linphone_core_message_storage_init(LinphoneCore *lc){
int ret;
const char *errmsg;
sqlite3 *db;
linphone_core_message_storage_close(lc);
ret=sqlite3_open(lc->chat_db_file,&db);
if(ret != SQLITE_OK) {
errmsg=sqlite3_errmsg(db);
ms_error("Error in the opening: %s.\n", errmsg);
sqlite3_close(db);
}
linphone_message_storage_activate_debug(db, lc->debug_storage);
linphone_create_table(db);
linphone_update_table(db);
lc->db=db;
// Create a chatroom for each contact in the chat history
linphone_message_storage_init_chat_rooms(lc);
}
void linphone_core_message_storage_close(LinphoneCore *lc){
if (lc->db){
sqlite3_close(lc->db);
lc->db=NULL;
}
}
#else
unsigned int linphone_chat_message_store(LinphoneChatMessage *cr){
return 0;
}
void linphone_chat_message_store_state(LinphoneChatMessage *cr){
}
void linphone_chat_message_store_appdata(LinphoneChatMessage *msg){
}
void linphone_chat_room_mark_as_read(LinphoneChatRoom *cr){
}
MSList *linphone_chat_room_get_history(LinphoneChatRoom *cr,int nb_message){
return NULL;
}
void linphone_chat_room_delete_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
}
void linphone_chat_room_delete_history(LinphoneChatRoom *cr){
}
void linphone_message_storage_init_chat_rooms(LinphoneCore *lc) {
}
void linphone_core_message_storage_init(LinphoneCore *lc){
}
void linphone_core_message_storage_close(LinphoneCore *lc){
}
void linphone_chat_room_update_url(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
}
int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr){
return 0;
}
#endif

View file

@ -803,6 +803,13 @@ static void get_default_addr_and_port(uint16_t componentID, const SalMediaDescri
if ((*addr)[0] == '\0') *addr = md->addr;
}
static void clear_ice_check_list(LinphoneCall *call, IceCheckList *removed){
if (call->audiostream && call->audiostream->ms.ice_check_list==removed)
call->audiostream->ms.ice_check_list=NULL;
if (call->videostream && call->videostream->ms.ice_check_list==removed)
call->videostream->ms.ice_check_list=NULL;
}
void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md)
{
bool_t ice_restarted = FALSE;
@ -853,6 +860,7 @@ void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call,
for (i = 0; i < md->n_total_streams; i++) {
const SalStreamDescription *stream = &md->streams[i];
IceCheckList *cl = ice_session_check_list(call->ice_session, i);
/*
if ((cl == NULL) && (i < md->n_active_streams)) {
cl = ice_check_list_new();
ice_session_add_check_list(call->ice_session, cl);
@ -867,16 +875,13 @@ void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call,
break;
}
}
*/
if (cl==NULL) continue;
if (stream->ice_mismatch == TRUE) {
ice_check_list_set_state(cl, ICL_Failed);
} else if (stream->rtp_port == 0) {
ice_session_remove_check_list(call->ice_session, cl);
#ifdef VIDEO_ENABLED
if (stream->type==SalVideo && call->videostream){
video_stream_stop(call->videostream);
call->videostream=NULL;
}
#endif
clear_ice_check_list(call,cl);
} else {
if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0'))
ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd);
@ -916,10 +921,7 @@ void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call,
for (i = ice_session_nb_check_lists(call->ice_session); i > md->n_active_streams; i--) {
IceCheckList *removed=ice_session_check_list(call->ice_session, i - 1);
ice_session_remove_check_list(call->ice_session, removed);
if (call->audiostream && call->audiostream->ms.ice_check_list==removed)
call->audiostream->ms.ice_check_list=NULL;
if (call->videostream && call->videostream->ms.ice_check_list==removed)
call->videostream->ms.ice_check_list=NULL;
clear_ice_check_list(call,removed);
}
ice_session_check_mismatch(call->ice_session);
} else {
@ -932,12 +934,11 @@ void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call,
}
}
bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md)
{
bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md){
int i;
for (i = 0; i < md->n_active_streams; i++) {
if (md->streams[i].type == SalVideo)
for (i = 0; i < md->n_total_streams; i++) {
if (md->streams[i].type == SalVideo && md->streams[i].rtp_port!=0)
return TRUE;
}
return FALSE;
@ -1471,3 +1472,81 @@ void linphone_core_set_tone(LinphoneCore *lc, LinphoneToneID id, const char *aud
_linphone_core_set_tone(lc, LinphoneReasonNone, id, audiofile);
}
const MSCryptoSuite * linphone_core_get_srtp_crypto_suites(LinphoneCore *lc){
const char *config=lp_config_get_string(lc->config,"sip","srtp_crypto_suites","AES_CM_128_HMAC_SHA1_80, AES_CM_128_HMAC_SHA1_32");
char *tmp=ms_strdup(config);
char *sep;
char *pos;
char *nextpos;
char *params;
int found=0;
MSCryptoSuite *result=NULL;
pos=tmp;
do{
sep=strchr(pos,',');
if (!sep) {
sep=pos+strlen(pos);
nextpos=NULL;
}else {
*sep='\0';
nextpos=sep+1;
}
while(*pos==' ') ++pos; /*strip leading spaces*/
params=strchr(pos,' '); /*look for params that arrive after crypto suite name*/
if (params){
while(*params==' ') ++params; /*strip parameters leading space*/
}
if (sep-pos>0){
MSCryptoSuiteNameParams np;
MSCryptoSuite suite;
np.name=pos;
np.params=params;
suite=ms_crypto_suite_build_from_name_params(&np);
if (suite!=MS_CRYPTO_SUITE_INVALID){
result=ms_realloc(result,(found+2)*sizeof(MSCryptoSuite));
result[found]=suite;
result[found+1]=MS_CRYPTO_SUITE_INVALID;
found++;
ms_message("Configured srtp crypto suite: %s %s",np.name,np.params ? np.params : "");
}
}
pos=nextpos;
}while(pos);
ms_free(tmp);
if (lc->rtp_conf.srtp_suites){
ms_free(lc->rtp_conf.srtp_suites);
lc->rtp_conf.srtp_suites=NULL;
}
lc->rtp_conf.srtp_suites=result;
return result;
}
bool_t is_video_active(const SalStreamDescription *sd) {
return (sd->rtp_port != 0) && (sd->dir != SalStreamInactive);
}
bool_t stream_description_has_avpf(const SalStreamDescription *sd) {
return ((sd->proto == SalProtoRtpAvpf) || (sd->proto == SalProtoRtpSavpf));
}
bool_t stream_description_has_srtp(const SalStreamDescription *sd) {
return ((sd->proto == SalProtoRtpSavp) || (sd->proto == SalProtoRtpSavpf));
}
bool_t media_description_has_avpf(const SalMediaDescription *md) {
int i;
if (md->n_active_streams == 0) return FALSE;
for (i = 0; i < md->n_active_streams; i++) {
if (stream_description_has_avpf(&md->streams[i]) != TRUE) return FALSE;
}
return TRUE;
}
bool_t 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++) {
if (stream_description_has_srtp(&md->streams[i]) != TRUE) return FALSE;
}
return TRUE;
}

View file

@ -99,6 +99,10 @@ static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t
if (p2->send_fmtp)
payload_type_set_send_fmtp(newp,p2->send_fmtp);
newp->flags|=PAYLOAD_TYPE_FLAG_CAN_RECV|PAYLOAD_TYPE_FLAG_CAN_SEND;
if (p2->flags & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED) {
newp->flags |= PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED;
newp->avpf = payload_type_get_avpf_params(p2);
}
res=ms_list_append(res,newp);
/* we should use the remote numbering even when parsing a response */
payload_type_set_number(newp,remote_number);
@ -156,17 +160,16 @@ static bool_t match_crypto_algo(const SalSrtpCryptoAlgo* local, const SalSrtpCry
result->algo = remote[i].algo;
/* We're answering an SDP offer. Supply our master key, associated with the remote supplied tag */
if (use_local_key) {
strncpy(result->master_key, local[j].master_key, 41);
strncpy(result->master_key, local[j].master_key, sizeof(result->master_key) );
result->tag = remote[i].tag;
*choosen_local_tag = local[j].tag;
}
/* We received an answer to our SDP crypto proposal. Copy matching algo remote master key to result, and memorize local tag */
else {
strncpy(result->master_key, remote[i].master_key, 41);
strncpy(result->master_key, remote[i].master_key, sizeof(result->master_key));
result->tag = local[j].tag;
*choosen_local_tag = local[j].tag;
}
result->master_key[40] = '\0';
return TRUE;
}
}
@ -234,7 +237,7 @@ static void initiate_outgoing(const SalStreamDescription *local_offer,
}else{
result->rtp_port=0;
}
if (result->proto == SalProtoRtpSavp) {
if (stream_description_has_srtp(result) == TRUE) {
/* verify crypto algo */
memset(result->crypto, 0, sizeof(result->crypto));
if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], &result->crypto_local_tag, FALSE))
@ -260,7 +263,7 @@ static void initiate_incoming(const SalStreamDescription *local_cap,
}else{
result->rtp_port=0;
}
if (result->proto == SalProtoRtpSavp) {
if (stream_description_has_srtp(result) == TRUE) {
/* select crypto algo */
memset(result->crypto, 0, sizeof(result->crypto));
if (!match_crypto_algo(local_cap->crypto, remote_offer->crypto, &result->crypto[0], &result->crypto_local_tag, TRUE))
@ -324,10 +327,11 @@ static bool_t local_stream_not_already_used(const SalMediaDescription *result, c
return TRUE;
}
/*in answering mode, we consider that if we are able to make SAVP, then we can do AVP as well*/
static bool_t proto_compatible(SalMediaProto local, SalMediaProto remote){
if (local==remote) return TRUE;
if (remote==SalProtoRtpAvp && local==SalProtoRtpSavp) return TRUE;
/*in answering mode, we consider that if we are able to make AVPF/SAVP/SAVPF, then we can do AVP as well*/
static bool_t proto_compatible(SalMediaProto local, SalMediaProto remote) {
if (local == remote) return TRUE;
if ((remote == SalProtoRtpAvp) && ((local == SalProtoRtpSavp) || (local == SalProtoRtpSavpf))) return TRUE;
if ((remote == SalProtoRtpAvpf) && (local == SalProtoRtpSavpf)) return TRUE;
return FALSE;
}

View file

@ -278,6 +278,7 @@ LinphonePresenceBasicStatus linphone_presence_model_get_basic_status(const Linph
int linphone_presence_model_set_basic_status(LinphonePresenceModel *model, LinphonePresenceBasicStatus basic_status) {
LinphonePresenceService *service;
int err = 0;
if (model == NULL) return -1;
@ -285,8 +286,9 @@ int linphone_presence_model_set_basic_status(LinphonePresenceModel *model, Linph
service = linphone_presence_service_new(NULL, basic_status, NULL);
if (service == NULL) return -1;
if (linphone_presence_model_add_service(model, service) < 0) return -1;
return 0;
err = linphone_presence_model_add_service(model, service);
linphone_presence_service_unref(service);
return err;
}
static void presence_service_find_newer_timestamp(LinphonePresenceService *service, time_t *timestamp) {
@ -363,6 +365,7 @@ LinphonePresenceActivity * linphone_presence_model_get_activity(const LinphonePr
int linphone_presence_model_set_activity(LinphonePresenceModel *model, LinphonePresenceActivityType acttype, const char *description) {
LinphonePresenceBasicStatus basic_status = LinphonePresenceBasicStatusOpen;
LinphonePresenceActivity *activity;
int err = 0;
if (model == NULL) return -1;
@ -383,8 +386,9 @@ int linphone_presence_model_set_activity(LinphonePresenceModel *model, LinphoneP
linphone_presence_model_clear_activities(model);
activity = linphone_presence_activity_new(acttype, description);
if (activity == NULL) return -1;
return linphone_presence_model_add_activity(model, activity);
err = linphone_presence_model_add_activity(model, activity);
linphone_presence_activity_unref(activity);
return err;
}
unsigned int linphone_presence_model_get_nb_activities(const LinphonePresenceModel *model) {

View file

@ -95,10 +95,12 @@ struct _LinphoneCallParams{
char *session_name;
SalCustomHeader *custom_headers;
bool_t has_video;
bool_t avpf_enabled; /* RTCP feedback messages are enabled */
bool_t real_early_media; /*send real media even during early media (for outgoing calls)*/
bool_t in_conference; /*in conference mode */
bool_t low_bandwidth;
LinphonePrivacyMask privacy;
uint8_t avpf_rr_interval;
};
struct _LinphoneCallLog{
@ -142,6 +144,7 @@ struct _LinphoneChatMessage {
LinphoneChatMessageStateChangedCb cb;
void* cb_ud;
void* message_userdata;
char* appdata;
char* external_body_url;
LinphoneAddress *from;
LinphoneAddress *to;
@ -336,7 +339,6 @@ void linphone_call_stats_fill(LinphoneCallStats *stats, MediaStream *ms, OrtpEve
void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session);
void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md);
bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md);
bool_t linphone_core_media_description_has_srtp(const SalMediaDescription *md);
void linphone_core_send_initial_subscribes(LinphoneCore *lc);
void linphone_core_write_friends_config(LinphoneCore* lc);
@ -391,10 +393,18 @@ bool_t linphone_core_rtcp_enabled(const LinphoneCore *lc);
LinphoneCall * is_a_linphone_call(void *user_pointer);
LinphoneProxyConfig * is_a_linphone_proxy_config(void *user_pointer);
bool_t is_video_active(const SalStreamDescription *sd);
bool_t stream_description_has_avpf(const SalStreamDescription *sd);
bool_t stream_description_has_srtp(const SalStreamDescription *sd);
bool_t media_description_has_avpf(const SalMediaDescription *md);
bool_t media_description_has_srtp(const SalMediaDescription *md);
void linphone_core_queue_task(LinphoneCore *lc, belle_sip_source_func_t task_fun, void *data, const char *task_description);
static const int linphone_proxy_config_magic=0x7979;
bool_t linphone_proxy_config_address_equal(const LinphoneAddress *a, const LinphoneAddress *b);
bool_t linphone_proxy_config_is_server_config_changed(const LinphoneProxyConfig* obj);
void _linphone_proxy_config_unregister(LinphoneProxyConfig *obj);
/*chat*/
void linphone_chat_message_destroy(LinphoneChatMessage* msg);
@ -407,7 +417,7 @@ struct _LinphoneProxyConfig
char *reg_proxy;
char *reg_identity;
char *reg_route;
char *statistics_collector;
char *quality_reporting_collector;
char *realm;
char *contact_params;
char *contact_uri_params;
@ -425,11 +435,19 @@ struct _LinphoneProxyConfig
bool_t publish;
bool_t dial_escape_plus;
bool_t send_publish;
bool_t send_statistics;
bool_t pad[3];
bool_t quality_reporting_enabled;
bool_t avpf_enabled;
bool_t pad;
uint8_t avpf_rr_interval;
uint8_t quality_reporting_interval;
void* user_data;
time_t deletion_date;
LinphonePrivacyMask privacy;
/*use to check if server config has changed between edit() and done()*/
LinphoneAddress *saved_proxy;
LinphoneAddress *saved_identity;
/*---*/
};
struct _LinphoneAuthInfo
@ -513,6 +531,7 @@ typedef struct rtp_config
int video_jitt_comp; /*jitter compensation*/
int nortp_timeout;
int disable_upnp;
MSCryptoSuite *srtp_suites;
bool_t rtp_no_xmit_on_audio_mute;
/* stop rtp xmit when audio muted */
bool_t audio_adaptive_jitt_comp_enabled;
@ -694,6 +713,7 @@ struct _LinphoneCore
char *chat_db_file;
#ifdef MSG_STORAGE_ENABLED
sqlite3 *db;
bool_t debug_storage;
#endif
#ifdef BUILD_UPNP
UpnpContext *upnp;
@ -735,6 +755,7 @@ int linphone_core_get_calls_nb(const LinphoneCore *lc);
void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message);
void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call);
void linphone_call_increment_local_media_description(LinphoneCall *call);
void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md);
bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, const PayloadType *pt, int bandwidth_limit);
@ -807,8 +828,10 @@ sqlite3 * linphone_message_storage_init();
void linphone_message_storage_init_chat_rooms(LinphoneCore *lc);
#endif
void linphone_chat_message_store_state(LinphoneChatMessage *msg);
void linphone_chat_message_store_appdata(LinphoneChatMessage* msg);
void linphone_core_message_storage_init(LinphoneCore *lc);
void linphone_core_message_storage_close(LinphoneCore *lc);
void linphone_core_message_storage_set_debug(LinphoneCore *lc, bool_t debug);
void linphone_core_play_named_tone(LinphoneCore *lc, LinphoneToneID id);
bool_t linphone_core_tone_indications_enabled(LinphoneCore*lc);
@ -882,6 +905,7 @@ static inline const LinphoneErrorInfo *linphone_error_info_from_sal_op(const Sal
return (const LinphoneErrorInfo*)sal_op_get_error_info(op);
}
const MSCryptoSuite * linphone_core_get_srtp_crypto_suites(LinphoneCore *lc);
/** Belle Sip-based objects need unique ids
*/

View file

@ -26,6 +26,54 @@ Copyright (C) 2000 Simon MORLAT (simon.morlat@linphone.org)
#include <ctype.h>
/*store current config related to server location*/
static void linphone_proxy_config_store_server_config(LinphoneProxyConfig* obj) {
if (obj->saved_identity) linphone_address_destroy(obj->saved_identity);
if (obj->reg_identity)
obj->saved_identity = linphone_address_new(obj->reg_identity);
else
obj->saved_identity = NULL;
if (obj->saved_proxy) linphone_address_destroy(obj->saved_proxy);
if (obj->reg_proxy)
obj->saved_proxy = linphone_address_new(obj->reg_proxy);
else
obj->saved_proxy = NULL;
}
bool_t linphone_proxy_config_address_equal(const LinphoneAddress *a, const LinphoneAddress *b) {
if (a == NULL && b == NULL)
return TRUE;
else if (!a || !b)
return FALSE;
if (linphone_address_weak_equal(a,b)) {
/*also check both transport and uri */
return linphone_address_is_secure(a) == linphone_address_is_secure(b) && linphone_address_get_transport(a) == linphone_address_get_transport(b);
} else
return FALSE; /*either username, domain or port ar not equals*/
}
bool_t linphone_proxy_config_is_server_config_changed(const LinphoneProxyConfig* obj) {
LinphoneAddress *current_identity=obj->reg_identity?linphone_address_new(obj->reg_identity):NULL;
LinphoneAddress *current_proxy=obj->reg_proxy?linphone_address_new(obj->reg_proxy):NULL;
bool_t result=FALSE;
if (!linphone_proxy_config_address_equal(obj->saved_identity,current_identity)){
result=TRUE;
goto end;
}
if (!linphone_proxy_config_address_equal(obj->saved_proxy,current_proxy)){
result=TRUE;
goto end;
}
end:
if (current_identity) linphone_address_destroy(current_identity);
if (current_proxy) linphone_address_destroy(current_proxy);
return result;
}
void linphone_proxy_config_write_all_to_config_file(LinphoneCore *lc){
MSList *elem;
@ -46,7 +94,7 @@ 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", "statistics_collector", NULL) : NULL;
const char *quality_reporting_collector = lc ? lp_config_get_default_string(lc->config, "proxy", "quality_reporting_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;
@ -60,10 +108,13 @@ 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->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->quality_reporting_enabled = lc ? lp_config_get_default_int(lc->config, "proxy", "quality_reporting_enabled", 0) : 0;
obj->quality_reporting_collector = quality_reporting_collector ? ms_strdup(quality_reporting_collector) : NULL;
obj->quality_reporting_interval = lc ? lp_config_get_default_int(lc->config, "proxy", "quality_reporting_interval", 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;
obj->avpf_enabled = lc ? lp_config_get_default_int(lc->config, "proxy", "avpf", 0) : 0;
obj->avpf_rr_interval = lc ? lp_config_get_default_int(lc->config, "proxy", "avpf_rr_interval", 5) : 5;
obj->publish_expires=-1;
}
@ -97,7 +148,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->statistics_collector!=NULL) ms_free(obj->statistics_collector);
if (obj->quality_reporting_collector!=NULL) ms_free(obj->quality_reporting_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);
@ -106,6 +157,8 @@ void linphone_proxy_config_destroy(LinphoneProxyConfig *obj){
if (obj->publish_op) sal_op_release(obj->publish_op);
if (obj->contact_params) ms_free(obj->contact_params);
if (obj->contact_uri_params) ms_free(obj->contact_uri_params);
if (obj->saved_proxy!=NULL) linphone_address_destroy(obj->saved_proxy);
if (obj->saved_identity!=NULL) linphone_address_destroy(obj->saved_identity);
ms_free(obj);
}
@ -265,21 +318,16 @@ void linphone_proxy_config_enable_publish(LinphoneProxyConfig *obj, bool_t val){
**/
void linphone_proxy_config_edit(LinphoneProxyConfig *obj){
if (obj->publish && obj->publish_op){
/*unpublish*/
sal_publish_presence(obj->publish_op,NULL,NULL,0,(SalPresenceModel *)NULL);
sal_op_release(obj->publish_op);
obj->publish_op=NULL;
}
if (obj->reg_sendregister){
/* unregister */
if (obj->state == LinphoneRegistrationOk
|| obj->state == LinphoneRegistrationProgress) {
sal_unregister(obj->op);
} else {
/*stop refresher*/
if (obj->op) sal_op_stop_refreshing(obj->op);
}
/*unpublish*/
sal_publish_presence(obj->publish_op,NULL,NULL,0,(SalPresenceModel *)NULL);
sal_op_release(obj->publish_op);
obj->publish_op=NULL;
}
/*store current config related to server location*/
linphone_proxy_config_store_server_config(obj);
/*stop refresher in any case*/
if (obj->op) sal_op_stop_refreshing(obj->op);
}
void linphone_proxy_config_apply(LinphoneProxyConfig *obj,LinphoneCore *lc){
@ -336,6 +384,14 @@ LinphoneAddress *guess_contact_for_register(LinphoneProxyConfig *obj){
return ret;
}
/**
* unregister without moving the register_enable flag
*/
void _linphone_proxy_config_unregister(LinphoneProxyConfig *obj) {
if (obj->state == LinphoneRegistrationOk) {
sal_unregister(obj->op);
}
}
static void linphone_proxy_config_register(LinphoneProxyConfig *obj){
if (obj->reg_sendregister){
@ -360,8 +416,14 @@ static void linphone_proxy_config_register(LinphoneProxyConfig *obj){
}
ms_free(proxy_string);
} else {
/*stop refresher, just in case*/
if (obj->op) sal_op_stop_refreshing(obj->op);
/* unregister if registered*/
if (obj->state == LinphoneRegistrationProgress) {
linphone_proxy_config_set_state(obj,LinphoneRegistrationCleared,"Registration cleared");
}
_linphone_proxy_config_unregister(obj);
}
}
@ -419,16 +481,24 @@ 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;
void linphone_proxy_config_enable_quality_reporting(LinphoneProxyConfig *cfg, bool_t val){
cfg->quality_reporting_enabled = val;
}
bool_t linphone_proxy_config_send_statistics_enabled(LinphoneProxyConfig *cfg){
bool_t linphone_proxy_config_quality_reporting_enabled(LinphoneProxyConfig *cfg){
// ensure that collector address is set too!
return cfg->send_statistics && cfg->statistics_collector != NULL;
return cfg->quality_reporting_enabled && cfg->quality_reporting_collector != NULL;
}
void linphone_proxy_config_set_statistics_collector(LinphoneProxyConfig *cfg, const char *collector){
void linphone_proxy_config_set_quality_reporting_interval(LinphoneProxyConfig *cfg, uint8_t interval) {
cfg->quality_reporting_interval = interval;
}
int linphone_proxy_config_get_quality_reporting_interval(LinphoneProxyConfig *cfg) {
return cfg->quality_reporting_interval;
}
void linphone_proxy_config_set_quality_reporting_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){
@ -436,16 +506,16 @@ void linphone_proxy_config_set_statistics_collector(LinphoneProxyConfig *cfg, co
if (addr)
linphone_address_destroy(addr);
} else {
if (cfg->statistics_collector != NULL)
ms_free(cfg->statistics_collector);
cfg->statistics_collector = ms_strdup(collector);
if (cfg->quality_reporting_collector != NULL)
ms_free(cfg->quality_reporting_collector);
cfg->quality_reporting_collector = ms_strdup(collector);
linphone_address_destroy(addr);
}
}
}
const char *linphone_proxy_config_get_statistics_collector(const LinphoneProxyConfig *cfg){
return cfg->statistics_collector;
const char *linphone_proxy_config_get_quality_reporting_collector(const LinphoneProxyConfig *cfg){
return cfg->quality_reporting_collector;
}
@ -843,7 +913,19 @@ int linphone_proxy_config_normalize_number(LinphoneProxyConfig *proxy, const cha
**/
int linphone_proxy_config_done(LinphoneProxyConfig *obj)
{
if (!linphone_proxy_config_check(obj->lc,obj)) return -1;
if (!linphone_proxy_config_check(obj->lc,obj))
return -1;
/*check if server address as changed*/
if (linphone_proxy_config_is_server_config_changed(obj)) {
/* server config has changed, need to unregister from previous first*/
if (obj->op) {
_linphone_proxy_config_unregister(obj);
sal_op_set_user_pointer(obj->op,NULL); /*we don't want to receive status for this un register*/
sal_op_unref(obj->op); /*but we keep refresher to handle authentication if needed*/
obj->op=NULL;
}
}
obj->commit=TRUE;
linphone_proxy_config_write_all_to_config_file(obj->lc);
return 0;
@ -947,7 +1029,7 @@ void linphone_proxy_config_set_contact_parameters(LinphoneProxyConfig *obj, cons
/**
* Set optional contact parameters that will be added to the contact information sent in the registration, inside the URI.
* @param obj the proxy config object
* @param contact_params a string contaning the additional parameters in text form, like "myparam=something;myparam2=something_else"
* @param contact_uri_params a string containing the additional parameters in text form, like "myparam=something;myparam2=something_else"
*
* The main use case for this function is provide the proxy additional information regarding the user agent, like for example unique identifier or apple push id.
* As an example, the contact address in the SIP register sent will look like <sip:joe@15.128.128.93:50421;apple-push-id=43143-DFE23F-2323-FA2232>.
@ -1014,8 +1096,10 @@ void linphone_core_remove_proxy_config(LinphoneCore *lc, LinphoneProxyConfig *cf
lc->sip_conf.deleted_proxies=ms_list_append(lc->sip_conf.deleted_proxies,cfg);
cfg->deletion_date=ms_time(NULL);
if (cfg->state==LinphoneRegistrationOk){
/* this will unREGISTER */
/* unREGISTER */
linphone_proxy_config_edit(cfg);
linphone_proxy_config_enable_register(cfg,FALSE);
linphone_proxy_config_done(cfg);
}
if (lc->default_proxy==cfg){
lc->default_proxy=NULL;
@ -1099,9 +1183,6 @@ 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->statistics_collector!=NULL){
lp_config_set_string(config,key,"statistics_collector",obj->statistics_collector);
}
if (obj->reg_identity!=NULL){
lp_config_set_string(config,key,"reg_identity",obj->reg_identity);
}
@ -1111,11 +1192,17 @@ void linphone_proxy_config_write_to_config_file(LpConfig *config, LinphoneProxyC
if (obj->contact_uri_params!=NULL){
lp_config_set_string(config,key,"contact_uri_parameters",obj->contact_uri_params);
}
if (obj->quality_reporting_collector!=NULL){
lp_config_set_string(config,key,"quality_reporting_collector",obj->quality_reporting_collector);
}
lp_config_set_int(config,key,"quality_reporting_enabled",obj->quality_reporting_enabled);
lp_config_set_int(config,key,"quality_reporting_interval",obj->quality_reporting_interval);
lp_config_set_int(config,key,"reg_expires",obj->expires);
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, "avpf", obj->avpf_enabled);
lp_config_set_int(config, key, "avpf_rr_interval", obj->avpf_rr_interval);
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);
}
@ -1129,6 +1216,7 @@ LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(LpConfig *config
const char *proxy;
LinphoneProxyConfig *cfg;
char key[50];
int interval;
sprintf(key,"proxy_%i",index);
@ -1147,9 +1235,11 @@ LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(LpConfig *config
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,"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_enable_quality_reporting(cfg,lp_config_get_int(config,key,"quality_reporting_enabled",0));
tmp=lp_config_get_string(config,key,"quality_reporting_collector",NULL);
if (tmp!=NULL) linphone_proxy_config_set_quality_reporting_collector(cfg,tmp);
interval=lp_config_get_int(config, key, "quality_reporting_interval", 0);
linphone_proxy_config_set_quality_reporting_interval(cfg, interval? MAX(interval, 120) : 0);
linphone_proxy_config_set_contact_parameters(cfg,lp_config_get_string(config,key,"contact_parameters",NULL));
@ -1160,6 +1250,9 @@ LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(LpConfig *config
linphone_proxy_config_enable_publish(cfg,lp_config_get_int(config,key,"publish",0));
linphone_proxy_config_enable_avpf(cfg, lp_config_get_int(config, key, "avpf", 0));
linphone_proxy_config_set_avpf_rr_interval(cfg, lp_config_get_int(config, key, "avpf_rr_interval", 5));
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)));
@ -1472,3 +1565,20 @@ int linphone_proxy_config_get_publish_expires(const LinphoneProxyConfig *obj) {
}
}
void linphone_proxy_config_enable_avpf(LinphoneProxyConfig *cfg, bool_t enable) {
cfg->avpf_enabled = enable;
}
bool_t linphone_proxy_config_avpf_enabled(LinphoneProxyConfig *cfg) {
return cfg->avpf_enabled;
}
void linphone_proxy_config_set_avpf_rr_interval(LinphoneProxyConfig *cfg, uint8_t interval) {
if (interval > 5) interval = 5;
cfg->avpf_rr_interval = interval;
}
uint8_t linphone_proxy_config_get_avpf_rr_interval(const LinphoneProxyConfig *cfg) {
return cfg->avpf_rr_interval;
}

View file

@ -30,13 +30,25 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
/***************************************************************************
* TODO / REMINDER LIST
****************************************************************************/
/*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*/
/***************************************************************************
***************************************************************************
For codecs that are able to change sample rates, the lowest and highest sample rates MUST be reported (e.g., 8000;16000).
rlq value: need algo to compute it
3.4 overload avoidance?
a. Send only one report at the end of each call. (audio | video?)
b. Use interval reports only on "problem" calls that are being closely monitored.
move "on_action_suggested" stuff in init
- The Session report when
codec change
session fork
video enable/disable <-- what happens if doing stop/resume?
if BYE and continue received packet drop them
***************************************************************************
* END OF TODO / REMINDER LIST
****************************************************************************/
@ -73,13 +85,13 @@ static void append_to_buffer_valist(char **buff, size_t *buff_size, size_t *offs
/*if we are out of memory, we add some size to buffer*/
if (ret == BELLE_SIP_BUFFER_OVERFLOW) {
/*some compilers complain that size_t cannot be formatted as unsigned long, hence forcing cast*/
ms_warning("Buffer was too small to contain the whole report - doubling its size from %lu to %lu",
(unsigned long)*buff_size, (unsigned long)2 * *buff_size);
ms_warning("Buffer was too small to contain the whole report - increasing its size from %lu to %lu",
(unsigned long)*buff_size, (unsigned long)*buff_size + 2048);
*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
/*recall itself 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);
}
@ -92,44 +104,90 @@ static void append_to_buffer(char **buff, size_t *buff_size, size_t *offset, con
va_end(args);
}
static void reset_avg_metrics(reporting_session_report_t * report){
int i;
reporting_content_metrics_t * metrics[2] = {&report->local_metrics, &report->remote_metrics};
for (i = 0; i < 2; i++) {
metrics[i]->rtcp_xr_count = 0;
metrics[i]->jitter_buffer.nominal = 0;
metrics[i]->jitter_buffer.max = 0;
metrics[i]->delay.round_trip_delay = 0;
/*metrics[i]->delay.symm_one_way_delay = 0;*/
}
report->last_report_date = ms_time(NULL);
}
#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);
#define METRICS_PACKET_LOSS 1 << 0
#define METRICS_QUALITY_ESTIMATES 1 << 1
#define METRICS_SESSION_DESCRIPTION 1 << 2
#define METRICS_JITTER_BUFFER 1 << 3
#define METRICS_DELAY 1 << 4
#define METRICS_SIGNAL 1 << 5
#define METRICS_ADAPTIVE_ALGORITHM 1 << 6
static uint8_t are_metrics_filled(const reporting_content_metrics_t rm) {
uint8_t ret = 0;
IF_NUM_IN_RANGE(rm.packet_loss.network_packet_loss_rate, 0, 255, ret|=METRICS_PACKET_LOSS);
IF_NUM_IN_RANGE(rm.packet_loss.jitter_buffer_discard_rate, 0, 255, ret|=METRICS_PACKET_LOSS);
/*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 (rm.session_description.payload_type != -1) ret|=METRICS_SESSION_DESCRIPTION;*/
/*if (rm.session_description.payload_desc != NULL) ret|=METRICS_SESSION_DESCRIPTION;*/
/*if (rm.session_description.sample_rate != -1) ret|=METRICS_SESSION_DESCRIPTION;*/
/*if (rm.session_description.fmtp != NULL) ret|=METRICS_SESSION_DESCRIPTION;*/
if (rm.session_description.frame_duration != -1) ret|=METRICS_SESSION_DESCRIPTION;
if (rm.session_description.packet_loss_concealment != -1) ret|=METRICS_SESSION_DESCRIPTION;
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.jitter_buffer.adaptive, 0, 3, ret|=METRICS_JITTER_BUFFER);
IF_NUM_IN_RANGE(rm.jitter_buffer.abs_max, 0, 65535, ret|=METRICS_JITTER_BUFFER);
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_NUM_IN_RANGE(rm.delay.end_system_delay, 0, 65535, ret|=METRICS_DELAY);
/*IF_NUM_IN_RANGE(rm.delay.symm_one_way_delay, 0, 65535, ret|=METRICS_DELAY);*/
IF_NUM_IN_RANGE(rm.delay.interarrival_jitter, 0, 65535, ret|=METRICS_DELAY);
IF_NUM_IN_RANGE(rm.delay.mean_abs_jitter, 0, 65535, ret|=METRICS_DELAY);
if (rm.signal.level != 127) return TRUE;
if (rm.signal.noise_level != 127) return TRUE;
if (rm.signal.level != 127) ret|=METRICS_SIGNAL;
if (rm.signal.noise_level != 127) ret|=METRICS_SIGNAL;
IF_NUM_IN_RANGE(rm.quality_estimates.rlq, 1, 120, return TRUE);
IF_NUM_IN_RANGE(rm.quality_estimates.rcq, 1, 120, return TRUE);
if (rm.qos_analyzer.input_leg!=NULL) ret|=METRICS_ADAPTIVE_ALGORITHM;
if (rm.qos_analyzer.input!=NULL) ret|=METRICS_ADAPTIVE_ALGORITHM;
if (rm.qos_analyzer.output_leg!=NULL) ret|=METRICS_ADAPTIVE_ALGORITHM;
if (rm.qos_analyzer.output!=NULL) ret|=METRICS_ADAPTIVE_ALGORITHM;
return FALSE;
if (rm.rtcp_xr_count>0){
IF_NUM_IN_RANGE(rm.jitter_buffer.nominal/rm.rtcp_xr_count, 0, 65535, ret|=METRICS_JITTER_BUFFER);
IF_NUM_IN_RANGE(rm.jitter_buffer.max/rm.rtcp_xr_count, 0, 65535, ret|=METRICS_JITTER_BUFFER);
IF_NUM_IN_RANGE(rm.delay.round_trip_delay, 0, 65535, ret|=METRICS_DELAY);
IF_NUM_IN_RANGE(rm.quality_estimates.moslq/rm.rtcp_xr_count, 1, 5, ret|=METRICS_QUALITY_ESTIMATES);
IF_NUM_IN_RANGE(rm.quality_estimates.moscq/rm.rtcp_xr_count, 1, 5, ret|=METRICS_QUALITY_ESTIMATES);
IF_NUM_IN_RANGE(rm.quality_estimates.rlq/rm.rtcp_xr_count, 1, 120, ret|=METRICS_QUALITY_ESTIMATES);
IF_NUM_IN_RANGE(rm.quality_estimates.rcq/rm.rtcp_xr_count, 1, 120, ret|=METRICS_QUALITY_ESTIMATES);
}
return ret;
}
static bool_t quality_reporting_enabled(const LinphoneCall * call) {
return (call->dest_proxy != NULL && linphone_proxy_config_quality_reporting_enabled(call->dest_proxy));
}
static bool_t media_report_enabled(LinphoneCall * call, int stats_type){
if (! quality_reporting_enabled(call))
return FALSE;
if (stats_type == LINPHONE_CALL_STATS_VIDEO && !linphone_call_params_video_enabled(linphone_call_get_current_params(call)))
return FALSE;
return (call->log->reports[stats_type] != NULL);
}
static void append_metrics_to_buffer(char ** buffer, size_t * size, size_t * offset, const reporting_content_metrics_t rm) {
@ -140,79 +198,105 @@ static void append_metrics_to_buffer(char ** buffer, size_t * size, size_t * off
/*char * gap_loss_density_str = NULL;*/
char * moslq_str = NULL;
char * moscq_str = NULL;
uint8_t available_metrics = are_metrics_filled(rm);
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);*/
if ((available_metrics & METRICS_SESSION_DESCRIPTION) != 0){
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);
if ((available_metrics & METRICS_JITTER_BUFFER) != 0){
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);*/
if (rm.rtcp_xr_count){
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " JBN=%d", rm.jitter_buffer.nominal/rm.rtcp_xr_count, 0, 65535);
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " JBM=%d", rm.jitter_buffer.max/rm.rtcp_xr_count, 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\nPacketLoss:");
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));
/*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_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\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\nBurstGapLoss:");*/
/*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));*/
/* 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\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);*/
if ((available_metrics & METRICS_DELAY) != 0){
append_to_buffer(buffer, size, offset, "\r\nDelay:");
if (rm.rtcp_xr_count){
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " RTD=%d", rm.delay.round_trip_delay/rm.rtcp_xr_count, 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);
}
if ((available_metrics & METRICS_SIGNAL) != 0){
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);*/
}
/*if quality estimates metrics are available, rtcp_xr_count should be always not null*/
if ((available_metrics & METRICS_QUALITY_ESTIMATES) != 0){
IF_NUM_IN_RANGE(rm.quality_estimates.moslq/rm.rtcp_xr_count, 1, 5, moslq_str = float_to_one_decimal_string(rm.quality_estimates.moslq/rm.rtcp_xr_count));
IF_NUM_IN_RANGE(rm.quality_estimates.moscq/rm.rtcp_xr_count, 1, 5, moscq_str = float_to_one_decimal_string(rm.quality_estimates.moscq/rm.rtcp_xr_count));
append_to_buffer(buffer, size, offset, "\r\nQualityEst:");
APPEND_IF_NUM_IN_RANGE(buffer, size, offset, " RLQ=%d", rm.quality_estimates.rlq/rm.rtcp_xr_count, 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/rm.rtcp_xr_count, 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);*/
}
if ((available_metrics & METRICS_ADAPTIVE_ALGORITHM) != 0){
append_to_buffer(buffer, size, offset, "\r\nAdaptiveAlg:");
APPEND_IF_NOT_NULL_STR(buffer, size, offset, " IN_LEG=%s", rm.qos_analyzer.input_leg);
APPEND_IF_NOT_NULL_STR(buffer, size, offset, " IN=%s", rm.qos_analyzer.input);
APPEND_IF_NOT_NULL_STR(buffer, size, offset, " OUT_LEG=%s", rm.qos_analyzer.output_leg);
APPEND_IF_NOT_NULL_STR(buffer, size, offset, " OUT=%s", rm.qos_analyzer.output);
}
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);
@ -224,7 +308,7 @@ static void append_metrics_to_buffer(char ** buffer, size_t * size, size_t * off
ms_free(moscq_str);
}
static void reporting_publish(const LinphoneCall* call, const reporting_session_report_t * report) {
static void send_report(const LinphoneCall* call, reporting_session_report_t * report, const char * report_event) {
LinphoneContent content = {0};
LinphoneAddress *addr;
int expires = -1;
@ -237,7 +321,14 @@ static void reporting_publish(const LinphoneCall* call, const reporting_session_
if (report->info.local_addr.ip == NULL || strlen(report->info.local_addr.ip) == 0
|| report->info.remote_addr.ip == NULL || 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));
"not be retrieved so dropping this report"
, linphone_call_get_duration(call));
return;
}
addr = linphone_address_new(linphone_proxy_config_get_quality_reporting_collector(call->dest_proxy));
if (addr == NULL) {
ms_warning("Asked to submit reporting statistics but no collector address found");
return;
}
@ -245,7 +336,7 @@ static void reporting_publish(const LinphoneCall* call, const reporting_session_
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, "%s\r\n", report_event);
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);
@ -261,24 +352,20 @@ static void reporting_publish(const LinphoneCall* call, const reporting_session_
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)) {
if (are_metrics_filled(report->remote_metrics)!=0) {
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);
content.size = strlen(buffer);
/*(WIP) Memory leak: PUBLISH message is never freed (issue 1283)*/
linphone_core_publish(call->core, addr, "vq-rtcpxr", expires, &content);
linphone_address_destroy(addr);
addr = linphone_address_new(call->dest_proxy->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");
}
reset_avg_metrics(report);
linphone_content_uninit(&content);
}
@ -291,16 +378,14 @@ static const SalStreamDescription * get_media_stream_for_desc(const SalMediaDesc
}
}
}
if (smd == NULL || count == smd->n_total_streams) {
ms_warning("Could not find the associated stream of type %d", sal_stream_type);
}
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) {
static void 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) {
if (media_report_enabled(call,stats_type)) {
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);
@ -324,32 +409,32 @@ static void reporting_update_ip(LinphoneCall * call, int stats_type) {
}
}
static bool_t reporting_enabled(const LinphoneCall * call) {
return (call->dest_proxy != NULL && linphone_proxy_config_send_statistics_enabled(call->dest_proxy));
static void qos_analyzer_on_action_suggested(void *user_data, int datac, const char** data){
reporting_content_metrics_t *metrics = (reporting_content_metrics_t*) user_data;
char * appendbuf;
STR_REASSIGN(metrics->qos_analyzer.input_leg, ms_strdup(data[0]));
appendbuf=ms_strdup_printf("%s%s;", metrics->qos_analyzer.input?metrics->qos_analyzer.input:"", data[1]);
STR_REASSIGN(metrics->qos_analyzer.input,appendbuf);
STR_REASSIGN(metrics->qos_analyzer.output_leg, ms_strdup(data[2]));
appendbuf=ms_strdup_printf("%s%s;", metrics->qos_analyzer.output?metrics->qos_analyzer.output:"", data[3]);
STR_REASSIGN(metrics->qos_analyzer.output, appendbuf);
}
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);
}
update_ip(call, LINPHONE_CALL_STATS_AUDIO);
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];
void linphone_reporting_update_media_info(LinphoneCall * call, int 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);
reporting_session_report_t * report = call->log->reports[stats_type];
if (! reporting_enabled(call))
if (!media_report_enabled(call, stats_type))
return;
STR_REASSIGN(report->info.call_id, ms_strdup(call->log->call_id));
@ -410,73 +495,89 @@ void linphone_reporting_update(LinphoneCall * call, int stats_type) {
}
}
void linphone_reporting_call_stats_updated(LinphoneCall *call, int stats_type) {
void linphone_reporting_on_rtcp_received(LinphoneCall *call, int stats_type) {
reporting_session_report_t * report = call->log->reports[stats_type];
reporting_content_metrics_t * metrics = NULL;
MSQosAnalyzer *analyzer=NULL;
LinphoneCallStats stats = call->stats[stats_type];
mblk_t *block = NULL;
if (! reporting_enabled(call))
int report_interval = linphone_proxy_config_get_quality_reporting_interval(call->dest_proxy);
if (! media_report_enabled(call,stats_type))
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;
}
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;
block = stats.sent_rtcp;
}
/*should not be done there*/
if (call->audiostream->ms.use_rc&&call->audiostream->ms.rc){
analyzer=ms_bitrate_controller_get_qos_analyzer(call->audiostream->ms.rc);
if (analyzer){
ms_qos_analyzer_set_on_action_suggested(analyzer,
qos_analyzer_on_action_suggested,
&report->local_metrics);
}
}
if (block != NULL) {
switch (rtcp_XR_get_block_type(block)) {
case RTCP_XR_VOIP_METRICS: {
uint8_t config;
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;
do{
if (rtcp_is_XR(block) && (rtcp_XR_get_block_type(block) == RTCP_XR_VOIP_METRICS)){
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);
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;
}
metrics->rtcp_xr_count++;
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->jitter_buffer.adaptive = (config >> 4) & 0x3;
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);
metrics->session_description.packet_loss_concealment = (config >> 6) & 0x3;
metrics->delay.round_trip_delay += rtcp_XR_voip_metrics_get_round_trip_delay(block);
}
}while(rtcp_next_packet(block));
/* check if we should send an interval report */
if (report_interval>0 && ms_time(NULL)-report->last_report_date>report_interval){
linphone_reporting_publish_interval_report(call);
}
}
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]);
static void publish_report(LinphoneCall *call, const char *event_type){
int i;
for (i = 0; i < 2; i++){
if (media_report_enabled(call, i)){
linphone_reporting_update_media_info(call, i);
send_report(call, call->log->reports[i], event_type);
}
}
}
void linphone_reporting_publish_session_report(LinphoneCall* call) {
publish_report(call, "VQSessionReport: CallTerm");
}
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]);
}
void linphone_reporting_publish_interval_report(LinphoneCall* call) {
publish_report(call, "VQIntervalReport");
}
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};
memset(rm, 0, sizeof(reporting_session_report_t));
for (i = 0; i < 2; i++) {
metrics[i]->session_description.payload_type = -1;
metrics[i]->session_description.sample_rate = -1;
@ -489,20 +590,18 @@ reporting_session_report_t * linphone_reporting_new() {
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;
}
reset_avg_metrics(rm);
return rm;
}
@ -522,6 +621,10 @@ void linphone_reporting_destroy(reporting_session_report_t * report) {
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);
if (report->local_metrics.qos_analyzer.input_leg != NULL) ms_free(report->local_metrics.qos_analyzer.input_leg);
if (report->local_metrics.qos_analyzer.input != NULL) ms_free(report->local_metrics.qos_analyzer.input);
if (report->local_metrics.qos_analyzer.output_leg != NULL) ms_free(report->local_metrics.qos_analyzer.output_leg);
if (report->local_metrics.qos_analyzer.output != NULL) ms_free(report->local_metrics.qos_analyzer.output);
ms_free(report);
}

View file

@ -28,7 +28,7 @@ extern "C"{
/**
* Linphone quality report sub object storing address related information (ip / port / MAC).
* Linphone quality report sub object storing address related information (IP/port/MAC).
*/
typedef struct reporting_addr {
char * ip;
@ -60,22 +60,22 @@ typedef struct reporting_content_metrics {
// jitter buffet - optional
struct {
int adaptive; // constant
int nominal; // no may vary during the call <- average? worst score?
int max; // no may vary during the call <- average?
int nominal; // average
int max; // average
int abs_max; // constant
} jitter_buffer;
// packet loss - optional
struct {
float network_packet_loss_rate;
float jitter_buffer_discard_rate;
float network_packet_loss_rate; // average
float jitter_buffer_discard_rate; // average
} packet_loss;
// delay - optional
struct {
int round_trip_delay; // no - vary
int end_system_delay; // no - not implemented yet
int symm_one_way_delay; // no - vary (depends on round_trip_delay) + not implemented (depends on end_system_delay)
int symm_one_way_delay; // no - not implemented (depends on end_system_delay)
int interarrival_jitter; // no - not implemented yet
int mean_abs_jitter; // to check
} delay;
@ -93,6 +93,20 @@ typedef struct reporting_content_metrics {
float moslq; // no - vary or avg - voip metrics - in [0..4.9]
float moscq; // no - vary or avg - voip metrics - in [0..4.9]
} quality_estimates;
// Quality of Service analyzer - custom extension
/* This should allow us to analysis bad network conditions and quality adaptation
on server side*/
struct {
char* input_leg;
char* input;
char* output_leg;
char* output;
} qos_analyzer;
// for internal processing
uint8_t rtcp_xr_count; // number of RTCP XR packets received since last report, used to compute average of instantaneous parameters as stated in the RFC 6035 (4.5)
} reporting_content_metrics_t;
@ -119,6 +133,9 @@ typedef struct reporting_session_report {
reporting_content_metrics_t remote_metrics; // optional
char * dialog_id; // optional
// for internal processing
time_t last_report_date;
} reporting_session_report_t;
reporting_session_report_t * linphone_reporting_new();
@ -131,23 +148,32 @@ void linphone_reporting_destroy(reporting_session_report_t * report);
* @param stats_type the media type (LINPHONE_CALL_STATS_AUDIO or LINPHONE_CALL_STATS_VIDEO)
*
*/
void linphone_reporting_update(LinphoneCall * call, int stats_type);
void linphone_reporting_update_media_info(LinphoneCall * call, int stats_type);
/**
* Fill IP information about a given call. This function must be called each
* time state is 'LinphoneCallStreamsRunning' since IP might be updated (if we
* time call state is 'LinphoneCallStreamsRunning' since IP might be updated (if we
* found a direct route between caller and callee for example).
* When call is starting, remote IP/port might be the proxy ones to which callee is registered
* @param call #LinphoneCall object to consider
*
*/
void linphone_reporting_update_ip(LinphoneCall * call);
/**
* Publish the report on the call end.
* Publish a session report. This function should be called when session terminates,
* media change (codec change or session fork), session terminates due to no media packets being received.
* @param call #LinphoneCall object to consider
*
*/
void linphone_reporting_publish(LinphoneCall* call);
void linphone_reporting_publish_session_report(LinphoneCall* call);
/**
* Publish an interval report. This function should be used for periodic interval
* @param call #LinphoneCall object to consider
*
*/
void linphone_reporting_publish_interval_report(LinphoneCall* call);
/**
* Update publish report data with fresh RTCP stats, if needed.
@ -155,7 +181,7 @@ void linphone_reporting_publish(LinphoneCall* call);
* @param stats_type the media type (LINPHONE_CALL_STATS_AUDIO or LINPHONE_CALL_STATS_VIDEO)
*
*/
void linphone_reporting_call_stats_updated(LinphoneCall *call, int stats_type);
void linphone_reporting_on_rtcp_received(LinphoneCall *call, int stats_type);
#ifdef __cplusplus
}

View file

@ -91,6 +91,39 @@ SalStreamDescription *sal_media_description_find_stream(SalMediaDescription *md,
return NULL;
}
unsigned int sal_media_description_nb_active_streams_of_type(SalMediaDescription *md, SalStreamType type) {
unsigned int i;
unsigned int nb = 0;
for (i = 0; i < md->n_active_streams; ++i) {
if (md->streams[i].type == type) nb++;
}
return nb;
}
SalStreamDescription * sal_media_description_get_active_stream_of_type(SalMediaDescription *md, SalStreamType type, unsigned int idx) {
unsigned int i;
for (i = 0; i < md->n_active_streams; ++i) {
if (md->streams[i].type == type) {
if (idx-- == 0) return &md->streams[i];
}
}
return NULL;
}
SalStreamDescription * sal_media_description_find_secure_stream_of_type(SalMediaDescription *md, SalStreamType type) {
SalStreamDescription *desc = sal_media_description_find_stream(md, SalProtoRtpSavpf, type);
if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpSavp, type);
return desc;
}
SalStreamDescription * sal_media_description_find_best_stream(SalMediaDescription *md, SalStreamType type) {
SalStreamDescription *desc = sal_media_description_find_stream(md, SalProtoRtpSavpf, type);
if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpSavp, type);
if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpAvpf, type);
if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpAvp, type);
return desc;
}
bool_t sal_media_description_empty(const SalMediaDescription *md){
if (md->n_active_streams > 0) return FALSE;
return TRUE;
@ -515,6 +548,8 @@ const char* sal_media_proto_to_string(SalMediaProto type) {
switch (type) {
case SalProtoRtpAvp:return "RTP/AVP";
case SalProtoRtpSavp:return "RTP/SAVP";
case SalProtoRtpAvpf:return "RTP/AVPF";
case SalProtoRtpSavpf:return "RTP/SAVPF";
default: return "unknown";
}
}

View file

@ -1,115 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0"?>
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy project-wide -->
<object class="GtkDialog" id="call_statistics">
<property name="can_focus">False</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">Call statistics</property>
<property name="type_hint">dialog</property>
<signal name="response" handler="linphone_gtk_call_statistics_closed" swapped="no"/>
<signal name="response" handler="linphone_gtk_call_statistics_closed"/>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkHButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkButton" id="button1">
<property name="label">gtk-close</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="left_padding">12</property>
<child>
<object class="GtkTable" id="table1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">9</property>
<property name="n_rows">10</property>
<property name="n_columns">2</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkLabel" id="audio_codec_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Audio codec</property>
</object>
<packing>
<property name="x_options"></property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="video_codec_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Video codec</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"></property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Audio IP bandwidth usage</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"></property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="audio_codec">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="video_codec">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
@ -119,9 +71,8 @@
</packing>
</child>
<child>
<object class="GtkLabel" id="audio_bandwidth_usage">
<object class="GtkLabel" id="video_codec">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
@ -130,22 +81,53 @@
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="audio_bandwidth_usage">
<property name="visible">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Audio Media connectivity</property>
</object>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options"></property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="x_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="audio_media_connectivity">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">Video IP bandwidth usage</property>
</object>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="video_bandwidth_usage">
<property name="visible">True</property>
</object>
<packing>
<property name="left_attach">1</property>
@ -154,120 +136,106 @@
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Video IP bandwidth usage</property>
</object>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="video_bandwidth_usage">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Video Media connectivity</property>
</object>
<packing>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="video_media_connectivity">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Round trip time</property>
</object>
<packing>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="round_trip_time">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="video_size_recv_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Video resolution received</property>
</object>
<packing>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
<property name="top_attach">8</property>
<property name="bottom_attach">9</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="video_size_recv">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
<property name="top_attach">8</property>
<property name="bottom_attach">9</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="video_size_sent_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Video resolution sent</property>
</object>
<packing>
<property name="top_attach">8</property>
<property name="bottom_attach">9</property>
<property name="top_attach">9</property>
<property name="bottom_attach">10</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="video_size_sent">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">8</property>
<property name="bottom_attach">9</property>
<property name="top_attach">9</property>
<property name="bottom_attach">10</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="rtp_profile_label">
<property name="visible">True</property>
<property name="label" translatable="yes">RTP profile</property>
</object>
<packing>
<property name="x_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="rtp_profile">
<property name="visible">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
</object>
@ -277,7 +245,6 @@
<child type="label">
<object class="GtkLabel" id="call_statistics_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Call statistics and information&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
@ -289,6 +256,34 @@
<property name="position">1</property>
</packing>
</child>
<child internal-child="action_area">
<object class="GtkHButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="layout_style">end</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkButton" id="button1">
<property name="label">gtk-close</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
<action-widgets>

View file

@ -258,9 +258,12 @@ static void _refresh_call_stats(GtkWidget *callstats, LinphoneCall *call){
gboolean has_video=linphone_call_params_video_enabled(linphone_call_get_current_params(call));
MSVideoSize size_received = linphone_call_params_get_received_video_size(linphone_call_get_current_params(call));
MSVideoSize size_sent = linphone_call_params_get_sent_video_size(linphone_call_get_current_params(call));
gchar *tmp=g_strdup_printf(_("download: %f\nupload: %f (kbit/s)"),
const char *rtp_profile = linphone_call_params_get_rtp_profile(linphone_call_get_current_params(call));
gchar *tmp = g_strdup_printf("%s", rtp_profile);
gtk_label_set_markup(GTK_LABEL(linphone_gtk_get_widget(callstats,"rtp_profile")),tmp);
g_free(tmp);
tmp=g_strdup_printf(_("download: %f\nupload: %f (kbit/s)"),
as->download_bandwidth,as->upload_bandwidth);
gtk_label_set_markup(GTK_LABEL(linphone_gtk_get_widget(callstats,"audio_bandwidth_usage")),tmp);
g_free(tmp);
if (has_video){

View file

@ -1313,12 +1313,16 @@ static bool_t notify_actions_supported() {
return accepts_actions;
}
static NotifyNotification* build_notification(const char *title, const char *body){
return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
static NotifyNotification* build_notification(const char *title, const char *body) {
const char *icon_path = linphone_gtk_get_ui_config("icon", LINPHONE_ICON);
GdkPixbuf *pbuf = create_pixbuf(icon_path);
NotifyNotification *n = notify_notification_new(title, body, NULL
#ifdef HAVE_NOTIFY1
,NULL
,NULL
#endif
);
notify_notification_set_icon_from_pixbuf(n, pbuf);
return n;
}
static void show_notification(NotifyNotification* n){
@ -2269,6 +2273,9 @@ int main(int argc, char *argv[]){
}
}
#endif
add_pixmap_directory("pixmaps");
add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
/* Now, look for the factory configuration file, we do it this late
since we want to have had time to change directory and to parse
the options, in case we needed to access the working directory */
@ -2283,9 +2290,6 @@ int main(int argc, char *argv[]){
pbuf=create_pixbuf(icon_path);
if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
add_pixmap_directory("pixmaps");
add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
#ifdef HAVE_GTK_OSX
GtkosxApplication *theMacApp = gtkosx_application_get();
g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);

View file

@ -25,8 +25,8 @@ void linphone_gtk_fill_combo_box(GtkWidget *combo, const char **devices, const c
const char **p=devices;
int i=0,active=-1;
GtkTreeModel *model;
if ((model=gtk_combo_box_get_model(GTK_COMBO_BOX(combo)))==NULL){
/*case where combo box is created with no model*/
GtkCellRenderer *renderer=gtk_cell_renderer_text_new();
@ -40,7 +40,7 @@ void linphone_gtk_fill_combo_box(GtkWidget *combo, const char **devices, const c
unless we fill it with a dummy text.
This dummy text needs to be removed first*/
}
for(;*p!=NULL;++p){
if ( cap==CAP_IGNORE
|| (cap==CAP_CAPTURE && linphone_core_sound_device_can_capture(linphone_gtk_get_core(),*p))
@ -836,9 +836,9 @@ static void linphone_gtk_proxy_closed(GtkWidget *w){
static void fill_transport_combo_box(GtkWidget *combo, LinphoneTransportType choice, gboolean is_sensitive){
GtkTreeModel *model;
GtkTreeIter iter;
if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(combo),"combo-updating"))) return;
if ((model=gtk_combo_box_get_model(GTK_COMBO_BOX(combo)))==NULL){
/*case where combo box is created with no model*/
GtkCellRenderer *renderer=gtk_cell_renderer_text_new();
@ -882,9 +882,9 @@ void linphone_gtk_proxy_transport_changed(GtkWidget *combo){
const char *addr=gtk_entry_get_text(GTK_ENTRY(proxy));
LinphoneAddress *laddr;
LinphoneTransportType new_transport=(LinphoneTransportType)index;
if (index==-1) return;
g_object_set_data(G_OBJECT(w),"combo-updating",GINT_TO_POINTER(1));
laddr=linphone_address_new(addr);
if (laddr){
@ -922,6 +922,10 @@ void linphone_gtk_show_proxy_config(GtkWidget *pb, LinphoneProxyConfig *cfg){
linphone_proxy_config_register_enabled(cfg));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(w,"publish")),
linphone_proxy_config_publish_enabled(cfg));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(w,"avpf")),
linphone_proxy_config_avpf_enabled(cfg));
gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(w,"avpf_rr_interval")),
linphone_proxy_config_get_avpf_rr_interval(cfg));
}
g_object_set_data(G_OBJECT(w),"config",(gpointer)cfg);
g_object_set_data(G_OBJECT(w),"parameters",(gpointer)pb);
@ -941,7 +945,7 @@ void linphone_gtk_proxy_ok(GtkButton *button){
int index=gtk_combo_box_get_active(GTK_COMBO_BOX(linphone_gtk_get_widget(w,"transport")));
LinphoneTransportType tport=(LinphoneTransportType)index;
gboolean was_editing=TRUE;
if (!cfg){
was_editing=FALSE;
cfg=linphone_proxy_config_new();
@ -978,7 +982,13 @@ void linphone_gtk_proxy_ok(GtkButton *button){
linphone_proxy_config_enable_register(cfg,
gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(w,"register"))));
linphone_proxy_config_enable_avpf(cfg,
gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(w,"avpf"))));
linphone_proxy_config_set_avpf_rr_interval(cfg,
(int)gtk_spin_button_get_value(
GTK_SPIN_BUTTON(linphone_gtk_get_widget(w,"avpf_rr_interval"))));
/* check if tls was asked but is not enabled in transport configuration*/
if (tport==LinphoneTransportTls){
LCSipTransports tports;
@ -988,7 +998,7 @@ void linphone_gtk_proxy_ok(GtkButton *button){
}
linphone_core_set_sip_transports(lc,&tports);
}
if (was_editing){
if (linphone_proxy_config_done(cfg)==-1)
return;
@ -1203,15 +1213,15 @@ static void linphone_gtk_show_media_encryption(GtkWidget *pb){
GtkListStore *store;
GtkTreeIter iter;
GtkCellRenderer *renderer=gtk_cell_renderer_text_new();
model=GTK_TREE_MODEL((store=gtk_list_store_new(1,G_TYPE_STRING)));
gtk_combo_box_set_model(GTK_COMBO_BOX(combo),model);
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo),renderer,TRUE);
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo),renderer,"text",0,NULL);
gtk_list_store_append(store,&iter);
gtk_list_store_set(store,&iter,0,_("None"),-1);
if (linphone_core_media_encryption_supported(lc,LinphoneMediaEncryptionSRTP)){
gtk_list_store_append(store,&iter);
gtk_list_store_set(store,&iter,0,_("SRTP"),-1);
@ -1293,19 +1303,26 @@ void linphone_gtk_fill_video_renderers(GtkWidget *pb){
GtkTreeModel *model=GTK_TREE_MODEL(store=gtk_list_store_new(2,G_TYPE_STRING,G_TYPE_STRING));
if (current_renderer==NULL) current_renderer=video_stream_get_default_video_renderer();
gtk_combo_box_set_model(GTK_COMBO_BOX(combo),model);
gtk_cell_layout_clear(GTK_CELL_LAYOUT(combo));
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo),renderer,TRUE);
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo),renderer,"text",1,NULL);
for(i=0,elem=l;elem!=NULL && i<4 ;elem=elem->next,++i){
for(i=0,elem=l;elem!=NULL && i<4 ;elem=elem->next){
MSFilterDesc *desc=(MSFilterDesc *)elem->data;
GtkTreeIter iter;
/* do not offer the user to select combo 'decoding/rendering' filter */
if (desc->enc_fmt != NULL)
continue;
gtk_list_store_append(store,&iter);
gtk_list_store_set(store,&iter,0,desc->name,1,desc->text,-1);
if (current_renderer && strcmp(current_renderer,desc->name)==0)
active=i;
i++;
}
ms_list_free(l);
if (active!=-1) gtk_combo_box_set_active(GTK_COMBO_BOX(combo),active);
@ -1386,7 +1403,7 @@ void linphone_gtk_show_parameters(void){
tr.udp_port);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"sip_tcp_port")),
tr.tcp_port);
linphone_core_get_audio_port_range(lc, &min_port, &max_port);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb, "audio_min_rtp_port")), min_port);
@ -1404,7 +1421,7 @@ void linphone_gtk_show_parameters(void){
}
linphone_gtk_show_media_encryption(pb);
tmp=linphone_core_get_nat_address(lc);
if (tmp) gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(pb,"nat_address")),tmp);
tmp=linphone_core_get_stun_server(lc);
@ -1466,6 +1483,8 @@ void linphone_gtk_show_parameters(void){
}
#ifdef BUILD_WIZARD
gtk_widget_show(linphone_gtk_get_widget(pb,"wizard"));
#else
gtk_widget_hide(linphone_gtk_get_widget(pb,"wizard"));
#endif
linphone_gtk_show_sip_accounts(pb);
/* CODECS CONFIG */
@ -1548,9 +1567,9 @@ void linphone_gtk_edit_tunnel(GtkButton *button){
const MSList *configs;
const char *host = NULL;
int port=0;
if (!tunnel) return;
configs = linphone_tunnel_get_servers(tunnel);
if(configs != NULL) {
LinphoneTunnelConfig *ltc = (LinphoneTunnelConfig *)configs->data;
@ -1598,7 +1617,7 @@ void linphone_gtk_tunnel_ok(GtkButton *button){
gint http_port = (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(w,"http_port")));
const char *username=gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(w,"username")));
const char *password=gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(w,"password")));
if (tunnel==NULL) return;
if (host && *host=='\0') host=NULL;
if (http_port==0) http_port=8080;
@ -1608,7 +1627,7 @@ void linphone_gtk_tunnel_ok(GtkButton *button){
linphone_tunnel_add_server(tunnel, config);
linphone_tunnel_enable(tunnel,enabled);
linphone_tunnel_set_http_proxy(tunnel,http_host,http_port,username,password);
gtk_widget_destroy(w);
}
@ -1621,7 +1640,7 @@ static void show_dscp(GtkWidget *entry, int val){
char tmp[20];
snprintf(tmp,sizeof(tmp),"0x%x",val);
gtk_entry_set_text(GTK_ENTRY(entry),tmp);
}
static int read_dscp(GtkWidget *entry){
@ -1660,7 +1679,7 @@ void linphone_gtk_dscp_edit_response(GtkWidget *dialog, guint response_id){
read_dscp(linphone_gtk_get_widget(dialog,"audio_dscp")));
linphone_core_set_video_dscp(lc,
read_dscp(linphone_gtk_get_widget(dialog,"video_dscp")));
break;
default:
break;

View file

@ -8,6 +8,13 @@
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="adjustment2">
<property name="upper">5</property>
<property name="lower">1</property>
<property name="value">5</property>
<property name="step_increment">1</property>
<property name="page_increment">1</property>
</object>
<object class="GtkDialog" id="sip_account">
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
@ -235,6 +242,37 @@
<property name="bottom_attach">6</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="avpf_rr_interval_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">AVPF regular RTCP interval (sec):</property>
<property name="justify">right</property>
</object>
<packing>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="avpf_rr_interval">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="adjustment">adjustment2</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label40">
<property name="visible">True</property>
@ -311,6 +349,22 @@
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="avpf">
<property name="label" translatable="yes">Enable AVPF</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
</child>
</object>

View file

@ -123,6 +123,8 @@ const char* sal_stream_type_to_string(SalStreamType type);
typedef enum{
SalProtoRtpAvp,
SalProtoRtpSavp,
SalProtoRtpAvpf,
SalProtoRtpSavpf,
SalProtoOther
}SalMediaProto;
const char* sal_media_proto_to_string(SalMediaProto type);
@ -166,7 +168,7 @@ typedef struct SalIceRemoteCandidate {
#define SAL_MEDIA_DESCRIPTION_MAX_ICE_PWD_LEN 256
/*sufficient for 256bit keys encoded in base 64*/
#define SAL_SRTP_KEY_SIZE 64
#define SAL_SRTP_KEY_SIZE 128
typedef struct SalSrtpCryptoAlgo {
unsigned int tag;
@ -252,6 +254,10 @@ int sal_media_description_equals(const SalMediaDescription *md1, const SalMediaD
bool_t sal_media_description_has_dir(const SalMediaDescription *md, SalStreamDir dir);
SalStreamDescription *sal_media_description_find_stream(SalMediaDescription *md,
SalMediaProto proto, SalStreamType type);
unsigned int sal_media_description_nb_active_streams_of_type(SalMediaDescription *md, SalStreamType type);
SalStreamDescription * sal_media_description_get_active_stream_of_type(SalMediaDescription *md, SalStreamType type, unsigned int idx);
SalStreamDescription * sal_media_description_find_secure_stream_of_type(SalMediaDescription *md, SalStreamType type);
SalStreamDescription * sal_media_description_find_best_stream(SalMediaDescription *md, SalStreamType type);
void sal_media_description_set_dir(SalMediaDescription *md, SalStreamDir stream_dir);
@ -514,6 +520,9 @@ void sal_set_keepalive_period(Sal *ctx,unsigned int value);
void sal_use_tcp_tls_keepalive(Sal *ctx, bool_t enabled);
int sal_enable_tunnel(Sal *ctx, void *tunnelclient);
void sal_disable_tunnel(Sal *ctx);
/*Default value is true*/
void sal_enable_sip_update_method(Sal *ctx,bool_t value);
/**
* returns keepalive period in ms
* 0 desactiaved
@ -552,6 +561,9 @@ void sal_op_set_to_address(SalOp *op, const SalAddress *to);
SalOp *sal_op_ref(SalOp* h);
void sal_op_stop_refreshing(SalOp *op);
void sal_op_release(SalOp *h);
/*same as release, but does not stop refresher if any*/
void* sal_op_unref(SalOp* op);
void sal_op_authenticate(SalOp *h, const SalAuthInfo *info);
void sal_op_cancel_authentication(SalOp *h);
void sal_op_set_user_pointer(SalOp *h, void *up);

View file

@ -114,4 +114,9 @@ public interface LinphoneChatRoom {
* @return LinphoneChatMessage object
*/
LinphoneChatMessage createLinphoneChatMessage(String message, String url, State state, long timestamp, boolean isRead, boolean isIncoming);
/**
* Returns a back pointer to the core managing the chat room.
* @return the LinphoneCore
*/
LinphoneCore getCore();
}

View file

@ -100,4 +100,10 @@ public interface LinphoneEvent {
* @param body the new data to be published
*/
void sendPublish(LinphoneContent body);
/**
* Get a back pointer to the LinphoneCore object managing this LinphoneEvent.
* @return
*/
LinphoneCore getCore();
}

View file

@ -163,6 +163,29 @@ public interface LinphoneProxyConfig {
*/
int getPrivacy();
/**
* Indicates whether AVPF/SAVPF must be used for calls using this proxy config.
* @param enable True to enable AVPF/SAVF, false to disable it.
*/
void enableAvpf(boolean enable);
/**
* Set the interval between regular RTCP reports when using AVPF/SAVPF.
* @param interval The interval in seconds (between 0 and 5 seconds).
*/
void setAvpfRRInterval(int interval);
/**
* Get the interval between regular RTCP reports when using AVPF/SAVPF.
* @return The interval in seconds.
*/
int getAvpfRRInterval();
/**
* Whether AVPF is used for calls through this proxy.
* @return
*/
boolean avpfEnabled();
/**
* Set optional contact parameters that will be added to the contact information sent in the registration.

View file

@ -44,79 +44,110 @@ class LinphoneChatRoomImpl implements LinphoneChatRoom {
nativePtr = aNativePtr;
}
public LinphoneAddress getPeerAddress() {
public synchronized LinphoneAddress getPeerAddress() {
return new LinphoneAddressImpl(getPeerAddress(nativePtr),LinphoneAddressImpl.WrapMode.FromConst);
}
public void sendMessage(String message) {
sendMessage(nativePtr,message);
}
@Override
public void sendMessage(LinphoneChatMessage message, StateListener listener) {
sendMessage2(nativePtr, message, ((LinphoneChatMessageImpl)message).getNativePtr(), listener);
}
@Override
public LinphoneChatMessage createLinphoneChatMessage(String message) {
return new LinphoneChatMessageImpl(createLinphoneChatMessage(nativePtr, message));
}
public LinphoneChatMessage[] getHistory() {
return getHistory(0);
}
public LinphoneChatMessage[] getHistory(int limit) {
long[] typesPtr = getHistory(nativePtr, limit);
if (typesPtr == null) return null;
LinphoneChatMessage[] messages = new LinphoneChatMessage[typesPtr.length];
for (int i=0; i < messages.length; i++) {
messages[i] = new LinphoneChatMessageImpl(typesPtr[i]);
public synchronized void sendMessage(String message) {
synchronized(getCore()){
sendMessage(nativePtr,message);
}
return messages;
}
public void destroy() {
@Override
public synchronized void sendMessage(LinphoneChatMessage message, StateListener listener) {
synchronized(getCore()){
sendMessage2(nativePtr, message, ((LinphoneChatMessageImpl)message).getNativePtr(), listener);
}
}
@Override
public synchronized LinphoneChatMessage createLinphoneChatMessage(String message) {
synchronized(getCore()){
return new LinphoneChatMessageImpl(createLinphoneChatMessage(nativePtr, message));
}
}
public synchronized LinphoneChatMessage[] getHistory() {
synchronized(getCore()){
return getHistory(0);
}
}
public synchronized LinphoneChatMessage[] getHistory(int limit) {
synchronized(getCore()){
long[] typesPtr = getHistory(nativePtr, limit);
if (typesPtr == null) return null;
LinphoneChatMessage[] messages = new LinphoneChatMessage[typesPtr.length];
for (int i=0; i < messages.length; i++) {
messages[i] = new LinphoneChatMessageImpl(typesPtr[i]);
}
return messages;
}
}
public synchronized void destroy() {
destroy(nativePtr);
}
public int getUnreadMessagesCount() {
return getUnreadMessagesCount(nativePtr);
public synchronized int getUnreadMessagesCount() {
synchronized(getCore()){
return getUnreadMessagesCount(nativePtr);
}
}
public void deleteHistory() {
deleteHistory(nativePtr);
public synchronized void deleteHistory() {
synchronized(getCore()){
deleteHistory(nativePtr);
}
}
public void compose() {
compose(nativePtr);
public synchronized void compose() {
synchronized(getCore()){
compose(nativePtr);
}
}
public boolean isRemoteComposing() {
return isRemoteComposing(nativePtr);
public synchronized boolean isRemoteComposing() {
synchronized(getCore()){
return isRemoteComposing(nativePtr);
}
}
public void markAsRead() {
markAsRead(nativePtr);
public synchronized void markAsRead() {
synchronized(getCore()){
markAsRead(nativePtr);
}
}
public void deleteMessage(LinphoneChatMessage message) {
if (message != null)
deleteMessage(nativePtr, ((LinphoneChatMessageImpl)message).getNativePtr());
public synchronized void deleteMessage(LinphoneChatMessage message) {
synchronized(getCore()){
if (message != null)
deleteMessage(nativePtr, ((LinphoneChatMessageImpl)message).getNativePtr());
}
}
public void updateUrl(LinphoneChatMessage message) {
if (message != null)
updateUrl(nativePtr, ((LinphoneChatMessageImpl)message).getNativePtr());
public synchronized void updateUrl(LinphoneChatMessage message) {
synchronized(getCore()){
if (message != null)
updateUrl(nativePtr, ((LinphoneChatMessageImpl)message).getNativePtr());
}
}
@Override
public LinphoneChatMessage createLinphoneChatMessage(String message,
public synchronized LinphoneChatMessage createLinphoneChatMessage(String message,
String url, State state, long timestamp, boolean isRead,
boolean isIncoming) {
return new LinphoneChatMessageImpl(createLinphoneChatMessage2(
nativePtr, message, url, state.value(), timestamp / 1000, isRead, isIncoming));
synchronized(getCore()){
return new LinphoneChatMessageImpl(createLinphoneChatMessage2(
nativePtr, message, url, state.value(), timestamp / 1000, isRead, isIncoming));
}
}
private native Object getCore(long nativePtr);
@Override
public synchronized LinphoneCore getCore() {
return (LinphoneCore)getCore(nativePtr);
}
}

View file

@ -610,13 +610,13 @@ class LinphoneCoreImpl implements LinphoneCore {
public synchronized void enableEchoLimiter(boolean val) {
enableEchoLimiter(nativePtr,val);
}
public void setVideoDevice(int id) {
public synchronized void setVideoDevice(int id) {
Log.i("Setting camera id :", id);
if (setVideoDevice(nativePtr, id) != 0) {
Log.e("Failed to set video device to id:", id);
}
}
public int getVideoDevice() {
public synchronized int getVideoDevice() {
return getVideoDevice(nativePtr);
}
@ -847,7 +847,7 @@ class LinphoneCoreImpl implements LinphoneCore {
private native void tunnelSetHttpProxy(long nativePtr, String proxy_host, int port,
String username, String password);
@Override
public void tunnelSetHttpProxy(String proxy_host, int port,
public synchronized void tunnelSetHttpProxy(String proxy_host, int port,
String username, String password) {
tunnelSetHttpProxy(nativePtr, proxy_host, port, username, password);
}
@ -1185,17 +1185,17 @@ class LinphoneCoreImpl implements LinphoneCore {
}
@Override
public void stopRinging() {
public synchronized void stopRinging() {
stopRinging(nativePtr);
}
private native void setPayloadTypeBitrate(long coreptr, long payload_ptr, int bitrate);
@Override
public void setPayloadTypeBitrate(PayloadType pt, int bitrate) {
public synchronized void setPayloadTypeBitrate(PayloadType pt, int bitrate) {
setPayloadTypeBitrate(nativePtr, ((PayloadTypeImpl)pt).nativePtr, bitrate);
}
private native int getPayloadTypeBitrate(long coreptr, long payload_ptr);
@Override
public int getPayloadTypeBitrate(PayloadType pt) {
public synchronized int getPayloadTypeBitrate(PayloadType pt) {
return getPayloadTypeBitrate(nativePtr, ((PayloadTypeImpl)pt).nativePtr);
}

View file

@ -11,71 +11,83 @@ public class LinphoneEventImpl implements LinphoneEvent {
private native String getEventName(long nativeptr);
@Override
public String getEventName() {
public synchronized String getEventName() {
return getEventName(mNativePtr);
}
private native int acceptSubscription(long nativeptr);
@Override
public void acceptSubscription() {
acceptSubscription(mNativePtr);
public synchronized void acceptSubscription() {
synchronized(getCore()){
acceptSubscription(mNativePtr);
}
}
private native int denySubscription(long nativeptr, int reason);
@Override
public void denySubscription(Reason reason) {
denySubscription(mNativePtr,reason.mValue);
public synchronized void denySubscription(Reason reason) {
synchronized(getCore()){
denySubscription(mNativePtr,reason.mValue);
}
}
private native int notify(long nativeptr, String type, String subtype, byte data[], String encoding);
@Override
public void notify(LinphoneContent content) {
notify(mNativePtr,content.getType(),content.getSubtype(),content.getData(),content.getEncoding());
public synchronized void notify(LinphoneContent content) {
synchronized(getCore()){
notify(mNativePtr,content.getType(),content.getSubtype(),content.getData(),content.getEncoding());
}
}
private native int updateSubscribe(long nativePtr, String type, String subtype, byte data[], String encoding);
@Override
public void updateSubscribe(LinphoneContent content) {
updateSubscribe(mNativePtr,content.getType(), content.getSubtype(),content.getData(),content.getEncoding());
public synchronized void updateSubscribe(LinphoneContent content) {
synchronized(getCore()){
updateSubscribe(mNativePtr,content.getType(), content.getSubtype(),content.getData(),content.getEncoding());
}
}
private native int updatePublish(long nativePtr, String type, String subtype, byte data[], String encoding);
@Override
public void updatePublish(LinphoneContent content) {
updatePublish(mNativePtr,content.getType(), content.getSubtype(),content.getData(),content.getEncoding());
public synchronized void updatePublish(LinphoneContent content) {
synchronized(getCore()){
updatePublish(mNativePtr,content.getType(), content.getSubtype(),content.getData(),content.getEncoding());
}
}
private native int terminate(long nativePtr);
@Override
public void terminate() {
terminate(mNativePtr);
public synchronized void terminate() {
synchronized(getCore()){
terminate(mNativePtr);
}
}
private native int getReason(long nativePtr);
@Override
public Reason getReason() {
public synchronized Reason getReason() {
return Reason.fromInt(getReason(mNativePtr));
}
@Override
public void setUserContext(Object obj) {
public synchronized void setUserContext(Object obj) {
mUserContext=obj;
}
@Override
public Object getUserContext() {
public synchronized Object getUserContext() {
return mUserContext;
}
private native int getSubscriptionDir(long nativeptr);
@Override
public SubscriptionDir getSubscriptionDir() {
public synchronized SubscriptionDir getSubscriptionDir() {
return SubscriptionDir.fromInt(getSubscriptionDir(mNativePtr));
}
private native int getSubscriptionState(long nativeptr);
@Override
public SubscriptionState getSubscriptionState() {
public synchronized SubscriptionState getSubscriptionState() {
try {
return SubscriptionState.fromInt(getSubscriptionState(mNativePtr));
} catch (LinphoneCoreException e) {
@ -91,37 +103,47 @@ public class LinphoneEventImpl implements LinphoneEvent {
private native void addCustomHeader(long ptr, String name, String value);
@Override
public void addCustomHeader(String name, String value) {
public synchronized void addCustomHeader(String name, String value) {
addCustomHeader(mNativePtr, name, value);
}
private native String getCustomHeader(long ptr, String name);
@Override
public String getCustomHeader(String name) {
public synchronized String getCustomHeader(String name) {
return getCustomHeader(mNativePtr, name);
}
private native void sendSubscribe(long ptr, String type, String subtype, byte data [], String encoding);
@Override
public void sendSubscribe(LinphoneContent body) {
if (body != null)
sendSubscribe(mNativePtr, body.getType(), body.getSubtype(), body.getData(), body.getEncoding());
else
sendSubscribe(mNativePtr, null, null, null, null);
public synchronized void sendSubscribe(LinphoneContent body) {
synchronized(getCore()){
if (body != null)
sendSubscribe(mNativePtr, body.getType(), body.getSubtype(), body.getData(), body.getEncoding());
else
sendSubscribe(mNativePtr, null, null, null, null);
}
}
private native void sendPublish(long ptr, String type, String subtype, byte data [], String encoding);
@Override
public void sendPublish(LinphoneContent body) {
if (body != null)
sendPublish(mNativePtr, body.getType(), body.getSubtype(), body.getData(), body.getEncoding());
else
sendPublish(mNativePtr, null, null, null, null);
public synchronized void sendPublish(LinphoneContent body) {
synchronized(getCore()){
if (body != null)
sendPublish(mNativePtr, body.getType(), body.getSubtype(), body.getData(), body.getEncoding());
else
sendPublish(mNativePtr, null, null, null, null);
}
}
private native long getErrorInfo(long nativePtr);
@Override
public ErrorInfo getErrorInfo() {
public synchronized ErrorInfo getErrorInfo() {
return new ErrorInfoImpl(getErrorInfo(mNativePtr));
}
private native Object getCore(long nativePtr);
@Override
public synchronized LinphoneCore getCore() {
return (LinphoneCore)getCore(mNativePtr);
}
}

View file

@ -198,6 +198,30 @@ class LinphoneProxyConfigImpl implements LinphoneProxyConfig {
return getPrivacy(nativePtr);
}
private native void enableAvpf(long nativePtr, boolean enable);
@Override
public void enableAvpf(boolean enable) {
enableAvpf(nativePtr, enable);
}
private native boolean avpfEnabled(long nativePtr);
@Override
public boolean avpfEnabled() {
return avpfEnabled(nativePtr);
}
private native void setAvpfRRInterval(long nativePtr, int interval);
@Override
public void setAvpfRRInterval(int interval) {
setAvpfRRInterval(nativePtr, interval);
}
private native int getAvpfRRInterval(long nativePtr);
@Override
public int getAvpfRRInterval() {
return getAvpfRRInterval(nativePtr);
}
private native String getContactParameters(long ptr);
@Override
public String getContactParameters() {

@ -1 +1 @@
Subproject commit fc3a434df7845f20e0fc3415f25bbad4598fe3be
Subproject commit 0aee9ceeaa0f766c8d0e15ee2bcb82e06c7c541d

2
oRTP

@ -1 +1 @@
Subproject commit c93363ac023c2122bfdfb8b0d99b811dffbad827
Subproject commit 9d85ca0e1a117a2fbfb02de8df3b19bd5eb5db81

View file

@ -26,7 +26,7 @@ liblinphonetester_la_LDFLAGS= -no-undefined
liblinphonetester_la_LIBADD= ../coreapi/liblinphone.la $(CUNIT_LIBS)
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/coreapi
AM_CFLAGS = $(STRICT_OPTIONS) -DIN_LINPHONE $(ORTP_CFLAGS) $(MEDIASTREAMER_CFLAGS) $(CUNIT_CFLAGS) $(BELLESIP_CFLAGS) $(LIBXML2_CFLAGS)
AM_CFLAGS = $(STRICT_OPTIONS) -DIN_LINPHONE $(ORTP_CFLAGS) $(MEDIASTREAMER_CFLAGS) $(CUNIT_CFLAGS) $(BELLESIP_CFLAGS) $(LIBXML2_CFLAGS) $(SQLITE3_CFLAGS)
if !BUILD_IOS

View file

@ -259,17 +259,17 @@ static void call_with_specified_codec_bitrate(void) {
ms_warning("opus codec not supported, test skipped.");
goto end;
}
disable_all_codecs_except_one(marie->lc,"opus");
disable_all_codecs_except_one(pauline->lc,"opus");
linphone_core_set_payload_type_bitrate(marie->lc,
linphone_core_find_payload_type(marie->lc,"opus",48000,-1),
50);
linphone_core_set_payload_type_bitrate(pauline->lc,
linphone_core_find_payload_type(pauline->lc,"opus",48000,-1),
24);
CU_ASSERT_TRUE((call_ok=call(pauline,marie)));
if (!call_ok) goto end;
liblinphone_tester_check_rtcp(marie,pauline);
@ -552,20 +552,23 @@ static void call_with_no_sdp(void) {
static bool_t check_ice(LinphoneCoreManager* caller, LinphoneCoreManager* callee, LinphoneIceState state) {
LinphoneCall *c1,*c2;
bool_t success=FALSE;
bool_t audio_success=FALSE;
bool_t video_success=FALSE;
int i;
bool_t video_enabled;
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);
CU_ASSERT_EQUAL(linphone_call_params_video_enabled(linphone_call_get_current_params(c1)),linphone_call_params_video_enabled(linphone_call_get_current_params(c2)));
video_enabled=linphone_call_params_video_enabled(linphone_call_get_current_params(c1));
for (i=0;i<200;i++){
if ((c1 != NULL) && (c2 != NULL)) {
if (linphone_call_get_audio_stats(c1)->ice_state==LinphoneIceStateHostConnection &&
linphone_call_get_audio_stats(c2)->ice_state==LinphoneIceStateHostConnection ){
success=TRUE;
if (linphone_call_get_audio_stats(c1)->ice_state==state &&
linphone_call_get_audio_stats(c2)->ice_state==state ){
audio_success=TRUE;
break;
}
linphone_core_iterate(caller->lc);
@ -574,6 +577,21 @@ static bool_t check_ice(LinphoneCoreManager* caller, LinphoneCoreManager* callee
ms_usleep(50000);
}
if (video_enabled){
for (i=0;i<200;i++){
if ((c1 != NULL) && (c2 != NULL)) {
if (linphone_call_get_video_stats(c1)->ice_state==state &&
linphone_call_get_video_stats(c2)->ice_state==state ){
video_success=TRUE;
break;
}
linphone_core_iterate(caller->lc);
linphone_core_iterate(callee->lc);
}
ms_usleep(50000);
}
}
/*make sure encryption mode are preserved*/
if (c1) {
const LinphoneCallParams* call_param = linphone_call_get_current_params(c1);
@ -584,7 +602,7 @@ static bool_t check_ice(LinphoneCoreManager* caller, LinphoneCoreManager* callee
CU_ASSERT_EQUAL(linphone_call_params_get_media_encryption(call_param),linphone_core_get_media_encryption(callee->lc));
}
return success;
return video_enabled ? audio_success && video_success : audio_success;
}
static void _call_with_ice_base(LinphoneCoreManager* pauline,LinphoneCoreManager* marie, bool_t caller_with_ice, bool_t callee_with_ice, bool_t random_ports) {
@ -645,9 +663,9 @@ static void call_with_ice_no_sdp(void){
linphone_core_set_firewall_policy(pauline->lc,LinphonePolicyUseIce);
linphone_core_set_stun_server(pauline->lc,"stun.linphone.org");
call(pauline,marie);
liblinphone_tester_check_rtcp(marie,pauline);
linphone_core_manager_destroy(marie);
@ -950,6 +968,9 @@ static void video_call_base(LinphoneCoreManager* pauline,LinphoneCoreManager* ma
marie_call=linphone_core_get_current_call(marie->lc);
pauline_call=linphone_core_get_current_call(pauline->lc);
linphone_call_params_destroy(caller_params);
linphone_call_params_destroy(callee_params);
if (marie_call && pauline_call ) {
CU_ASSERT_TRUE(linphone_call_log_video_enabled(linphone_call_get_call_log(marie_call)));
CU_ASSERT_TRUE(linphone_call_log_video_enabled(linphone_call_get_call_log(pauline_call)));
@ -982,6 +1003,54 @@ static void video_call_no_sdp(void) {
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
static void call_with_ice_video_to_novideo(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
LinphoneVideoPolicy vpol={0};
vpol.automatically_initiate=TRUE;
linphone_core_set_video_policy(pauline->lc,&vpol);
vpol.automatically_initiate=FALSE;
linphone_core_set_video_policy(marie->lc,&vpol);
_call_with_ice_base(pauline,marie,TRUE,TRUE,TRUE);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
static void call_with_ice_video_added(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
LinphoneVideoPolicy vpol={0};
linphone_core_set_video_policy(pauline->lc,&vpol);
linphone_core_set_video_policy(marie->lc,&vpol);
linphone_core_set_firewall_policy(marie->lc,LinphonePolicyUseIce);
linphone_core_set_stun_server(marie->lc,"stun.linphone.org");
linphone_core_set_firewall_policy(pauline->lc,LinphonePolicyUseIce);
linphone_core_set_stun_server(pauline->lc,"stun.linphone.org");
if (1){
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(check_ice(pauline,marie,LinphoneIceStateHostConnection));
/*wait for ICE reINVITEs to complete*/
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,2)
&&
wait_for(pauline->lc,pauline->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,2));
CU_ASSERT_TRUE(add_video(pauline,marie));
CU_ASSERT_TRUE(check_ice(pauline,marie,LinphoneIceStateHostConnection));
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
#endif /*VIDEO_ENABLED*/
static void _call_with_media_relay(bool_t random_ports) {
@ -1200,7 +1269,8 @@ static void call_waiting_indication_with_param(bool_t enable_caller_privacy) {
if (pauline_called_by_laure && enable_caller_privacy )
CU_ASSERT_EQUAL(linphone_call_params_get_privacy(linphone_call_get_current_params(pauline_called_by_laure)),LinphonePrivacyId);
/*wait a bit for ACK to be sent*/
wait_for_list(lcs,NULL,0,1000);
linphone_core_terminate_all_calls(pauline->lc);
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,2000));
@ -1221,10 +1291,8 @@ static void call_waiting_indication_with_privacy(void) {
call_waiting_indication_with_param(TRUE);
}
static void simple_conference(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
LinphoneCoreManager* laure = linphone_core_manager_new( "laure_rc");
static void simple_conference_base(LinphoneCoreManager* marie, LinphoneCoreManager* pauline, LinphoneCoreManager* laure) {
stats initial_marie_stat;
stats initial_pauline_stat;
stats initial_laure_stat;
@ -1266,6 +1334,18 @@ static void simple_conference(void) {
CU_ASSERT_TRUE(linphone_core_is_in_conference(marie->lc));
CU_ASSERT_EQUAL(linphone_core_get_conference_size(marie->lc),3)
/*
* FIXME: check_ice cannot work as it is today because there is no current call for the party that hosts the conference
if (linphone_core_get_firewall_policy(marie->lc) == LinphonePolicyUseIce) {
if (linphone_core_get_firewall_policy(pauline->lc) == LinphonePolicyUseIce) {
check_ice(marie,pauline,LinphoneIceStateHostConnection);
}
if (linphone_core_get_firewall_policy(laure->lc) == LinphonePolicyUseIce) {
check_ice(marie,laure,LinphoneIceStateHostConnection);
}
}
*/
linphone_core_terminate_conference(marie->lc);
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,2000));
@ -1273,10 +1353,35 @@ static void simple_conference(void) {
CU_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallEnd,1,2000));
ms_list_free(lcs);
}
static void simple_conference(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
LinphoneCoreManager* laure = linphone_core_manager_new( "laure_rc");
simple_conference_base(marie,pauline,laure);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
linphone_core_manager_destroy(laure);
}
static void simple_conference_with_ice(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
LinphoneCoreManager* laure = linphone_core_manager_new( "laure_rc");
linphone_core_set_firewall_policy(marie->lc,LinphonePolicyUseIce);
linphone_core_set_stun_server(marie->lc,"stun.linphone.org");
linphone_core_set_firewall_policy(pauline->lc,LinphonePolicyUseIce);
linphone_core_set_stun_server(pauline->lc,"stun.linphone.org");
linphone_core_set_firewall_policy(laure->lc,LinphonePolicyUseIce);
linphone_core_set_stun_server(laure->lc,"stun.linphone.org");
simple_conference_base(marie,pauline,laure);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
linphone_core_manager_destroy(laure);
ms_list_free(lcs);
}
static void srtp_call() {
@ -2034,7 +2139,7 @@ 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(
void create_call_for_quality_reporting_tests(
LinphoneCoreManager* marie,
LinphoneCoreManager* pauline,
LinphoneCall** call_marie,
@ -2046,20 +2151,20 @@ void create_call_for_statistics_tests(
CU_ASSERT_PTR_NOT_NULL(*call_pauline);
}
static void statistics_not_used_without_config() {
static void quality_reporting_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);
create_call_for_quality_reporting_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_TRUE(linphone_proxy_config_quality_reporting_enabled(call_marie->dest_proxy));
CU_ASSERT_FALSE(linphone_proxy_config_quality_reporting_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);
linphone_proxy_config_get_quality_reporting_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);
@ -2071,7 +2176,7 @@ static void statistics_not_used_without_config() {
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
static void statistics_not_sent_if_call_not_started() {
static void quality_reporting_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;
@ -2099,24 +2204,25 @@ static void statistics_not_sent_if_call_not_started() {
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
static void statistics_sent_at_call_termination() {
// int return_code = -1;
static void quality_reporting_at_call_termination() {
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);
create_call_for_quality_reporting_tests(marie, pauline, &call_marie, &call_pauline);
linphone_core_terminate_all_calls(marie->lc);
// now dialog id should be filled
CU_ASSERT_PTR_NOT_NULL(call_marie->log->reports[0]->dialog_id);
CU_ASSERT_TRUE(wait_for_until(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallReleased,1, 10000));
CU_ASSERT_TRUE(wait_for_until(pauline->lc,NULL,&pauline->stat.number_of_LinphoneCallReleased,1, 10000));
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));
@ -2126,7 +2232,108 @@ static void statistics_sent_at_call_termination() {
linphone_core_manager_destroy(pauline);
}
static void quality_reporting_interval_report() {
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_quality_reporting_tests(marie, pauline, &call_marie, &call_pauline);
linphone_proxy_config_set_quality_reporting_interval(call_marie->dest_proxy, 3);
CU_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(marie->lc));
CU_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(pauline->lc));
// PUBLISH submission to the collector should be ok
CU_ASSERT_TRUE(wait_for_until(marie->lc,pauline->lc,&marie->stat.number_of_LinphonePublishProgress,3,25000));
CU_ASSERT_TRUE(wait_for_until(marie->lc,pauline->lc,&marie->stat.number_of_LinphonePublishOk,3,25000));
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
#ifdef VIDEO_ENABLED
/*this is call forking with early media managed at client side (not by flexisip server)*/
static void multiple_early_media(void) {
LinphoneCoreManager* marie1 = linphone_core_manager_new("marie_early_rc");
LinphoneCoreManager* marie2 = linphone_core_manager_new("marie_early_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_tcp_rc");
MSList *lcs=NULL;
LinphoneCallParams *params=linphone_core_create_default_call_parameters(pauline->lc);
LinphoneVideoPolicy pol;
LinphoneCall *marie1_call;
LinphoneCall *marie2_call;
LinphoneCall *pauline_call;
int dummy=0;
char ringbackpath[256];
snprintf(ringbackpath,sizeof(ringbackpath), "%s/sounds/hello8000.wav" /*use hello because rinback is too short*/, liblinphone_tester_file_prefix);
pol.automatically_accept=1;
pol.automatically_initiate=1;
linphone_core_enable_video(pauline->lc,TRUE,TRUE);
linphone_core_enable_video(marie1->lc,TRUE,TRUE);
linphone_core_set_video_policy(marie1->lc,&pol);
/*use playfile for marie1 to avoid locking on capture card*/
linphone_core_use_files(marie1->lc,TRUE);
linphone_core_set_play_file(marie1->lc,ringbackpath);
linphone_core_enable_video(marie2->lc,TRUE,TRUE);
linphone_core_set_video_policy(marie2->lc,&pol);
linphone_core_set_audio_port_range(marie2->lc,40200,40300);
linphone_core_set_video_port_range(marie2->lc,40400,40500);
/*use playfile for marie2 to avoid locking on capture card*/
linphone_core_use_files(marie2->lc,TRUE);
linphone_core_set_play_file(marie2->lc,ringbackpath);
lcs=ms_list_append(lcs,marie1->lc);
lcs=ms_list_append(lcs,marie2->lc);
lcs=ms_list_append(lcs,pauline->lc);
linphone_call_params_enable_early_media_sending(params,TRUE);
linphone_call_params_enable_video(params,TRUE);
linphone_core_invite_address_with_params(pauline->lc,marie1->identity,params);
linphone_call_params_destroy(params);
CU_ASSERT_TRUE(wait_for_list(lcs, &marie1->stat.number_of_LinphoneCallIncomingEarlyMedia,1,3000));
CU_ASSERT_TRUE(wait_for_list(lcs, &marie2->stat.number_of_LinphoneCallIncomingEarlyMedia,1,3000));
CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallOutgoingEarlyMedia,1,3000));
pauline_call=linphone_core_get_current_call(pauline->lc);
marie1_call=linphone_core_get_current_call(marie1->lc);
marie2_call=linphone_core_get_current_call(marie2->lc);
/*wait a bit that streams are established*/
wait_for_list(lcs,&dummy,1,6000);
CU_ASSERT_TRUE(linphone_call_get_audio_stats(pauline_call)->download_bandwidth>70);
CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie1_call)->download_bandwidth>70);
CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie2_call)->download_bandwidth>70);
linphone_core_accept_call(marie1->lc,linphone_core_get_current_call(marie1->lc));
CU_ASSERT_TRUE(wait_for_list(lcs,&marie1->stat.number_of_LinphoneCallStreamsRunning,1,3000));
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallStreamsRunning,1,3000));
/*marie2 should get her call terminated*/
CU_ASSERT_TRUE(wait_for_list(lcs,&marie2->stat.number_of_LinphoneCallEnd,1,1000));
/*wait a bit that streams are established*/
wait_for_list(lcs,&dummy,1,1000);
CU_ASSERT_TRUE(linphone_call_get_audio_stats(pauline_call)->download_bandwidth>71);
CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie1_call)->download_bandwidth>71);
linphone_core_terminate_all_calls(pauline->lc);
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,1000));
CU_ASSERT_TRUE(wait_for_list(lcs,&marie1->stat.number_of_LinphoneCallEnd,1,1000));
ms_list_free(lcs);
linphone_core_manager_destroy(marie1);
linphone_core_manager_destroy(marie2);
linphone_core_manager_destroy(pauline);
}
#endif
test_t call_tests[] = {
@ -2161,6 +2368,9 @@ test_t call_tests[] = {
{ "Call with video added", call_with_video_added },
{ "Call with video added (random ports)", call_with_video_added_random_ports },
{ "Call with video declined",call_with_declined_video},
{ "Call with multiple early media", multiple_early_media },
{ "Call with ICE from video to non-video", call_with_ice_video_to_novideo},
{ "Call with ICE and video added", call_with_ice_video_added },
#endif
{ "SRTP ice call", srtp_ice_call },
{ "ZRTP ice call", zrtp_ice_call },
@ -2173,6 +2383,7 @@ test_t call_tests[] = {
{ "Call waiting indication", call_waiting_indication },
{ "Call waiting indication with privacy", call_waiting_indication_with_privacy },
{ "Simple conference", simple_conference },
{ "Simple conference with ICE",simple_conference_with_ice},
{ "Simple call transfer", simple_call_transfer },
{ "Unattended call transfer", unattended_call_transfer },
{ "Unattended call transfer with error", unattended_call_transfer_with_error },
@ -2188,9 +2399,10 @@ test_t call_tests[] = {
{ "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 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},
{ "Quality reporting not used if no config", quality_reporting_not_used_without_config},
{ "Quality reporting session report not sent if call did not start", quality_reporting_not_sent_if_call_not_started},
{ "Quality reporting session report sent if call ended normally", quality_reporting_at_call_termination},
{ "Quality reporting interval report if interval is configured", quality_reporting_interval_report},
{ "Call with specified codec bitrate", call_with_specified_codec_bitrate}
};

View file

@ -119,7 +119,7 @@ static void liblinphone_tester_qnx_log_handler(OrtpLogLevel lev, const char *fmt
void helper(const char *name) {
fprintf(stderr,"%s \t--help\n"
fprintf(stderr,"%s --help\n"
"\t\t\t--verbose\n"
"\t\t\t--silent\n"
"\t\t\t--list-suites\n"
@ -197,12 +197,10 @@ int main (int argc, char *argv[])
} else if (strcmp(argv[i],"--list-tests")==0){
CHECK_ARG("--list-tests", ++i, argc);
suite_name = argv[i];
for(j=0;j<liblinphone_tester_nb_tests(suite_name);j++) {
test_name = liblinphone_tester_test_name(suite_name, j);
fprintf(stdout, "%s\n", test_name);
}
liblinphone_tester_list_suite_tests(suite_name);
return 0;
} else {
fprintf(stderr, "Unknown option \"%s\"\n", argv[i]); \
helper(argv[0]);
return -1;
}
@ -216,7 +214,8 @@ int main (int argc, char *argv[])
}
if(test_name != NULL) {
if(liblinphone_tester_test_index(suite_name, test_name) == -1) {
fprintf(stderr, "Test \"%s\" not found\n", test_name);
fprintf(stderr, "Test \"%s\" not found. Available tests are:\n", test_name);
liblinphone_tester_list_suite_tests(suite_name);
return -1;
}
}

View file

@ -1,11 +1,11 @@
/*
liblinphone_tester - liblinphone test suite
Copyright (C) 2013 Belledonne Communications SARL
liblinphone_tester - liblinphone test suite
Copyright (C) 2013 Belledonne Communications SARL
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 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
@ -46,6 +46,7 @@ extern "C" {
#endif
extern const char *liblinphone_tester_file_prefix;
extern const char *liblinphone_tester_writable_dir_prefix;
extern test_suite_t setup_test_suite;
extern test_suite_t register_test_suite;
extern test_suite_t call_test_suite;
@ -62,11 +63,14 @@ extern int liblinphone_tester_nb_test_suites(void);
extern int liblinphone_tester_nb_tests(const char *suite_name);
extern const char * liblinphone_tester_test_suite_name(int suite_index);
extern int liblinphone_tester_test_suite_index(const char *suite_name);
extern void liblinphone_tester_list_suite_tests(const char *suite_name);
extern const char * liblinphone_tester_test_name(const char *suite_name, int test_index);
extern int liblinphone_tester_test_index(const char *suite_name, const char *test_name);
extern void liblinphone_tester_init(void);
extern void liblinphone_tester_uninit(void);
extern int liblinphone_tester_run_tests(const char *suite_name, const char *test_name);
extern void liblinphone_tester_set_fileprefix(const char* file_prefix);
extern void liblinphone_tester_set_writable_dir_prefix(const char* writable_dir_prefix);
#ifdef __cplusplus
};
@ -177,7 +181,7 @@ typedef struct _stats {
int number_of_LinphonePublishExpiring;
int number_of_LinphonePublishError;
int number_of_LinphonePublishCleared;
int number_of_LinphoneConfiguringSkipped;
int number_of_LinphoneConfiguringFailed;
int number_of_LinphoneConfiguringSuccessful;
@ -233,7 +237,7 @@ const char *liblinphone_tester_get_notify_content(void);
void liblinphone_tester_chat_message_state_change(LinphoneChatMessage* msg,LinphoneChatMessageState state,void* ud);
void liblinphone_tester_check_rtcp(LinphoneCoreManager* caller, LinphoneCoreManager* callee);
void liblinphone_tester_clock_start(MSTimeSpec *start);
bool_t liblinphone_tester_clock_elapsed(const MSTimeSpec *start, int value_ms);
bool_t liblinphone_tester_clock_elapsed(const MSTimeSpec *start, int value_ms);
#endif /* LIBLINPHONE_TESTER_H_ */

View file

@ -1,19 +1,19 @@
/*
liblinphone_tester - liblinphone test suite
Copyright (C) 2013 Belledonne Communications SARL
liblinphone_tester - liblinphone test suite
Copyright (C) 2013 Belledonne Communications SARL
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 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.
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, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@ -23,6 +23,11 @@
#include "private.h"
#include "liblinphone_tester.h"
#ifdef MSG_STORAGE_ENABLED
#include <sqlite3.h>
#endif
static char* message_external_body_url;
void text_message_received(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from_address, const char *message) {
@ -169,7 +174,7 @@ static void text_message(void) {
static void text_message_within_dialog(void) {
LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
lp_config_set_int(pauline->lc->config,"sip","chat_use_call_dialogs",1);
char* to = linphone_address_as_string(marie->identity);
@ -177,7 +182,7 @@ static void text_message_within_dialog(void) {
ms_free(to);
CU_ASSERT_TRUE(call(marie,pauline));
linphone_chat_room_send_message(chat_room,"Bla bla bla bla");
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceived,1));
@ -306,12 +311,19 @@ static void text_message_with_external_body(void) {
LinphoneChatMessage* message = linphone_chat_room_create_message(chat_room,"Bli bli bli \n blu");
linphone_chat_message_set_external_body_url(message,message_external_body_url="http://www.linphone.org");
linphone_chat_room_send_message2(chat_room,message,liblinphone_tester_chat_message_state_change,pauline->lc);
/* check transient message list: the message should be in it, and should be the only one */
CU_ASSERT_EQUAL(ms_list_size(chat_room->transient_messages), 1);
CU_ASSERT_EQUAL(ms_list_nth_data(chat_room->transient_messages,0), message);
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceived,1));
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneMessageDelivered,1));
CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneMessageInProgress,1);
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneMessageExtBodyReceived,1);
CU_ASSERT_EQUAL(ms_list_size(chat_room->transient_messages), 0);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
@ -366,10 +378,18 @@ static void text_message_with_send_error(void) {
sal_set_send_error(marie->lc->sal, -1);
linphone_chat_room_send_message2(chat_room,message,liblinphone_tester_chat_message_state_change,marie->lc);
/* check transient message list: the message should be in it, and should be the only one */
CU_ASSERT_EQUAL(ms_list_size(chat_room->transient_messages), 1);
CU_ASSERT_EQUAL(ms_list_nth_data(chat_room->transient_messages,0), message);
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageNotDelivered,1));
/*CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneMessageInProgress,1);*/
CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneMessageReceived,0);
/* the message should have been discarded from transient list after an error */
CU_ASSERT_EQUAL(ms_list_size(chat_room->transient_messages), 0);
sal_set_send_error(marie->lc->sal, 0);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
@ -384,7 +404,7 @@ static void text_message_denied(void) {
/*pauline doesn't want to be disturbed*/
linphone_core_disable_chat(pauline->lc,LinphoneReasonDoNotDisturb);
linphone_chat_room_send_message2(chat_room,message,liblinphone_tester_chat_message_state_change,marie->lc);
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageNotDelivered,1));
@ -482,6 +502,89 @@ static void is_composing_notification(void) {
linphone_core_manager_destroy(pauline);
}
#ifdef MSG_STORAGE_ENABLED
/*
* Copy file "from" to file "to".
* Destination file is truncated if existing.
* Return 1 on success, 0 on error (printing an error).
*/
static int
message_tester_copy_file(const char *from, const char *to)
{
char message[256];
FILE *in, *out;
char buf[256];
size_t n;
/* Open "from" file for reading */
in=fopen(from, "r");
if ( in == NULL )
{
snprintf(message, 255, "Can't open %s for reading: %s\n",
from, strerror(errno));
fprintf(stderr, "%s", message);
return 0;
}
/* Open "to" file for writing (will truncate existing files) */
out=fopen(to, "w");
if ( out == NULL )
{
snprintf(message, 255, "Can't open %s for writing: %s\n",
to, strerror(errno));
fprintf(stderr, "%s", message);
fclose(in);
return 0;
}
/* Copy data from "in" to "out" */
while ( (n=fread(buf, 1, sizeof buf, in)) > 0 )
{
if ( ! fwrite(buf, 1, n, out) )
{
fclose(in);
fclose(out);
return 0;
}
}
fclose(in);
fclose(out);
return 1;
}
static int check_no_strange_time(void* data,int argc, char** argv,char** cNames) {
CU_ASSERT_EQUAL(argc, 0);
return 0;
}
static void message_storage_migration() {
LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
char src_db[256];
char tmp_db[256];
snprintf(src_db,sizeof(src_db), "%s/messages.db", liblinphone_tester_file_prefix);
snprintf(tmp_db,sizeof(tmp_db), "%s/tmp.db", liblinphone_tester_writable_dir_prefix);
CU_ASSERT_EQUAL_FATAL(message_tester_copy_file(src_db, tmp_db), 1);
// enable to test the performances of the migration step
//linphone_core_message_storage_set_debug(marie->lc, TRUE);
// the messages.db has 10000 dummy messages with the very first DB scheme.
// This will test the migration procedure
linphone_core_set_chat_database_path(marie->lc, tmp_db);
MSList* chatrooms = linphone_core_get_chat_rooms(marie->lc);
CU_ASSERT(ms_list_size(chatrooms) > 0);
// check that all messages have been migrated to the UTC time storage
CU_ASSERT(sqlite3_exec(marie->lc->db, "SELECT * FROM history WHERE time != '-1';", check_no_strange_time, NULL, NULL) == SQLITE_OK );
}
#endif
test_t message_tests[] = {
{ "Text message", text_message },
{ "Text message within call's dialog", text_message_within_dialog},
@ -496,6 +599,9 @@ test_t message_tests[] = {
{ "Info message", info_message },
{ "Info message with body", info_message_with_body },
{ "IsComposing notification", is_composing_notification }
#ifdef MSG_STORAGE_ENABLED
,{ "Database migration", message_storage_migration }
#endif
};
test_suite_t message_test_suite = {

BIN
tester/messages.db Normal file

Binary file not shown.

View file

@ -30,8 +30,8 @@ subscribe=0
[rtp]
audio_rtp_port=8070
video_rtp_port=8072
audio_rtp_port=18070
video_rtp_port=19072
[video]
display=0

View file

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

View file

@ -119,7 +119,8 @@ static void register_with_refresh_base_3(LinphoneCore* lc
linphone_core_add_proxy_config(lc,proxy_cfg);
linphone_core_set_default_proxy(lc,proxy_cfg);
while (counters->number_of_LinphoneRegistrationOk<1+(refresh!=0) && retry++ <310) {
while (counters->number_of_LinphoneRegistrationOk<1+(refresh!=0)
&& retry++ <(110 /*only wait 11 s if final state is progress*/+(expected_final_state==LinphoneRegistrationProgress?0:200))) {
linphone_core_iterate(lc);
if (counters->number_of_auth_info_requested>0 && linphone_proxy_config_get_state(proxy_cfg) == LinphoneRegistrationFailed && late_auth_info) {
if (!linphone_core_get_auth_info_list(lc)) {
@ -197,6 +198,52 @@ static void simple_register(){
linphone_core_manager_destroy(lcm);
}
static void simple_unregister(){
LinphoneCoreManager* lcm = create_lcm();
stats* counters = &lcm->stat;
LinphoneProxyConfig* proxy_config;
register_with_refresh_base(lcm->lc,FALSE,NULL,NULL);
linphone_core_get_default_proxy(lcm->lc,&proxy_config);
linphone_proxy_config_edit(proxy_config);
reset_counters(counters); /*clear stats*/
/*nothing is supposed to arrive until done*/
CU_ASSERT_FALSE(wait_for_until(lcm->lc,lcm->lc,&counters->number_of_LinphoneRegistrationCleared,1,3000));
linphone_proxy_config_enable_register(proxy_config,FALSE);
linphone_proxy_config_done(proxy_config);
CU_ASSERT_TRUE(wait_for(lcm->lc,lcm->lc,&counters->number_of_LinphoneRegistrationCleared,1));
linphone_core_manager_destroy(lcm);
}
static void change_expires(){
LinphoneCoreManager* lcm = create_lcm();
stats* counters = &lcm->stat;
LinphoneProxyConfig* proxy_config;
register_with_refresh_base(lcm->lc,FALSE,NULL,NULL);
linphone_core_get_default_proxy(lcm->lc,&proxy_config);
linphone_proxy_config_edit(proxy_config);
reset_counters(counters); /*clear stats*/
/*nothing is supposed to arrive until done*/
CU_ASSERT_FALSE(wait_for_until(lcm->lc,lcm->lc,&counters->number_of_LinphoneRegistrationCleared,1,3000));
linphone_proxy_config_set_expires(proxy_config,3);
linphone_proxy_config_done(proxy_config);
CU_ASSERT_TRUE(wait_for(lcm->lc,lcm->lc,&counters->number_of_LinphoneRegistrationOk,1));
/*wait 2s without receive refresh*/
CU_ASSERT_FALSE(wait_for_until(lcm->lc,lcm->lc,&counters->number_of_LinphoneRegistrationOk,2,2000));
/* now, it should be ok*/
CU_ASSERT_TRUE(wait_for(lcm->lc,lcm->lc,&counters->number_of_LinphoneRegistrationOk,2));
linphone_core_manager_destroy(lcm);
}
/*take care of min expires configuration from server*/
static void simple_register_with_refresh() {
LinphoneCoreManager* lcm = create_lcm();
@ -467,6 +514,101 @@ static void transport_change(){
linphone_core_manager_destroy(mgr);
}
static void proxy_transport_change(){
LinphoneCoreManager* lcm = create_lcm();
stats* counters = &lcm->stat;
LinphoneProxyConfig* proxy_config;
LinphoneAddress* addr;
char* addr_as_string;
LinphoneAuthInfo *info=linphone_auth_info_new(test_username,NULL,test_password,NULL,auth_domain,NULL); /*create authentication structure from identity*/
linphone_core_add_auth_info(lcm->lc,info); /*add authentication info to LinphoneCore*/
register_with_refresh_base(lcm->lc,FALSE,auth_domain,NULL);
linphone_core_get_default_proxy(lcm->lc,&proxy_config);
reset_counters(counters); /*clear stats*/
linphone_proxy_config_edit(proxy_config);
CU_ASSERT_FALSE(wait_for_until(lcm->lc,lcm->lc,&counters->number_of_LinphoneRegistrationCleared,1,3000));
addr = linphone_address_new(linphone_proxy_config_get_addr(proxy_config));
if (LinphoneTransportTcp == linphone_address_get_transport(addr)) {
linphone_address_set_transport(addr,LinphoneTransportUdp);
} else {
linphone_address_set_transport(addr,LinphoneTransportTcp);
}
linphone_proxy_config_set_server_addr(proxy_config,addr_as_string=linphone_address_as_string(addr));
linphone_proxy_config_done(proxy_config);
CU_ASSERT(wait_for(lcm->lc,lcm->lc,&counters->number_of_LinphoneRegistrationOk,1));
/*as we change p[roxy server destination, we should'nt be notified about the clear*/
CU_ASSERT_EQUAL(counters->number_of_LinphoneRegistrationCleared,0);
ms_free(addr_as_string);
linphone_address_destroy(addr);
linphone_core_manager_destroy(lcm);
}
static void proxy_transport_change_with_wrong_port() {
LinphoneCoreManager* lcm = create_lcm();
stats* counters = &lcm->stat;
LinphoneProxyConfig* proxy_config;
LinphoneAuthInfo *info=linphone_auth_info_new(test_username,NULL,test_password,NULL,auth_domain,NULL); /*create authentication structure from identity*/
char route[256];
LCSipTransports transport= {LC_SIP_TRANSPORT_RANDOM,LC_SIP_TRANSPORT_RANDOM,LC_SIP_TRANSPORT_RANDOM,LC_SIP_TRANSPORT_RANDOM};
sprintf(route,"sip:%s",test_route);
linphone_core_add_auth_info(lcm->lc,info); /*add authentication info to LinphoneCore*/
register_with_refresh_base_3(lcm->lc, FALSE, auth_domain, "sip2.linphone.org:5987", 0,transport,LinphoneRegistrationProgress);
linphone_core_get_default_proxy(lcm->lc,&proxy_config);
linphone_proxy_config_edit(proxy_config);
CU_ASSERT_FALSE(wait_for_until(lcm->lc,lcm->lc,&counters->number_of_LinphoneRegistrationCleared,1,3000));
linphone_proxy_config_set_server_addr(proxy_config,route);
linphone_proxy_config_done(proxy_config);
CU_ASSERT(wait_for(lcm->lc,lcm->lc,&counters->number_of_LinphoneRegistrationOk,1));
/*as we change proxy server destination, we should'nt be notified about the clear*/
CU_ASSERT_EQUAL(counters->number_of_LinphoneRegistrationCleared,0);
CU_ASSERT_EQUAL(counters->number_of_LinphoneRegistrationOk,1);
CU_ASSERT_EQUAL(counters->number_of_LinphoneRegistrationProgress,1);
CU_ASSERT_EQUAL(counters->number_of_LinphoneRegistrationFailed,0);
linphone_core_manager_destroy(lcm);
}
static void proxy_transport_change_with_wrong_port_givin_up() {
LinphoneCoreManager* lcm = create_lcm();
stats* counters = &lcm->stat;
LinphoneProxyConfig* proxy_config;
LinphoneAuthInfo *info=linphone_auth_info_new(test_username,NULL,test_password,NULL,auth_domain,NULL); /*create authentication structure from identity*/
char route[256];
LCSipTransports transport= {LC_SIP_TRANSPORT_RANDOM,LC_SIP_TRANSPORT_RANDOM,LC_SIP_TRANSPORT_RANDOM,LC_SIP_TRANSPORT_RANDOM};
sprintf(route,"sip:%s",test_route);
linphone_core_add_auth_info(lcm->lc,info); /*add authentication info to LinphoneCore*/
register_with_refresh_base_3(lcm->lc, FALSE, auth_domain, "sip2.linphone.org:5987", 0,transport,LinphoneRegistrationProgress);
linphone_core_get_default_proxy(lcm->lc,&proxy_config);
linphone_proxy_config_edit(proxy_config);
CU_ASSERT_FALSE(wait_for_until(lcm->lc,lcm->lc,&counters->number_of_LinphoneRegistrationCleared,1,3000));
linphone_proxy_config_enableregister(proxy_config,FALSE);
linphone_proxy_config_done(proxy_config);
CU_ASSERT(wait_for(lcm->lc,lcm->lc,&counters->number_of_LinphoneRegistrationCleared,1));
CU_ASSERT_EQUAL(counters->number_of_LinphoneRegistrationOk,0);
CU_ASSERT_EQUAL(counters->number_of_LinphoneRegistrationProgress,1);
CU_ASSERT_EQUAL(counters->number_of_LinphoneRegistrationFailed,0);
linphone_core_manager_destroy(lcm);
}
static void io_recv_error(){
LinphoneCoreManager *mgr;
LinphoneCore* lc;
@ -539,7 +681,7 @@ static void io_recv_error_late_recovery(){
CU_ASSERT_TRUE(wait_for(lc,NULL,&counters->number_of_LinphoneRegistrationProgress,(register_ok-number_of_udp_proxy)+register_ok /*because 1 udp*/));
CU_ASSERT_EQUAL(counters->number_of_LinphoneRegistrationFailed,0)
CU_ASSERT_TRUE(wait_for_list(lcs=ms_list_append(NULL,lc),&counters->number_of_LinphoneRegistrationFailed,(register_ok-number_of_udp_proxy),sal_get_refresher_retry_after(lc->sal)+1000));
CU_ASSERT_TRUE(wait_for_list(lcs=ms_list_append(NULL,lc),&counters->number_of_LinphoneRegistrationFailed,(register_ok-number_of_udp_proxy),sal_get_refresher_retry_after(lc->sal)+3000));
sal_set_recv_error(lc->sal, 1); /*reset*/
sal_set_send_error(lc->sal, 0);
@ -567,22 +709,17 @@ static void io_recv_error_without_active_register(){
for (proxys=ms_list_copy(linphone_core_get_proxy_config_list(lc));proxys!=NULL;proxys=proxys->next) {
LinphoneProxyConfig* proxy_cfg=(LinphoneProxyConfig*)proxys->data;
linphone_proxy_config_edit(proxy_cfg);
linphone_proxy_config_enableregister(proxy_cfg,FALSE);
linphone_proxy_config_done(proxy_cfg);
}
ms_list_free(proxys);
/*wait for unregistrations*/
CU_ASSERT_TRUE(wait_for(lc,lc,&counters->number_of_LinphoneRegistrationCleared,register_ok /*because 1 udp*/));
for (proxys=ms_list_copy(linphone_core_get_proxy_config_list(lc));proxys!=NULL;proxys=proxys->next) {
LinphoneProxyConfig* proxy_cfg=(LinphoneProxyConfig*)proxys->data;
linphone_proxy_config_enable_register(proxy_cfg,FALSE);
linphone_proxy_config_done(proxy_cfg);
}
ms_list_free(proxys);
sal_set_recv_error(lc->sal, 0);
/*nothing should happen because no active registration*/
CU_ASSERT_FALSE(wait_for(lc,lc,&counters->number_of_LinphoneRegistrationProgress,2*(register_ok-number_of_udp_proxy) /*because 1 udp*/));
CU_ASSERT_FALSE(wait_for_until(lc,lc,&counters->number_of_LinphoneRegistrationProgress,2*(register_ok-number_of_udp_proxy) /*because 1 udp*/,3000));
CU_ASSERT_EQUAL(counters->number_of_LinphoneRegistrationFailed,0)
@ -669,6 +806,7 @@ static void tls_wildcard_register(){
test_t register_tests[] = {
{ "Simple register", simple_register },
{ "Simple register unregister", simple_unregister },
{ "TCP register", simple_tcp_register },
{ "TCP register compatibility mode", simple_tcp_register_compatibility_mode },
{ "TLS register", simple_tls_register },
@ -688,7 +826,11 @@ test_t register_tests[] = {
{ "Authenticated register with refresh", simple_auth_register_with_refresh },
{ "Register with refresh and send error", register_with_refresh_with_send_error },
{ "Multi account", multiple_proxy },
{ "Transport change", transport_change },
{ "Transport changes", transport_change },
{ "Proxy transport changes", proxy_transport_change},
{ "Proxy transport changes with wrong address at first", proxy_transport_change_with_wrong_port},
{ "Proxy transport changes with wrong address, giving up",proxy_transport_change_with_wrong_port_givin_up},
{ "Change expires", change_expires},
{ "Network state change", network_state_change },
{ "Io recv error", io_recv_error },
{ "Io recv error with recovery", io_recv_error_retry_immediatly},

View file

@ -21,7 +21,7 @@
#include "linphonecore.h"
#include "liblinphone_tester.h"
#include "lpconfig.h"
#include "private.h"
static void core_init_test(void) {
LinphoneCoreVTable v_table;
@ -104,10 +104,70 @@ static void linphone_lpconfig_from_buffer(){
lp_config_destroy(conf);
}
void linphone_proxy_config_address_equal_test() {
LinphoneAddress *a = linphone_address_new("sip:toto@titi");
LinphoneAddress *b = linphone_address_new("sips:toto@titi");
LinphoneAddress *c = linphone_address_new("sip:toto@titi;transport=tcp");
LinphoneAddress *d = linphone_address_new("sip:toto@titu");
LinphoneAddress *e = linphone_address_new("sip:toto@titi;transport=udp");
CU_ASSERT_FALSE(linphone_proxy_config_address_equal(a,NULL));
CU_ASSERT_FALSE(linphone_proxy_config_address_equal(a,b));
CU_ASSERT_FALSE(linphone_proxy_config_address_equal(a,c));
CU_ASSERT_FALSE(linphone_proxy_config_address_equal(a,d));
CU_ASSERT_TRUE(linphone_proxy_config_address_equal(a,e));
CU_ASSERT_TRUE(linphone_proxy_config_address_equal(NULL,NULL));
linphone_address_destroy(a);
linphone_address_destroy(b);
linphone_address_destroy(c);
linphone_address_destroy(d);
}
void linphone_proxy_config_is_server_config_changed_test() {
LinphoneProxyConfig* proxy_config = linphone_proxy_config_new();
linphone_proxy_config_set_identity(proxy_config,"sip:toto@titi");
linphone_proxy_config_edit(proxy_config);
linphone_proxy_config_set_identity(proxy_config,"sips:toto@titi");
CU_ASSERT_TRUE(linphone_proxy_config_is_server_config_changed(proxy_config));
linphone_proxy_config_set_server_addr(proxy_config,"sip:sip.linphone.org");
linphone_proxy_config_edit(proxy_config);
linphone_proxy_config_set_server_addr(proxy_config,"sip:toto.com");
CU_ASSERT_TRUE(linphone_proxy_config_is_server_config_changed(proxy_config));
linphone_proxy_config_set_server_addr(proxy_config,"sip:sip.linphone.org");
linphone_proxy_config_edit(proxy_config);
linphone_proxy_config_set_server_addr(proxy_config,"sip:sip.linphone.org:4444");
CU_ASSERT_TRUE(linphone_proxy_config_is_server_config_changed(proxy_config));
linphone_proxy_config_set_server_addr(proxy_config,"sip:sip.linphone.org");
linphone_proxy_config_edit(proxy_config);
linphone_proxy_config_set_server_addr(proxy_config,"sip:sip.linphone.org;transport=tcp");
CU_ASSERT_TRUE(linphone_proxy_config_is_server_config_changed(proxy_config));
linphone_proxy_config_set_server_addr(proxy_config,"sip:sip.linphone.org");
linphone_proxy_config_edit(proxy_config);
linphone_proxy_config_set_server_addr(proxy_config,"sip:sip.linphone.org;param=blue");
CU_ASSERT_FALSE(linphone_proxy_config_is_server_config_changed(proxy_config));
linphone_proxy_config_edit(proxy_config);
linphone_proxy_config_set_contact_parameters(proxy_config,"blabla=blue");
CU_ASSERT_FALSE(linphone_proxy_config_is_server_config_changed(proxy_config));
linphone_proxy_config_edit(proxy_config);
linphone_proxy_config_enable_register(proxy_config,TRUE);
CU_ASSERT_FALSE(linphone_proxy_config_is_server_config_changed(proxy_config));
linphone_proxy_config_destroy(proxy_config);
}
test_t setup_tests[] = {
{ "Linphone Address", linphone_address_test },
{ "Linphone proxy config address equal (internal api)", linphone_proxy_config_address_equal_test},
{ "Linphone proxy config server address change (internal api)", linphone_proxy_config_is_server_config_changed_test},
{ "Linphone core init/uninit", core_init_test },
{ "Linphone random transport port",core_sip_transport_test},
{ "Linphone interpret url", linphone_interpret_url_test },

View file

@ -49,6 +49,9 @@ const char *liblinphone_tester_file_prefix="./app/native/assets/";
const char *liblinphone_tester_file_prefix=".";
#endif
/* TODO: have the same "static" for QNX and windows as above? */
const char *liblinphone_tester_writable_dir_prefix = ".";
const char *userhostsfile = "tester_hosts";
void liblinphone_tester_clock_start(MSTimeSpec *start){
@ -80,8 +83,8 @@ LinphoneAddress * create_linphone_address(const char * domain) {
static void auth_info_requested(LinphoneCore *lc, const char *realm, const char *username, const char *domain) {
stats* counters;
ms_message("Auth info requested for user id [%s] at realm [%s]\n"
,username
,realm);
,username
,realm);
counters = get_stats(lc);
counters->number_of_auth_info_requested++;
}
@ -147,7 +150,7 @@ bool_t wait_for(LinphoneCore* lc_1, LinphoneCore* lc_2,int* counter,int value) {
bool_t wait_for_list(MSList* lcs,int* counter,int value,int timeout_ms) {
MSList* iterator;
MSTimeSpec start;
liblinphone_tester_clock_start(&start);
while ((counter==NULL || *counter<value) && !liblinphone_tester_clock_elapsed(&start,timeout_ms)) {
for (iterator=lcs;iterator!=NULL;iterator=iterator->next) {
@ -164,7 +167,7 @@ static void set_codec_enable(LinphoneCore* lc,const char* type,int rate,bool_t e
MSList* codecs_it;
PayloadType* pt;
for (codecs_it=codecs;codecs_it!=NULL;codecs_it=codecs_it->next) {
linphone_core_enable_payload_type(lc,(PayloadType*)codecs_it->data,0);
linphone_core_enable_payload_type(lc,(PayloadType*)codecs_it->data,0);
}
if((pt = linphone_core_find_payload_type(lc,type,rate,1))) {
linphone_core_enable_payload_type(lc,pt, enable);
@ -288,6 +291,14 @@ int liblinphone_tester_test_suite_index(const char *suite_name) {
return -1;
}
void liblinphone_tester_list_suite_tests(const char *suite_name) {
int j;
for( j = 0; j < liblinphone_tester_nb_tests(suite_name); j++) {
const char *test_name = liblinphone_tester_test_name(suite_name, j);
fprintf(stdout, "%s\n", test_name);
}
}
int liblinphone_tester_test_index(const char *suite_name, const char *test_name) {
int j,i;
@ -325,6 +336,15 @@ const char * liblinphone_tester_test_name(const char *suite_name, int test_index
return test_suite[suite_index]->tests[test_index].name;
}
void liblinphone_tester_set_fileprefix(const char* file_prefix){
liblinphone_tester_file_prefix = file_prefix;
}
void liblinphone_tester_set_writable_dir_prefix(const char* writable_dir_prefix){
liblinphone_tester_writable_dir_prefix = writable_dir_prefix;
}
void liblinphone_tester_init(void) {
add_test_suite(&setup_test_suite);
add_test_suite(&register_test_suite);
@ -386,6 +406,13 @@ int liblinphone_tester_run_tests(const char *suite_name, const char *test_name)
}
ret=CU_get_number_of_tests_failed()!=0;
/* Redisplay list of failed tests on end */
if (CU_get_number_of_failure_records()){
CU_basic_show_failures(CU_get_failure_list());
printf("\n");
}
CU_cleanup_registry();
return ret;
}