/mingw/bin/libstdc++-6.dll \
/mingw/bin/libintl-8.dll \
/mingw/bin/libiconv-2.dll \
+ /mingw/bin/libpthread-2.dll \
$(INSTALLDIR_WITH_PREFIX)/bin/.
if test "$enable_x11" = "true"; then
AC_CHECK_HEADERS(X11/Xlib.h)
- AC_CHECK_LIB(X11,XUnmapWindow, X11_LIBS="-lX11")
+ if test "$build_macos" = "yes"; then
+ X11_LIBS="-L/usr/X11/lib -lX11"
+ else
+ AC_CHECK_LIB(X11,XUnmapWindow, X11_LIBS="-lX11")
+ fi
AC_SUBST(X11_LIBS)
fi
AC_DEFINE(VIDEO_ENABLED,1,[defined if video support is available])
long id=(long)linphone_call_get_user_pointer (call);
switch(st){
case LinphoneCallEnd:
- linphonec_out("Call %i with %s ended.\n", id, from);
+ linphonec_out("Call %i with %s ended (%s).\n", id, from, linphone_reason_to_string(linphone_call_get_reason(call)));
break;
case LinphoneCallResuming:
linphonec_out("Resuming call %i with %s.\n", id, from);
case LinphoneCallOutgoingInit:
linphonec_call_identify(call);
id=(long)linphone_call_get_user_pointer (call);
- from=linphone_call_get_remote_address_as_string(call);
linphonec_out("Establishing call id to %s, assigned id %i\n", from,id);
break;
case LinphoneCallUpdatedByRemote:
/* we already started media: check if we really need to restart it*/
if (oldmd){
if (!media_parameters_changed(call,oldmd,new_md) && !call->playing_ringbacktone){
- sal_media_description_unref(oldmd);
+ /*as nothing has changed, keep the oldmd */
+ call->resultdesc=oldmd;
+ sal_media_description_unref(new_md);
if (call->all_muted){
ms_message("Early media finished, unmuting inputs...");
/*we were in early media, now we want to enable real media */
if (is_duplicate_call(lc,from_addr,to_addr)){
ms_warning("Receiving duplicated call, refusing this one.");
sal_call_decline(h,SalReasonBusy,NULL);
+ sal_op_release(h);
linphone_address_destroy(from_addr);
linphone_address_destroy(to_addr);
return;
*
**/
+/**
+ * @defgroup call_misc Obtaining information about a running call: sound volumes, quality indicators
+ *
+ * When a call is running, it is possible to retrieve in real time current measured volumes and quality indicator.
+ *
+**/
+
/**
* @defgroup media_parameters Controlling media parameters
**/
/* this function is called internally to get rid of a call.
It performs the following tasks:
- remove the call from the internal list of calls
- - unref the LinphoneCall object
- update the call logs accordingly
*/
cp->audio_bw=bandwidth;
}
+#ifdef VIDEO_ENABLED
+/**
+ * Request remote side to send us a Video Fast Update.
+**/
+void linphone_call_send_vfu_request(LinphoneCall *call)
+{
+ if (LinphoneCallStreamsRunning == linphone_call_get_state(call))
+ sal_call_send_vfu_request(call->op);
+}
+#endif
+
/**
*
**/
int enabled=lp_config_get_int(lc->config,"sound","noisegate",0);
audio_stream_enable_noise_gate(audiostream,enabled);
}
+
if (lc->a_rtp)
rtp_session_set_transports(audiostream->session,lc->a_rtp,lc->a_rtcp);
}
}
-
-
-
static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *md, const SalStreamDescription *desc, int *used_pt){
int bw;
const MSList *elem;
bool_t first=TRUE;
int remote_bw=0;
LinphoneCore *lc=call->core;
+ int up_ptime=0;
*used_pt=-1;
for(elem=desc->payloads;elem!=NULL;elem=elem->next){
PayloadType *pt=(PayloadType*)elem->data;
int number;
- if (first) {
+ if ((pt->flags & PAYLOAD_TYPE_FLAG_CAN_SEND) && first) {
if (desc->type==SalAudio){
linphone_core_update_allocated_audio_bandwidth_in_call(call,pt);
+ up_ptime=linphone_core_get_upload_ptime(lc);
}
*used_pt=payload_type_get_number(pt);
first=FALSE;
pt->normal_bitrate=-1;
}
if (desc->ptime>0){
+ up_ptime=desc->ptime;
+ }
+ if (up_ptime>0){
char tmp[40];
- snprintf(tmp,sizeof(tmp),"ptime=%i",desc->ptime);
+ snprintf(tmp,sizeof(tmp),"ptime=%i",up_ptime);
payload_type_append_send_fmtp(pt,tmp);
}
number=payload_type_get_number(pt);
const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc,
SalProtoRtpAvp,SalVideo);
#endif
+ bool_t use_arc=linphone_core_adaptive_rate_control_enabled(lc);
if(call->audiostream == NULL)
{
const char *playfile=lc->play_file;
const char *recfile=lc->rec_file;
call->audio_profile=make_profile(call,call->resultdesc,stream,&used_pt);
- bool_t use_ec;
+ bool_t use_ec,use_arc_audio=use_arc;
if (used_pt!=-1){
if (playcard==NULL) {
playcard=NULL;
}
use_ec=captcard==NULL ? FALSE : linphone_core_echo_cancellation_enabled(lc);
-#if defined(VIDEO_ENABLED) && defined(ANDROID)
- /*On android we have to disable the echo canceller to preserve CPU for video codecs */
- if (vstream && vstream->dir!=SalStreamInactive && vstream->payloads!=NULL)
+#if defined(VIDEO_ENABLED)
+ if (vstream && vstream->dir!=SalStreamInactive && vstream->payloads!=NULL){
+ /*when video is used, do not make adaptive rate control on audio, it is stupid.*/
+ use_arc_audio=FALSE;
+ #if defined(ANDROID)
+ /*On android we have to disable the echo canceller to preserve CPU for video codecs */
use_ec=FALSE;
+ #endif
+ }
#endif
+ audio_stream_enable_adaptive_bitrate_control(call->audiostream,use_arc_audio);
audio_stream_start_full(
call->audiostream,
call->audio_profile,
static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){
audio_stream_get_local_rtp_stats (st,&log->local_stats);
+ log->quality=audio_stream_get_average_quality_rating(st);
}
void linphone_call_stop_media_streams(LinphoneCall *call){
}
}
-#ifdef VIDEO_ENABLED
-/**
- * Request remote side to send us VFU.
-**/
-void linphone_call_send_vfu_request(LinphoneCall *call)
-{
- if (LinphoneCallStreamsRunning == linphone_call_get_state(call))
- sal_call_send_vfu_request(call->op);
-}
-#endif
+
void linphone_call_enable_echo_cancellation(LinphoneCall *call, bool_t enable) {
if (call!=NULL && call->audiostream!=NULL && call->audiostream->ec){
}
}
+/**
+ * @addtogroup call_misc
+ * @{
+**/
+
/**
* Returns the measured sound volume played locally (received from remote)
* It is expressed in dbm0.
return LINPHONE_VOLUME_DB_LOWEST;
}
+/**
+ * Obtain real-time quality rating of the call
+ *
+ * Based on local RTP statistics and RTCP feedback, a quality rating is computed and updated
+ * during all the duration of the call. This function returns its value at the time of the function call.
+ * It is expected that the rating is updated at least every 5 seconds or so.
+ * The rating is a floating point number comprised between 0 and 5.
+ *
+ * 4-5 = good quality <br>
+ * 3-4 = average quality <br>
+ * 2-3 = poor quality <br>
+ * 1-2 = very poor quality <br>
+ * 0-1 = can't be worse, mostly unusable <br>
+ *
+ * @returns The function returns -1 if no quality measurement is available, for example if no
+ * active audio stream exist. Otherwise it returns the quality rating.
+**/
+float linphone_call_get_current_quality(LinphoneCall *call){
+ if (call->audiostream){
+ return audio_stream_get_quality_rating(call->audiostream);
+ }
+ return -1;
+}
+
+/**
+ * Returns call quality averaged over all the duration of the call.
+ *
+ * See linphone_call_get_current_quality() for more details about quality measurement.
+**/
+float linphone_call_get_average_quality(LinphoneCall *call){
+ if (call->audiostream){
+ return audio_stream_get_average_quality_rating(call->audiostream);
+ }
+ return -1;
+}
+
+/**
+ * @}
+**/
static void display_bandwidth(RtpSession *as, RtpSession *vs){
ms_message("bandwidth usage: audio=[d=%.1f,u=%.1f] video=[d=%.1f,u=%.1f] kbit/sec",
if (call->videostream!=NULL)
video_stream_iterate(call->videostream);
#endif
+ if (call->audiostream!=NULL)
+ audio_stream_iterate(call->audiostream);
if (one_second_elapsed && call->audiostream!=NULL && disconnect_timeout>0 )
disconnected=!audio_stream_alive(call->audiostream,disconnect_timeout);
if (disconnected)
lp_config_set_string(cfg,logsection,"start_date",cl->start_date);
lp_config_set_int(cfg,logsection,"duration",cl->duration);
if (cl->refkey) lp_config_set_string(cfg,logsection,"refkey",cl->refkey);
+ lp_config_set_float(cfg,logsection,"quality",cl->quality);
}
for(;i<lc->max_call_logs;++i){
snprintf(logsection,sizeof(logsection),"call_log_%i",i);
cl->duration=lp_config_get_int(cfg,logsection,"duration",0);
tmp=lp_config_get_string(cfg,logsection,"refkey",NULL);
if (tmp) cl->refkey=ms_strdup(tmp);
+ cl->quality=lp_config_get_float(cfg,logsection,"quality",-1);
lc->call_logs=ms_list_append(lc->call_logs,cl);
}else break;
}
sal_use_rport(lc->sal,lp_config_get_int(lc->config,"sip","use_rport",1));
sal_use_101(lc->sal,lp_config_get_int(lc->config,"sip","use_101",1));
+ sal_reuse_authorization(lc->sal, lp_config_get_int(lc->config,"sip","reuse_authorization",0));
tmp=lp_config_get_int(lc->config,"sip","use_rfc2833",0);
linphone_core_set_use_rfc2833_for_dtmf(lc,tmp);
}
*/
+/**
+ * Enable adaptive rate control (experimental feature, audio-only).
+ *
+ * Adaptive rate control consists in using RTCP feedback provided information to dynamically
+ * control the output bitrate of the encoders, so that we can adapt to the network conditions and
+ * available bandwidth.
+**/
+void linphone_core_enable_adaptive_rate_control(LinphoneCore *lc, bool_t enabled){
+ lp_config_set_int(lc->config,"net","adaptive_rate_control",(int)enabled);
+}
+
+/**
+ * Returns whether adaptive rate control is enabled.
+ *
+ * See linphone_core_enable_adaptive_rate_control().
+**/
+bool_t linphone_core_adaptive_rate_control_enabled(const LinphoneCore *lc){
+ return lp_config_get_int(lc->config,"net","adaptive_rate_control",FALSE);
+}
+
/**
* Sets maximum available download bandwidth
*
return lc->net_conf.upload_bw;
}
/**
- * set audio packetization time linphone expect to received from peer
+ * Set audio packetization time linphone expects to receive from peer
*/
void linphone_core_set_download_ptime(LinphoneCore *lc, int ptime) {
lc->net_conf.down_ptime=ptime;
}
-int linphone_core_get_download_ptime(LinphoneCore *lc) {
+/**
+ * Get audio packetization time linphone expects to receive from peer
+ */
+int linphone_core_get_download_ptime(LinphoneCore *lc) {
return lc->net_conf.down_ptime;
}
+/**
+ * Set audio packetization time linphone will send (in absence of requirement from peer)
+ * A value of 0 stands for the current codec default packetization time.
+ *
+**/
+void linphone_core_set_upload_ptime(LinphoneCore *lc, int ptime){
+ lp_config_set_int(lc->config,"rtp","upload_ptime",ptime);
+}
+
+/**
+ * Set audio packetization time linphone will send (in absence of requirement from peer)
+ * A value of 0 stands for the current codec default packetization time.
+ *
+**/
+int linphone_core_get_upload_ptime(LinphoneCore *lc){
+ return lp_config_get_int(lc->config,"rtp","upload_ptime",0);
+}
+
+
/**
* Returns liblinphone's version as a string.
sal_unlisten_ports (sal);
if (tr->udp_port>0){
- if (sal_listen_port (sal,anyaddr,tr->udp_port,SalTransportDatagram,FALSE)!=0){
+ if (sal_listen_port (sal,anyaddr,tr->udp_port,SalTransportUDP,FALSE)!=0){
transport_error(lc,"UDP",tr->udp_port);
return -1;
}
}
if (tr->tcp_port>0){
- if (sal_listen_port (sal,anyaddr,tr->tcp_port,SalTransportStream,FALSE)!=0){
+ if (sal_listen_port (sal,anyaddr,tr->tcp_port,SalTransportTCP,FALSE)!=0){
transport_error(lc,"TCP",tr->tcp_port);
}
}
linphone_core_start_invite() */
calls=calls->next;
if (call->state==LinphoneCallOutgoingInit && (curtime-call->start_time>=2)){
- /*start the call even if the OPTIONS reply did not arrive*/
- linphone_core_start_invite(lc,call,NULL);
- }
+ /*start the call even if the OPTIONS reply did not arrive*/
+ linphone_core_start_invite(lc,call,NULL);
+ }
if (call->dir==LinphoneCallIncoming && call->state==LinphoneCallOutgoingRinging){
elapsed=curtime-call->start_time;
ms_message("incoming call ringing for %i seconds",elapsed);
**/
LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const LinphoneAddress *addr, const LinphoneCallParams *params)
{
- int err=0;
const char *route=NULL;
const char *from=NULL;
LinphoneProxyConfig *proxy=NULL;
lc->current_call=call;
linphone_call_set_state (call,LinphoneCallOutgoingInit,"Starting outgoing call");
if (dest_proxy!=NULL || lc->sip_conf.ping_with_options==FALSE){
- err=linphone_core_start_invite(lc,call,dest_proxy);
+ linphone_core_start_invite(lc,call,dest_proxy);
}else{
/*defer the start of the call after the OPTIONS ping*/
call->ping_op=sal_op_new(lc->sal);
return p;
}
-const char *linphone_error_to_string(LinphoneReason err){
+const char *linphone_reason_to_string(LinphoneReason err){
switch(err){
case LinphoneReasonNone:
return "No error";
}
return "unknown error";
}
+
+const char *linphone_error_to_string(LinphoneReason err){
+ return linphone_reason_to_string(err);
+}
/**
* Enables signaling keep alive
*/
void *user_pointer;
rtp_stats_t local_stats;
rtp_stats_t remote_stats;
+ float quality;
struct _LinphoneCore *lc;
} LinphoneCallLog;
const char *linphone_call_get_remote_user_agent(LinphoneCall *call);
float linphone_call_get_play_volume(LinphoneCall *call);
float linphone_call_get_record_volume(LinphoneCall *call);
+float linphone_call_get_current_quality(LinphoneCall *call);
+float linphone_call_get_average_quality(LinphoneCall *call);
void *linphone_call_get_user_pointer(LinphoneCall *call);
void linphone_call_set_user_pointer(LinphoneCall *call, void *user_pointer);
/**
int linphone_core_get_download_bandwidth(const LinphoneCore *lc);
int linphone_core_get_upload_bandwidth(const LinphoneCore *lc);
+
+void linphone_core_enable_adaptive_rate_control(LinphoneCore *lc, bool_t enabled);
+bool_t linphone_core_adaptive_rate_control_enabled(const LinphoneCore *lc);
/**
- * set audio packetization time linphone expect to received from peer
+ * set audio packetization time linphone expect to receive from peer
* @ingroup media_parameters
*
*/
void linphone_core_set_download_ptime(LinphoneCore *lc, int ptime);
/**
- * get audio packetization time linphone expect to received from peer, 0 means unspecified
+ * get audio packetization time linphone expect to receive from peer, 0 means unspecified
* @ingroup media_parameters
*/
int linphone_core_get_download_ptime(LinphoneCore *lc);
+void linphone_core_set_upload_ptime(LinphoneCore *lc, int ptime);
+
+int linphone_core_get_upload_ptime(LinphoneCore *lc);
+
/* returns a MSList of PayloadType */
const MSList *linphone_core_get_audio_codecs(const LinphoneCore *lc);
return (jlong)linphone_call_get_replaced_call((LinphoneCall*)ptr);
}
+extern "C" jfloat Java_org_linphone_core_LinphoneCallImpl_getCurrentQuality( JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jfloat)linphone_call_get_current_quality((LinphoneCall*)ptr);
+}
+
+extern "C" jfloat Java_org_linphone_core_LinphoneCallImpl_getAverageQuality( JNIEnv* env
+ ,jobject thiz
+ ,jlong ptr) {
+ return (jfloat)linphone_call_get_average_quality((LinphoneCall*)ptr);
+}
+
//LinphoneFriend
extern "C" long Java_org_linphone_core_LinphoneFriendImpl_newLinphoneFriend(JNIEnv* env
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUploadBandwidth(JNIEnv *env, jobject thiz, jlong lc, jint bw){
linphone_core_set_upload_bandwidth((LinphoneCore *)lc, (int) bw);
}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setDownloadPtime(JNIEnv *env, jobject thiz, jlong lc, jint ptime){
+ linphone_core_set_download_ptime((LinphoneCore *)lc, (int) ptime);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUploadPtime(JNIEnv *env, jobject thiz, jlong lc, jint ptime){
+ linphone_core_set_upload_ptime((LinphoneCore *)lc, (int) ptime);
+}
+
extern "C" int Java_org_linphone_core_LinphoneProxyConfigImpl_getState(JNIEnv* env,jobject thiz,jlong ptr) {
return (int) linphone_proxy_config_get_state((const LinphoneProxyConfig *) ptr);
}
void lp_config_set_int(LpConfig *lpconfig,const char *section, const char *key, int value){
char tmp[30];
- snprintf(tmp,30,"%i",value);
+ snprintf(tmp,sizeof(tmp),"%i",value);
+ lp_config_set_string(lpconfig,section,key,tmp);
+}
+
+void lp_config_set_float(LpConfig *lpconfig,const char *section, const char *key, float value){
+ char tmp[30];
+ snprintf(tmp,sizeof(tmp),"%f",value);
lp_config_set_string(lpconfig,section,key,tmp);
- lpconfig->modified++;
}
void lp_item_write(LpItem *item, FILE *file){
* @ingroup misc
**/
void lp_config_set_int(LpConfig *lpconfig,const char *section, const char *key, int value);
+/**
+ * Sets a float config item
+ *
+ * @ingroup misc
+**/
+void lp_config_set_float(LpConfig *lpconfig,const char *section, const char *key, float value);
/**
* Writes the config file to disk.
*
lsd_player_init(&lsd->branches[0],mp,MS_ITC_SOURCE_ID,lsd);
ms_filter_set_notify_callback(lsd->branches[0].player,(MSFilterNotifyFunc)lsd_player_configure,&lsd->branches[0]);
- ms_filter_enable_synchronous_notifcations (lsd->branches[0].player,TRUE);
for(i=1;i<MAX_BRANCHES;++i){
mp.pin=i;
lsd_player_init(&lsd->branches[i],mp,MS_FILE_PLAYER_ID,lsd);
}
static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t reading_response, bool_t one_matching_codec){
- const MSList *e2;
+ const MSList *e2,*e1;
MSList *res=NULL;
PayloadType *matched;
bool_t found_codec=FALSE;
newp=payload_type_clone(matched);
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;
res=ms_list_append(res,newp);
/* we should use the remote numbering even when parsing a response */
payload_type_set_number(newp,remote_number);
Indeed despite we must sent with the remote numbering, we must be able to receive with
our local one.
*/
- newp=payload_type_clone(matched);
+ newp=payload_type_clone(newp);
payload_type_set_number(newp,local_number);
res=ms_list_append(res,newp);
}
ms_message("No match for %s/%i",p2->mime_type,p2->clock_rate);
}
}
+ if (reading_response){
+ /* add remaning local payload as CAN_RECV only so that if we are in front of a non-compliant equipment we are still able to decode the RTP stream*/
+ for(e1=local;e1!=NULL;e1=e1->next){
+ PayloadType *p1=(PayloadType*)e1->data;
+ bool_t found=FALSE;
+ for(e2=res;e2!=NULL;e2=e2->next){
+ PayloadType *p2=(PayloadType*)e2->data;
+ if (payload_type_get_number(p2)==payload_type_get_number(p1)){
+ found=TRUE;
+ break;
+ }
+ }
+ if (!found){
+ ms_message("Adding %s/%i for compatibility, just in case.",p1->mime_type,p1->clock_rate);
+ p1=payload_type_clone(p1);
+ p1->flags|=PAYLOAD_TYPE_FLAG_CAN_RECV;
+ res=ms_list_append(res,p1);
+ }
+ }
+ }
return res;
}
linphone_core_get_sip_transports(obj->lc,&tr);
if (tr.udp_port <= 0 && tr.tcp_port>0) {
- sal_address_add_param(contact,"transport","tcp");
+ sal_address_set_param(contact,"transport","TCP");
}
ret=linphone_address_as_string(contact);
linphone_address_destroy(contact);
}
static void linphone_proxy_config_register(LinphoneProxyConfig *obj){
- const char *id_str;
-
- if (obj->reg_identity!=NULL) id_str=obj->reg_identity;
- else id_str=linphone_core_get_primary_contact(obj->lc);
if (obj->reg_sendregister){
char *contact;
if (obj->op)
**/
#include "sal.h"
-
+const char* sal_transport_to_string(SalTransport transport) {
+ switch (transport) {
+ case SalTransportUDP:return "UDP";
+ case SalTransportTCP: return "TCP";
+ case SalTransportTLS:return "TLS";
+ case SalTransportDTLS:return "DTLS";
+ default: {
+ ms_fatal("Unexpected transport [%i]",transport);
+ return NULL;
+ }
+
+ }
+}
+SalTransport sal_transport_parse(const char* param) {
+ if (strcasecmp("UDP",param)==0) return SalTransportUDP;
+ if (strcasecmp("TCP",param)==0) return SalTransportTCP;
+ if (strcasecmp("TLS",param)==0) return SalTransportTLS;
+ if (strcasecmp("DTLS",param)==0) return SalTransportDTLS;
+ ms_error("Unkown transport type[%s], returning UDP", param);
+ return SalTransportUDP;
+}
SalMediaDescription *sal_media_description_new(){
SalMediaDescription *md=ms_new0(SalMediaDescription,1);
md->refcount=1;
if (auth_info->password) ms_free(auth_info->password);
ms_free((void*)auth_info);
}
+
#include "mediastreamer2/mscommon.h"
+/*Dirty hack, keep in sync with mediastreamer2/include/mediastream.h */
+#ifndef PAYLOAD_TYPE_FLAG_CAN_RECV
+#define PAYLOAD_TYPE_FLAG_CAN_RECV PAYLOAD_TYPE_USER_FLAG_1
+#define PAYLOAD_TYPE_FLAG_CAN_SEND PAYLOAD_TYPE_USER_FLAG_2
+#endif
struct Sal;
typedef struct Sal Sal;
typedef struct SalAddress SalAddress;
+typedef enum {
+ SalTransportUDP, /*UDP*/
+ SalTransportTCP, /*TCP*/
+ SalTransportTLS, /*TLS*/
+ SalTransportDTLS /*DTLS*/
+}SalTransport;
+
+const char* sal_transport_to_string(SalTransport transport);
+SalTransport sal_transport_parse(const char*);
/* Address manipulation API*/
SalAddress * sal_address_new(const char *uri);
SalAddress * sal_address_clone(const SalAddress *addr);
const char *sal_address_get_username(const SalAddress *addr);
const char *sal_address_get_domain(const SalAddress *addr);
const char * sal_address_get_port(const SalAddress *addr);
-int sal_address_get_port_int(const SalAddress *uri);
+int sal_address_get_port_int(const SalAddress *addr);
+SalTransport sal_address_get_transport(const SalAddress* addr);
void sal_address_set_display_name(SalAddress *addr, const char *display_name);
void sal_address_set_username(SalAddress *addr, const char *username);
char *sal_address_as_string(const SalAddress *u);
char *sal_address_as_string_uri_only(const SalAddress *u);
void sal_address_destroy(SalAddress *u);
-void sal_address_add_param(SalAddress *u,const char* name,const char* value);
-
+void sal_address_set_param(SalAddress *u,const char* name,const char* value);
+void sal_address_set_transport(SalAddress* addr,SalTransport transport);
Sal * sal_init();
void sal_set_user_pointer(Sal *sal, void *user_data);
void *sal_get_user_pointer(const Sal *sal);
-typedef enum {
- SalTransportDatagram,
- SalTransportStream
-}SalTransport;
typedef enum {
SalAudio,
unsigned int sal_get_keepalive_period(Sal *ctx);
void sal_use_session_timers(Sal *ctx, int expires);
void sal_use_double_registrations(Sal *ctx, bool_t enabled);
+void sal_reuse_authorization(Sal *ctx, bool_t enabled);
void sal_use_one_matching_codec_policy(Sal *ctx, bool_t one_matching_codec);
void sal_use_rport(Sal *ctx, bool_t use_rports);
void sal_use_101(Sal *ctx, bool_t use_101);
int sal_call_terminate(SalOp *h);
bool_t sal_call_autoanswer_asked(SalOp *op);
void sal_call_send_vfu_request(SalOp *h);
+int sal_call_is_offerer(const SalOp *h);
/*Registration*/
int sal_register(SalOp *op, const char *proxy, const char *from, int expires);
/*ping: main purpose is to obtain its own contact address behind firewalls*/
int sal_ping(SalOp *op, const char *from, const char *to);
+
+
#define payload_type_set_number(pt,n) (pt)->user_data=(void*)((long)n);
#define payload_type_get_number(pt) ((int)(long)(pt)->user_data)
static void text_received(Sal *sal, eXosip_event_t *ev);
+static void masquerade_via(osip_message_t *msg, const char *ip, const char *port);
+static bool_t fix_message_contact(SalOp *op, osip_message_t *request,osip_message_t *last_answer);
+static void update_contact_from_response(SalOp *op, osip_message_t *response);
+
void _osip_list_set_empty(osip_list_t *l, void (*freefunc)(void*)){
void *data;
while(!osip_list_eol(l,0)) {
eXosip_event_free(op->pending_auth);
if (op->rid!=-1){
sal_remove_register(op->base.root,op->rid);
+ eXosip_register_remove(op->rid);
}
if (op->cid!=-1){
ms_message("Cleaning cid %i",op->cid);
sal->double_reg=TRUE;
sal->use_rports=TRUE;
sal->use_101=TRUE;
+ sal->reuse_authorization=FALSE;
return sal;
}
int keepalive = ctx->keepalive_period;
switch (tr) {
- case SalTransportDatagram:
+ case SalTransportUDP:
proto=IPPROTO_UDP;
eXosip_set_option (EXOSIP_OPT_UDP_KEEP_ALIVE, &keepalive);
break;
- case SalTransportStream:
+ case SalTransportTCP:
proto= IPPROTO_TCP;
keepalive=-1;
eXosip_set_option (EXOSIP_OPT_UDP_KEEP_ALIVE,&keepalive);
void sal_use_101(Sal *ctx, bool_t use_101){
ctx->use_101=use_101;
}
-static int extract_received_rport(osip_message_t *msg, const char **received, int *rportval){
+
+static int extract_received_rport(osip_message_t *msg, const char **received, int *rportval,SalTransport* transport){
osip_via_t *via=NULL;
osip_generic_param_t *param=NULL;
const char *rport=NULL;
osip_message_get_via(msg,0,&via);
if (!via) return -1;
- /* it is useless to do that with tcp since client socket might have a different port
- than the server socket.
- */
- if (strcasecmp(via->protocol,"tcp")==0) return -1;
+ *transport = sal_transport_parse(via->protocol);
if (via->port && via->port[0]!='\0')
*rportval=atoi(via->port);
}
+int sal_call_is_offerer(const SalOp *h){
+ return h->sdp_offering;
+}
+
int sal_call_set_local_media_description(SalOp *h, SalMediaDescription *desc){
if (desc)
sal_media_description_ref(desc);
int sal_call_notify_ringing(SalOp *h, bool_t early_media){
osip_message_t *msg;
- int err;
/*if early media send also 180 and 183 */
if (early_media && h->sdp_answer){
msg=NULL;
eXosip_lock();
- err=eXosip_call_build_answer(h->tid,180,&msg);
+ eXosip_call_build_answer(h->tid,180,&msg);
if (msg){
set_sdp(msg,h->sdp_answer);
eXosip_call_send_answer(h->tid,180,msg);
}
msg=NULL;
- err=eXosip_call_build_answer(h->tid,183,&msg);
+ eXosip_call_build_answer(h->tid,183,&msg);
if (msg){
set_sdp(msg,h->sdp_answer);
eXosip_call_send_answer(h->tid,183,msg);
eXosip_lock();
err=eXosip_call_terminate(h->cid,h->did);
eXosip_unlock();
- pop_auth_from_exosip();
+ if (!h->base.root->reuse_authorization) pop_auth_from_exosip();
if (err!=0){
ms_warning("Exosip could not terminate the call: cid=%i did=%i", h->cid,h->did);
}
}
void sal_op_authenticate(SalOp *h, const SalAuthInfo *info){
- if (h->pending_auth){
+ if (h->pending_auth){
push_auth_to_exosip(info);
+
+ /*FIXME exosip does not take into account this update register message*/
+ /*
+ if (fix_message_contact(h, h->pending_auth->request,h->pending_auth->response)) {
+
+ };
+ */
+ update_contact_from_response(h,h->pending_auth->response);
eXosip_lock();
eXosip_default_action(h->pending_auth);
eXosip_unlock();
ms_message("eXosip_default_action() done");
- pop_auth_from_exosip();
+ if (!h->base.root->reuse_authorization) pop_auth_from_exosip();
if (h->auth_info) sal_auth_info_delete(h->auth_info); /*if already exist*/
h->auth_info=sal_auth_info_clone(info); /*store auth info for subsequent request*/
static void set_network_origin(SalOp *op, osip_message_t *req){
const char *received=NULL;
int rport=5060;
- char origin[64];
- if (extract_received_rport(req,&received,&rport)!=0){
+ char origin[64]={0};
+ SalTransport transport;
+ if (extract_received_rport(req,&received,&rport,&transport)!=0){
osip_via_t *via=NULL;
char *tmp;
osip_message_get_via(req,0,&via);
tmp=osip_via_get_port(via);
if (tmp) rport=atoi(tmp);
}
- snprintf(origin,sizeof(origin)-1,"sip:%s:%i",received,rport);
+ if (transport != SalTransportUDP) {
+ snprintf(origin,sizeof(origin)-1,"sip:%s:%i",received,rport);
+ } else {
+ snprintf(origin,sizeof(origin)-1,"sip:%s:%i;transport=%s",received,rport,sal_transport_to_string(transport));
+ }
__sal_op_set_network_origin(op,origin);
}
static void update_contact_from_response(SalOp *op, osip_message_t *response){
const char *received;
int rport;
- if (extract_received_rport(response,&received,&rport)==0){
+ SalTransport transport;
+ if (extract_received_rport(response,&received,&rport,&transport)==0){
const char *contact=sal_op_get_contact(op);
if (!contact){
/*no contact given yet, use from instead*/
char *tmp;
sal_address_set_domain(addr,received);
sal_address_set_port_int(addr,rport);
+ if (transport!=SalTransportUDP)
+ sal_address_set_transport(addr,transport);
tmp=sal_address_as_string(addr);
- ms_message("Contact address updated to %s for this dialog",tmp);
+ ms_message("Contact address updated to %s",tmp);
sal_op_set_contact(op,tmp);
sal_address_destroy(addr);
ms_free(tmp);
static int call_proceeding(Sal *sal, eXosip_event_t *ev){
SalOp *op=find_op(sal,ev);
-
- if (op==NULL) {
+
+ if (op==NULL || op->terminated==TRUE) {
ms_warning("This call has been canceled.");
eXosip_lock();
eXosip_call_terminate(ev->cid,ev->did);
eXosip_unlock();
- op->terminated=TRUE;
return -1;
}
if (ev->did>0)
SalOp *op=find_op(sal,ev);
const char *contact;
- if (op==NULL){
- ms_error("A closed call is accepted ?");
- return;
+ if (op==NULL || op->terminated==TRUE) {
+ ms_warning("This call has been already terminated.");
+ eXosip_lock();
+ eXosip_call_terminate(ev->cid,ev->did);
+ eXosip_unlock();
+ return ;
}
op->did=ev->did;
}
}
-static bool_t register_again_with_updated_contact(SalOp *op, osip_message_t *orig_request, osip_message_t *last_answer){
- osip_message_t *msg;
+
+static bool_t fix_message_contact(SalOp *op, osip_message_t *request,osip_message_t *last_answer) {
+ osip_contact_t *ctt=NULL;
const char *received;
int rport;
- osip_contact_t *ctt=NULL;
- char *tmp;
+ SalTransport transport;
char port[20];
- SalAddress *addr;
- Sal *sal=op->base.root;
- if (sal->double_reg==FALSE) return FALSE;
-
- if (extract_received_rport(last_answer,&received,&rport)==-1) return FALSE;
- osip_message_get_contact(orig_request,0,&ctt);
- if (strcmp(ctt->url->host,received)==0){
- /*ip address matches, check ports*/
- const char *contact_port=ctt->url->port;
- if (contact_port==NULL || contact_port[0]=='\0')
- contact_port="5060";
- if (atoi(contact_port)==rport){
- ms_message("Register has up to date contact, doing nothing.");
- return FALSE;
- }else ms_message("ports do not match, need to update the register (%s <> %i)", contact_port,rport);
- }
- eXosip_lock();
- msg=NULL;
- eXosip_register_build_register(op->rid,op->expires,&msg);
- if (msg==NULL){
- eXosip_unlock();
- ms_warning("Fail to create a contact updated register.");
+ if (extract_received_rport(last_answer,&received,&rport,&transport)==-1) return FALSE;
+ osip_message_get_contact(request,0,&ctt);
+ if (ctt == NULL) {
+ /*nothing to update*/
return FALSE;
}
- osip_message_get_contact(msg,0,&ctt);
if (ctt->url->host!=NULL){
osip_free(ctt->url->host);
}
}
snprintf(port,sizeof(port),"%i",rport);
ctt->url->port=osip_strdup(port);
- if (op->masquerade_via) masquerade_via(msg,received,port);
- eXosip_register_send_register(op->rid,msg);
- eXosip_unlock();
+ if (op->masquerade_via) masquerade_via(request,received,port);
+
+ if (transport != SalTransportUDP) {
+ sal_address_set_param((SalAddress *)ctt, "transport", sal_transport_to_string(transport));
+ }
+ return TRUE;
+}
+
+static bool_t register_again_with_updated_contact(SalOp *op, osip_message_t *orig_request, osip_message_t *last_answer){
+ osip_contact_t *ctt=NULL;
+ SalAddress* ori_contact_address=NULL;
+ const char *received;
+ int rport;
+ SalTransport transport;
+ char* tmp;
+ osip_message_t *msg=NULL;
+ Sal* sal=op->base.root;
+
+ if (sal->double_reg==FALSE ) return FALSE;
+
+ if (extract_received_rport(last_answer,&received,&rport,&transport)==-1) return FALSE;
+ osip_message_get_contact(orig_request,0,&ctt);
osip_contact_to_str(ctt,&tmp);
- addr=sal_address_new(tmp);
+ ori_contact_address = sal_address_new((const char*)tmp);
+
+ /*check if contact is up to date*/
+ if (strcmp(sal_address_get_domain(ori_contact_address),received) ==0
+ && sal_address_get_port_int(ori_contact_address) == rport
+ && sal_address_get_transport(ori_contact_address) == transport) {
+ ms_message("Register has up to date contact, doing nothing.");
+ osip_free(tmp);
+ return FALSE;
+ } else ms_message("contact do not match, need to update the register (%s with %s:%i;transport=%s)"
+ ,tmp
+ ,received
+ ,rport
+ ,sal_transport_to_string(transport));
osip_free(tmp);
- sal_address_clean(addr);
- tmp=sal_address_as_string(addr);
- sal_op_set_contact(op,tmp);
- sal_address_destroy(addr);
- ms_message("Resending new register with updated contact %s",tmp);
- ms_free(tmp);
- return TRUE;
+ sal_address_destroy(ori_contact_address);
+
+ if (transport == SalTransportUDP) {
+ eXosip_lock();
+ eXosip_register_build_register(op->rid,op->expires,&msg);
+ if (msg==NULL){
+ eXosip_unlock();
+ ms_warning("Fail to create a contact updated register.");
+ return FALSE;
+ }
+ if (fix_message_contact(op,msg,last_answer)) {
+ eXosip_register_send_register(op->rid,msg);
+ eXosip_unlock();
+ ms_message("Resending new register with updated contact");
+ return TRUE;
+ } else {
+ ms_warning("Fail to send updated register.");
+ eXosip_unlock();
+ return FALSE;
+ }
+ eXosip_unlock();
+ }
+
+ update_contact_from_response(op,last_answer);
+ return FALSE;
}
static void registration_success(Sal *sal, eXosip_event_t *ev){
osip_free(tmp);
return ret;
}
-void sal_address_add_param(SalAddress *u,const char* name,const char* value) {
- osip_uri_uparam_add (((osip_from_t*)u)->url,ms_strdup(name),ms_strdup(value));
+void sal_address_set_param(SalAddress *u,const char* name,const char* value) {
+ osip_uri_param_t *param=NULL;
+ osip_uri_uparam_get_byname(((osip_from_t*)u)->url,(char*)name,¶m);
+ if (param == NULL){
+ osip_uri_uparam_add (((osip_from_t*)u)->url,ms_strdup(name),ms_strdup(value));
+ } else {
+ osip_free(param->gvalue);
+ param->gvalue=osip_strdup(value);
+ }
+
}
void sal_address_destroy(SalAddress *u){
return 5060;
}
}
+SalTransport sal_address_get_transport(const SalAddress* addr) {
+ const osip_from_t *u=(const osip_from_t*)addr;
+ osip_uri_param_t *transport_param=NULL;
+ osip_uri_uparam_get_byname(u->url,"transport",&transport_param);
+ if (transport_param == NULL){
+ return SalTransportUDP;
+ } else {
+ return sal_transport_parse(transport_param->gvalue);
+ }
+}
+void sal_address_set_transport(SalAddress* addr,SalTransport transport) {
+ sal_address_set_param(addr, "transport", sal_transport_to_string(transport));
+}
/* sends a reinvite. Local media description may have changed by application since call establishment*/
int sal_call_update(SalOp *h, const char *subject){
eXosip_unlock();
return err;
}
+void sal_reuse_authorization(Sal *ctx, bool_t value) {
+ ctx->reuse_authorization=value;
+}
bool_t double_reg;
bool_t use_rports;
bool_t use_101;
+ bool_t reuse_authorization;
};
struct SalOp{
char *addr= linphone_address_as_string_uri_only (la);
const char *display;
gchar *logtxt;
+ gchar quality[20];
+
display=linphone_address_get_display_name (la);
if (display==NULL){
display=linphone_address_get_username (la);
if (display==NULL)
display=linphone_address_get_domain (la);
}
- logtxt=g_markup_printf_escaped("<big><b>%s</b></big>\t<small><i>%s</i></small>\n"
- "%s\t%i minutes %i seconds",display, addr, cl->start_date,
+ if (cl->quality!=-1){
+ snprintf(quality,sizeof(quality),"%.1f",cl->quality);
+ }
+ logtxt=g_markup_printf_escaped("<big><b>%s</b></big>\t<small><i>%s</i>\t<i>Quality: %s</i></small>\n"
+ "%s\t%i minutes %i seconds\t",display, addr, cl->quality!=-1 ? quality : _("n/a"),
+ cl->start_date,
cl->duration/60,cl->duration%60);
gtk_list_store_append (store,&iter);
gtk_list_store_set (store,&iter,
void linphone_gtk_set_friend_status(GtkWidget *friendlist , LinphoneFriend * fid, const gchar *url, const gchar *status, const gchar *img){
GtkTreeIter iter;
LinphoneFriend *tmp=0;
- gboolean found=FALSE;
+
GtkTreeModel *model=gtk_tree_view_get_model(GTK_TREE_VIEW(friendlist));
if (gtk_tree_model_get_iter_first(model,&iter)) {
do{
gtk_list_store_set(GTK_LIST_STORE(model),&iter,FRIEND_PRESENCE_STATUS,status,-1);
pixbuf = create_pixbuf(img);
if (pixbuf)
- {
- gtk_list_store_set(GTK_LIST_STORE(model),&iter,FRIEND_PRESENCE_IMG, pixbuf,-1);
- }
- found=TRUE;
+ {
+ gtk_list_store_set(GTK_LIST_STORE(model),&iter,FRIEND_PRESENCE_IMG, pixbuf,-1);
+ }
}
}while(gtk_tree_model_iter_next(model,&iter));
}
gtk_label_set_markup(GTK_LABEL(status),_("<b>Incoming call</b>"));
gtk_widget_show_all(linphone_gtk_get_widget(callview,"answer_decline_panel"));
- gtk_widget_hide(linphone_gtk_get_widget(callview,"duration_frame"));
gtk_widget_hide(linphone_gtk_get_widget(callview,"mute_pause_buttons"));
display_peer_name_in_label(callee,linphone_call_get_remote_address (call));
}else gtk_image_set_from_stock(GTK_IMAGE(animation),GTK_STOCK_EXECUTE,GTK_ICON_SIZE_DIALOG);
}
+static void rating_to_color(float rating, GdkColor *color){
+ const char *colorname="grey";
+ if (rating>=4.0)
+ colorname="green";
+ else if (rating>=3.0)
+ colorname="white";
+ else if (rating>=2.0)
+ colorname="yellow";
+ else if (rating>=1.0)
+ colorname="orange";
+ else if (rating>=0)
+ colorname="red";
+ if (!gdk_color_parse(colorname,color)){
+ g_warning("Fail to parse color %s",colorname);
+ }
+}
+
+static const char *rating_to_text(float rating){
+ if (rating>=4.0)
+ return _("good");
+ if (rating>=3.0)
+ return _("average");
+ if (rating>=2.0)
+ return _("poor");
+ if (rating>=1.0)
+ return _("very poor");
+ if (rating>=0)
+ return _("too bad");
+ return _("unavailable");
+}
+
+static gboolean linphone_gtk_in_call_view_refresh(LinphoneCall *call){
+ GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call);
+ GtkWidget *qi=linphone_gtk_get_widget(callview,"quality_indicator");
+ float rating=linphone_call_get_current_quality(call);
+ GdkColor color;
+ gchar tmp[50];
+ linphone_gtk_in_call_view_update_duration(call);
+ if (rating>=0){
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(qi),rating/5.0);
+ snprintf(tmp,sizeof(tmp),"%.1f (%s)",rating,rating_to_text(rating));
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(qi),tmp);
+ }else{
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(qi),0);
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(qi),_("unavailable"));
+ }
+ rating_to_color(rating,&color);
+ gtk_widget_modify_bg(qi,GTK_STATE_NORMAL,&color);
+ return TRUE;
+}
+
void linphone_gtk_in_call_view_set_in_call(LinphoneCall *call){
GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call);
GtkWidget *status=linphone_gtk_get_widget(callview,"in_call_status");
GtkWidget *duration=linphone_gtk_get_widget(callview,"in_call_duration");
GtkWidget *animation=linphone_gtk_get_widget(callview,"in_call_animation");
GdkPixbufAnimation *pbuf=create_pixbuf_animation("incall_anim.gif");
+ guint taskid=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(callview),"taskid"));
display_peer_name_in_label(callee,linphone_call_get_remote_address (call));
- gtk_widget_show(linphone_gtk_get_widget(callview,"duration_frame"));
gtk_widget_show(linphone_gtk_get_widget(callview,"mute_pause_buttons"));
gtk_widget_hide(linphone_gtk_get_widget(callview,"answer_decline_panel"));
gtk_label_set_markup(GTK_LABEL(status),_("<b>In call</b>"));
}else gtk_image_set_from_stock(GTK_IMAGE(animation),GTK_STOCK_EXECUTE,GTK_ICON_SIZE_DIALOG);
linphone_gtk_enable_mute_button(
GTK_BUTTON(linphone_gtk_get_widget(callview,"incall_mute")),TRUE);
+ if (taskid==0){
+ taskid=g_timeout_add(250,(GSourceFunc)linphone_gtk_in_call_view_refresh,call);
+ g_object_set_data(G_OBJECT(callview),"taskid",GINT_TO_POINTER(taskid));
+ }
}
void linphone_gtk_in_call_view_set_paused(LinphoneCall *call){
GtkWidget *status=linphone_gtk_get_widget(callview,"in_call_status");
GtkWidget *animation=linphone_gtk_get_widget(callview,"in_call_animation");
GdkPixbuf *pbuf=create_pixbuf(linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png"));
+ guint taskid=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(callview),"taskid"));
if (error_msg==NULL)
gtk_label_set_markup(GTK_LABEL(status),_("<b>Call ended.</b>"));
linphone_gtk_enable_mute_button(
GTK_BUTTON(linphone_gtk_get_widget(callview,"incall_mute")),FALSE);
linphone_gtk_enable_hold_button(call,FALSE,TRUE);
+ if (taskid!=0) g_source_remove(taskid);
g_timeout_add_seconds(2,(GSourceFunc)in_call_view_terminated,call);
}
update_video_title();
}
-static gboolean in_call_timer(){
- LinphoneCall *call=linphone_core_get_current_call(linphone_gtk_get_core());
- if (call){
- linphone_gtk_in_call_view_update_duration(call);
- return TRUE;
- }
- return FALSE;
-}
-
static bool_t all_other_calls_paused(LinphoneCall *refcall, const MSList *calls){
for(;calls!=NULL;calls=calls->next){
LinphoneCall *call=(LinphoneCall*)calls->data;
linphone_gtk_enable_mute_button(
GTK_BUTTON(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"main_mute")),
TRUE);
- g_timeout_add(250,(GSourceFunc)in_call_timer,NULL);
break;
case LinphoneCallError:
linphone_gtk_in_call_view_terminate (call,msg);
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy toplevel-contextual -->
<object class="GtkVBox" id="vbox2">
<property name="visible">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="orientation">vertical</property>
<child>
<object class="GtkMenuBar" id="menubar1">
<property name="visible">True</property>
<child>
<object class="GtkVBox" id="main_frame">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
<child>
<object class="GtkHBox" id="address_bar">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="tooltip_text" translatable="yes">Enter username, phone number, or full sip address</property>
- <property name="invisible_char">●</property>
+ <property name="invisible_char">●</property>
<signal name="activate" handler="linphone_gtk_uri_bar_activate"/>
</object>
<packing>
<object class="GtkVBox" id="idle_frame">
<property name="visible">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="orientation">vertical</property>
<child>
<object class="GtkHBox" id="hbox5">
<property name="visible">True</property>
<child>
<object class="GtkVBox" id="vbox5">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
<child>
<object class="GtkFrame" id="contact_list_frame">
<property name="visible">True</property>
<object class="GtkVBox" id="vbox7">
<property name="visible">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="orientation">vertical</property>
<child>
<object class="GtkHBox" id="filtering_box">
<property name="visible">True</property>
<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="invisible_char">●</property>
+ <property name="invisible_char">●</property>
<signal name="changed" handler="linphone_gtk_show_friends"/>
</object>
<packing>
<object class="GtkEntry" id="directory_search_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="invisible_char">●</property>
+ <property name="invisible_char">●</property>
+ <signal name="focus_in_event" handler="linphone_gtk_directory_search_focus_in"/>
<signal name="activate" handler="linphone_gtk_directory_search_activate"/>
<signal name="icon_press" handler="linphone_gtk_directory_search_activate"/>
- <signal name="focus_in_event" handler="linphone_gtk_directory_search_focus_in"/>
<signal name="focus_out_event" handler="linphone_gtk_directory_search_focus_out"/>
</object>
<packing>
<child>
<object class="GtkVBox" id="keypad_frame">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
<child>
<object class="GtkFrame" id="frame3">
<property name="visible">True</property>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
<child>
<object class="GtkImage" id="login_image">
<property name="visible">True</property>
<object class="GtkEntry" id="login_username">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="invisible_char">●</property>
+ <property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="visibility">False</property>
- <property name="invisible_char">●</property>
+ <property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<child>
<object class="GtkVBox" id="vbox3">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
<child>
<object class="GtkImage" id="in_call_animation">
<property name="visible">True</property>
</packing>
</child>
<child>
- <object class="GtkFrame" id="frame2">
+ <object class="GtkLabel" id="in_call_uri">
<property name="visible">True</property>
- <property name="label_xalign">0</property>
- <child>
- <object class="GtkLabel" id="in_call_uri">
- <property name="visible">True</property>
- <property name="label" translatable="yes">label</property>
- <property name="justify">center</property>
- </object>
- </child>
- <child type="label">
- <object class="GtkLabel" id="label3">
- <property name="visible">True</property>
- <property name="use_markup">True</property>
- </object>
- </child>
+ <property name="label" translatable="yes">label</property>
+ <property name="justify">center</property>
</object>
<packing>
<property name="position">1</property>
<property name="position">2</property>
</packing>
</child>
- <child>
- <object class="GtkFrame" id="duration_frame">
- <property name="visible">True</property>
- <property name="label_xalign">0</property>
- <child>
- <object class="GtkVBox" id="vbox4">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkLabel" id="in_call_duration">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Duration</property>
- <property name="justify">center</property>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- </child>
- <child type="label">
- <object class="GtkLabel" id="call_label">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Duration:</property>
- <property name="use_markup">True</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">3</property>
- </packing>
- </child>
<child>
<object class="GtkHButtonBox" id="mute_pause_buttons">
<property name="visible">True</property>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">4</property>
+ <property name="position">3</property>
</packing>
</child>
</object>
</object>
</child>
<child type="label">
- <object class="GtkLabel" id="in_call_status">
+ <object class="GtkHBox" id="heading_box">
<property name="visible">True</property>
- <property name="label" translatable="yes">In call</property>
- <property name="use_markup">True</property>
- <property name="justify">center</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkLabel" id="in_call_status">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">In call</property>
+ <property name="use_markup">True</property>
+ <property name="justify">center</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="in_call_duration">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Duration</property>
+ <property name="justify">center</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkProgressBar" id="quality_indicator">
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes">Call quality rating</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
</object>
</child>
</object>
* @return call duration computed from media start
*/
int getDuration();
+ /**
+ * Obtain real-time quality rating of the call
+ *
+ * Based on local RTP statistics and RTCP feedback, a quality rating is computed and updated
+ * during all the duration of the call. This function returns its value at the time of the function call.
+ * It is expected that the rating is updated at least every 5 seconds or so.
+ * The rating is a floating point number comprised between 0 and 5.
+ *
+ * 4-5 = good quality <br>
+ * 3-4 = average quality <br>
+ * 2-3 = poor quality <br>
+ * 1-2 = very poor quality <br>
+ * 0-1 = can't be worse, mostly unusable <br>
+ *
+ * @returns The function returns -1 if no quality measurement is available, for example if no
+ * active audio stream exist. Otherwise it returns the quality rating.
+ */
+ float getCurrentQuality();
+ /**
+ * Returns call quality averaged over all the duration of the call.
+ *
+ * See getCurrentQuality() for more details about quality measurement.
+ */
+ float getAverageQuality();
}
void setUploadBandwidth(int bw);
void setDownloadBandwidth(int bw);
+
+ /**
+ * Sets audio packetization interval suggested for remote end.
+ * @param ptime packetization interval in milliseconds
+ */
+ void setDownloadPtime(int ptime);
+
+ /**
+ * Sets audio packetization interval sent to remote end.
+ * @param ptime packetization interval in milliseconds
+ */
+ void setUploadPtime(int ptime);
void setPreferredVideoSize(VideoSize vSize);
-./bin/avcodec-52.dll
-./bin/avutil-50.dll
+./bin/avcodec-53.dll
+./bin/avutil-51.dll
./bin/libeXosip2-6.dll
./bin/libogg-0.dll
./bin/libtheora-0.dll
./bin/libxml2-2.dll
./bin/libosip2-6.dll
./bin/libosipparser2-6.dll
-./bin/swscale-0.dll
-
+./bin/swscale-2.dll
-Subproject commit 3428fc9d84c363c677d0db0a3682b4bd2ca6d21a
+Subproject commit 06f75eec486b8a5be3032d387bc91d9f4ca135e3
-Subproject commit b0c5530bee255033f09011e5aab6d036e00520dc
+Subproject commit 662a65869902a927673d9ceff10781e217ca8e9d
"Project-Id-Version: linphone 0.7.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-04-08 22:32+0200\n"
-"PO-Revision-Date: 2011-04-10 02:35+0200\n"
+"PO-Revision-Date: 2011-05-27 23:45+0200\n"
"Last-Translator: Gerhard Stengel <gstengel@gmx.net>\n"
"Language-Team: German <kde-i18n-de@kde.org>\n"
"Language: \n"
#: ../gtk/main.ui.h:33
msgid "Enable self-view"
-msgstr "Selbstansicht einschalten"
+msgstr "Selbstansicht ein"
#: ../gtk/main.ui.h:34
msgid "Enter username, phone number, or full sip address"
#: ../coreapi/callbacks.c:574
msgid "no response timeout"
-msgstr "keine Zeitüberschreitung bei der Antwort"
+msgstr "Zeitüberschreitung bei der Antwort"
#: ../coreapi/callbacks.c:577
#, c-format