Add slider to control video quality
This reverts commit 953969acc04e8ec1f9731dce770d187599e37e6d.
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
diff --git a/pixmaps/ic_high_quality_black_24px.svg b/pixmaps/ic_high_quality_black_24px.svg
new file mode 100644
index 0000000..6f030a1
--- /dev/null
+++ b/pixmaps/ic_high_quality_black_24px.svg
@@ -0,0 +1,4 @@
+<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
+ <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"/>
+</svg>
\ No newline at end of file
diff --git a/pixmaps/pixmaps.gresource.xml b/pixmaps/pixmaps.gresource.xml
index 23c4b62..a143e94 100644
--- a/pixmaps/pixmaps.gresource.xml
+++ b/pixmaps/pixmaps.gresource.xml
@@ -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>
</gresource>
</gresources>
diff --git a/src/currentcallview.cpp b/src/currentcallview.cpp
index 60197bd..525ef02 100644
--- a/src/currentcallview.cpp
+++ b/src/currentcallview.cpp
@@ -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 @@
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 @@
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);
+ }
+ g_list_free(children);
+ }
+
+ 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 = idx.data(static_cast<int>(CodecModel::Role::MIN_BITRATE)).toInt();
+ auto max_bitrate = idx.data(static_cast<int>(CodecModel::Role::MAX_BITRATE)).toInt();
+ auto min_quality = idx.data(static_cast<int>(CodecModel::Role::MIN_QUALITY)).toInt();
+ auto max_quality = idx.data(static_cast<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)
+{
+ g_return_if_fail(IS_CURRENT_CALL_VIEW(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)
+{
+ g_return_if_fail(IS_CURRENT_CALL_VIEW(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;
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+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);
+
+ return GDK_EVENT_PROPAGATE;
+}
+
static void
current_call_view_init(CurrentCallView *view)
{
@@ -339,6 +493,22 @@
G_SETTINGS_BIND_GET,
map_boolean_to_orientation,
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_widget_show(priv->checkbutton_autoquality);
+ 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 @@
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 (
"video-double-clicked",
@@ -606,4 +777,20 @@
/* 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 = idx.data(static_cast<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
+ }
+ }
}
diff --git a/ui/currentcallview.ui b/ui/currentcallview.ui
index cd2e470..5a65dcc 100644
--- a/ui/currentcallview.ui
+++ b/ui/currentcallview.ui
@@ -290,6 +290,28 @@
</packing>
</child>
<child>
+ <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>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkToggleButton" id="togglebutton_chat">
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -357,6 +379,15 @@
</object>
</child>
</object>
+ <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>
+ </child>
+ </object>
<object class="GtkImage" id="image_record">
<property name="visible">True</property>
<property name="icon_name">media-record</property>
@@ -366,4 +397,11 @@
</object>
</child>
</object>
+ <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>
+ </object>
</interface>