diff --git a/coreapi/help/doxygen.dox b/coreapi/help/doxygen.dox
index 99293c65b..7d0a82849 100644
--- a/coreapi/help/doxygen.dox
+++ b/coreapi/help/doxygen.dox
@@ -43,6 +43,13 @@
*
**/
+/**
+ * @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
**/
diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c
index ab521aa7f..fdcca58d1 100644
--- a/coreapi/linphonecall.c
+++ b/coreapi/linphonecall.c
@@ -585,6 +585,17 @@ void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int
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
+
/**
*
**/
@@ -1040,16 +1051,7 @@ 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){
@@ -1089,6 +1091,11 @@ bool_t linphone_call_echo_limiter_enabled(const LinphoneCall *call){
}
}
+/**
+ * @addtogroup call_misc
+ * @{
+**/
+
/**
* Returns the measured sound volume played locally (received from remote)
* It is expressed in dbm0.
@@ -1119,6 +1126,45 @@ float linphone_call_get_record_volume(LinphoneCall *call){
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
+ * 3-4 = average quality
+ * 2-3 = poor quality
+ * 1-2 = very poor quality
+ * 0-1 = can't be worse, mostly unusable
+ *
+ * @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",
@@ -1171,6 +1217,8 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse
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)
diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h
index 50fdb96d8..cd57b926d 100644
--- a/coreapi/linphonecore.h
+++ b/coreapi/linphonecore.h
@@ -255,6 +255,8 @@ LinphoneReason linphone_call_get_reason(const LinphoneCall *call);
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);
/**
diff --git a/gtk/incall_view.c b/gtk/incall_view.c
index 96f672cfc..919adaa3c 100644
--- a/gtk/incall_view.c
+++ b/gtk/incall_view.c
@@ -205,7 +205,6 @@ void linphone_gtk_in_call_view_set_incoming(LinphoneCall *call, bool_t with_paus
gtk_label_set_markup(GTK_LABEL(status),_("Incoming call"));
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));
@@ -228,6 +227,57 @@ void linphone_gtk_in_call_view_set_incoming(LinphoneCall *call, bool_t with_paus
}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");
@@ -235,10 +285,10 @@ void linphone_gtk_in_call_view_set_in_call(LinphoneCall *call){
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),_("In call"));
@@ -250,6 +300,10 @@ void linphone_gtk_in_call_view_set_in_call(LinphoneCall *call){
}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){
@@ -283,6 +337,7 @@ void linphone_gtk_in_call_view_terminate(LinphoneCall *call, const char *error_m
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),_("Call ended."));
@@ -299,6 +354,7 @@ void linphone_gtk_in_call_view_terminate(LinphoneCall *call, const char *error_m
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);
}
diff --git a/gtk/main.c b/gtk/main.c
index b47296f27..6968466d8 100644
--- a/gtk/main.c
+++ b/gtk/main.c
@@ -632,15 +632,6 @@ void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
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;
@@ -961,7 +952,6 @@ static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call
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);
diff --git a/gtk/main.ui b/gtk/main.ui
index aaa4ae4a4..c24591a91 100644
--- a/gtk/main.ui
+++ b/gtk/main.ui
@@ -1,4 +1,4 @@
-
+
@@ -62,7 +62,6 @@