Commit 7e283551 authored by Stepan Salenikovich's avatar Stepan Salenikovich

Add slider to control video quality

This reverts commit 953969ac.
It also adds a checkbutton below the slider which enables
automatic video quality adjustment (and is on by default).

The slider now has a range of 0 to 100 and sets both the bitrate
and quality parameter of each codec by getting the min and max
values of both and scaling the set value.

Change-Id: I307e541c6e30c432ab5452bba2af9c2f069d79d9
Tuleap: #215
parent 16db8ca2
<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 11H9.5v-2h-2v2H6V9h1.5v2.5h2V9H11v6zm7-1c0 .55-.45 1-1 1h-.75v1.5h-1.5V15H14c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v4zm-3.5-.5h2v-3h-2v3z"/>
\ No newline at end of file
......@@ -16,5 +16,6 @@
<file alias="mute_video">ic_videocam_off_black_24px.svg</file>
<file alias="pause">ic_pause_black_24px.svg</file>
<file alias="end">ic_clear_black_24px.svg</file>
<file alias="quality">ic_high_quality_black_24px.svg</file>
......@@ -31,6 +31,7 @@
#include "currentcallview.h"
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <call.h>
#include <callmodel.h>
#include "utils/drawing.h"
......@@ -83,6 +84,13 @@ struct _CurrentCallViewPrivate
GtkWidget *entry_chat_input;
GtkWidget *scrolledwindow_chat;
GtkWidget *button_hangup;
GtkWidget *scalebutton_quality;
GtkWidget *checkbutton_autoquality;
/* flag used to keep track of the video quality scale pressed state;
* we do not want to update the codec bitrate until the user releases the
* scale button */
gboolean quality_scale_pressed;
Call *call;
......@@ -276,6 +284,152 @@ video_widget_focus(GtkWidget *widget, GtkDirectionType direction, CurrentCallVie
return FALSE;
static GtkBox *
gtk_scale_button_get_box(GtkScaleButton *button)
GtkWidget *box = NULL;
if (auto dock = gtk_scale_button_get_popup(button)) {
// the dock is a popover which contains the box
box = gtk_bin_get_child(GTK_BIN(dock));
if (box) {
if (GTK_IS_FRAME(box)) {
// support older versions of gtk; the box used to be in a frame
box = gtk_bin_get_child(GTK_BIN(box));
return GTK_BOX(box);
* This gets the GtkScaleButtonScale widget (which is a GtkScale) from the
* given GtkScaleButton in order to be able to modify its properties and connect
* to its signals
static GtkScale *
gtk_scale_button_get_scale(GtkScaleButton *button)
GtkScale *scale = NULL;
if (auto box = gtk_scale_button_get_box(button)) {
GList *children = gtk_container_get_children(GTK_CONTAINER(box));
for (GList *c = children; c && !scale; c = c->next) {
if (GTK_IS_SCALE(c->data))
scale = GTK_SCALE(c->data);
return scale;
static void
set_quality(Call *call, gboolean auto_quality_on, double desired_quality)
/* set auto quality true or false, also set the bitrate and quality values;
* the slider is from 0 to 100, use the min and max vals to scale each value accordingly */
if (const auto& codecModel = call->account()->codecModel()) {
const auto& videoCodecs = codecModel->videoCodecs();
for (int i=0; i < videoCodecs->rowCount();i++) {
const auto& idx = videoCodecs->index(i,0);
if (auto_quality_on) {
// g_debug("enable auto quality");
videoCodecs->setData(idx, "true", CodecModel::Role::AUTO_QUALITY_ENABLED);
} else {
auto min_bitrate =<int>(CodecModel::Role::MIN_BITRATE)).toInt();
auto max_bitrate =<int>(CodecModel::Role::MAX_BITRATE)).toInt();
auto min_quality =<int>(CodecModel::Role::MIN_QUALITY)).toInt();
auto max_quality =<int>(CodecModel::Role::MAX_QUALITY)).toInt();
// g_debug("bitrate min: %d, max: %d, quality min: %d, max: %d", min_bitrate, max_bitrate, min_quality, max_quality);
double bitrate;
bitrate = min_bitrate + (double)(max_bitrate - min_bitrate)*(desired_quality/100.0);
if (bitrate < 0) bitrate = 0;
double quality;
// note: a lower value means higher quality
quality = (double)min_quality - (min_quality - max_quality)*(desired_quality/100.0);
if (quality < 0) quality = 0;
// g_debug("disable auto quality; %% quality: %d; bitrate: %d; quality: %d", (int)desired_quality, (int)bitrate, (int)quality);
videoCodecs->setData(idx, "false", CodecModel::Role::AUTO_QUALITY_ENABLED);
videoCodecs->setData(idx, QString::number((int)bitrate), CodecModel::Role::BITRATE);
videoCodecs->setData(idx, QString::number((int)quality), CodecModel::Role::QUALITY);
codecModel << CodecModel::EditAction::SAVE;
static void
autoquality_toggled(GtkToggleButton *button, CurrentCallView *self)
CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
gboolean auto_quality_on = gtk_toggle_button_get_active(button);
auto scale = gtk_scale_button_get_scale(GTK_SCALE_BUTTON(priv->scalebutton_quality));
auto plus_button = gtk_scale_button_get_plus_button(GTK_SCALE_BUTTON(priv->scalebutton_quality));
auto minus_button = gtk_scale_button_get_minus_button(GTK_SCALE_BUTTON(priv->scalebutton_quality));
gtk_widget_set_sensitive(GTK_WIDGET(scale), !auto_quality_on);
gtk_widget_set_sensitive(plus_button, !auto_quality_on);
gtk_widget_set_sensitive(minus_button, !auto_quality_on);
double desired_quality = gtk_scale_button_get_value(GTK_SCALE_BUTTON(priv->scalebutton_quality));
if (priv->call)
set_quality(priv->call, auto_quality_on, desired_quality);
static void
quality_changed(GtkScaleButton *button, G_GNUC_UNUSED gdouble value, CurrentCallView *self)
CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
/* no need to upate quality if auto quality is enabled */
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->checkbutton_autoquality))) return;
/* only update if the scale button is released, to reduce the number of updates */
if (priv->quality_scale_pressed) return;
/* we get the value directly from the widget, in case this function is not
* called from the event */
if (priv->call)
set_quality(priv->call, FALSE, gtk_scale_button_get_value(button));
static gboolean
quality_button_pressed(G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkEvent *event, CurrentCallView *self)
g_return_val_if_fail(IS_CURRENT_CALL_VIEW(self), FALSE);
CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
priv->quality_scale_pressed = TRUE;
static gboolean
quality_button_released(G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkEvent *event, CurrentCallView *self)
g_return_val_if_fail(IS_CURRENT_CALL_VIEW(self), FALSE);
CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
priv->quality_scale_pressed = FALSE;
/* now make sure the quality gets updated */
quality_changed(GTK_SCALE_BUTTON(priv->scalebutton_quality), 0, self);
static void
current_call_view_init(CurrentCallView *view)
......@@ -339,6 +493,22 @@ current_call_view_init(CurrentCallView *view)
nullptr, nullptr, nullptr);
g_signal_connect(priv->scalebutton_quality, "value-changed", G_CALLBACK(quality_changed), view);
/* customize the quality button scale */
if (auto scale_box = gtk_scale_button_get_box(GTK_SCALE_BUTTON(priv->scalebutton_quality))) {
priv->checkbutton_autoquality = gtk_check_button_new_with_label(C_("Enable automatic video quality", "Auto"));
gtk_box_pack_start(GTK_BOX(scale_box), priv->checkbutton_autoquality, FALSE, TRUE, 0);
g_signal_connect(priv->checkbutton_autoquality, "toggled", G_CALLBACK(autoquality_toggled), view);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->checkbutton_autoquality), TRUE);
if (auto scale = gtk_scale_button_get_scale(GTK_SCALE_BUTTON(priv->scalebutton_quality))) {
g_signal_connect(scale, "button-press-event", G_CALLBACK(quality_button_pressed), view);
g_signal_connect(scale, "button-release-event", G_CALLBACK(quality_button_released), view);
static void
......@@ -364,6 +534,7 @@ current_call_view_class_init(CurrentCallViewClass *klass)
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, entry_chat_input);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, scrolledwindow_chat);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, button_hangup);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, scalebutton_quality);
current_call_view_signals[VIDEO_DOUBLE_CLICKED] = g_signal_new (
......@@ -606,4 +777,20 @@ current_call_view_set_call_info(CurrentCallView *view, const QModelIndex& idx) {
/* check if there were any chat notifications and open the chat view if so */
if (ring_notify_close_chat_notification(priv->call))
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->togglebutton_chat), TRUE);
/* check if auto quality is enabled or not; */
if (const auto& codecModel = priv->call->account()->codecModel()) {
const auto& videoCodecs = codecModel->videoCodecs();
if (videoCodecs->rowCount() > 0) {
/* we only need to check the first codec since by default it is ON for all, and the
* gnome client sets its ON or OFF for all codecs as well */
const auto& idx = videoCodecs->index(0,0);
auto auto_quality_enabled =<int>(CodecModel::Role::AUTO_QUALITY_ENABLED)).toString() == "true";
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->checkbutton_autoquality), auto_quality_enabled);
// TODO: save the manual quality setting in the client and set the slider to that value here;
// the daemon resets the bitrate/quality between each call, and the default may be
// different for each codec, so there is no reason to check it here
......@@ -289,6 +289,28 @@
<property name="fill">True</property>
<object class="GtkScaleButton" id="scalebutton_quality">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="width-request">42</property>
<property name="height-request">42</property>
<property name="has_tooltip">True</property>
<property name="relief">normal</property>
<property name="tooltip-text" translatable="yes">Adjust outgoing video quality</property>
<property name="image">image_quality</property>
<property name="adjustment">adjustment_quality</property>
<child internal-child="accessible">
<object class="AtkObject" id="scalebutton_quality-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">Video quality</property>
<property name="expand">False</property>
<property name="fill">True</property>
<object class="GtkToggleButton" id="togglebutton_chat">
<property name="visible">True</property>
......@@ -357,6 +379,15 @@
<object class="GtkImage" id="image_quality">
<property name="visible">True</property>
<property name="resource">/cx/ring/RingGnome/quality</property>
<child internal-child="accessible">
<object class="AtkObject" id="image_quality-atkobject">
<property name="AtkObject::accessible-description" translatable="yes">Video quality</property>
<object class="GtkImage" id="image_record">
<property name="visible">True</property>
<property name="icon_name">media-record</property>
......@@ -366,4 +397,11 @@
<object class="GtkAdjustment" id="adjustment_quality">
<property name="lower">0</property>
<property name="upper">100</property>
<property name="value">50</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment