commit e67d27e74d0a2b0281256402ea79adea8ae91373
parent 90aeae98ca55fffcfbd598255d62fd258d287e1d
Author: TheJackiMonster <thejackimonster@gmail.com>
Date: Mon, 5 Dec 2022 05:27:27 +0100
Fix issues with video playback
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
Diffstat:
5 files changed, 325 insertions(+), 117 deletions(-)
diff --git a/resources/ui/play_media.ui b/resources/ui/play_media.ui
@@ -23,6 +23,11 @@ Author: Tobias Frisch
<interface>
<requires lib="gtk+" version="3.24"/>
<requires lib="libhandy" version="1.2"/>
+ <object class="GtkAdjustment" id="timeline_adjustment">
+ <property name="upper">100</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">10</property>
+ </object>
<object class="HdyWindow" id="play_media_window">
<property name="width-request">250</property>
<property name="height-request">250</property>
@@ -234,15 +239,37 @@ audio-volume-medium-symbolic</property>
</packing>
</child>
<child>
- <object class="GtkLabel" id="timeline_label">
+ <object class="HdyLeaflet">
<property name="visible">True</property>
<property name="can-focus">False</property>
- <property name="label" translatable="yes">0:00 / 0:00</property>
+ <property name="visible-child">timeline_scale</property>
+ <property name="transition-type">slide</property>
+ <child>
+ <object class="GtkLabel" id="timeline_label">
+ <property name="width-request">80</property>
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-end">4</property>
+ <property name="label" translatable="yes">0:00 / 0:00</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScale" id="timeline_scale">
+ <property name="width-request">100</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can-focus">True</property>
+ <property name="margin-start">4</property>
+ <property name="hexpand">True</property>
+ <property name="adjustment">timeline_adjustment</property>
+ <property name="draw-value">False</property>
+ </object>
+ </child>
</object>
<packing>
- <property name="expand">False</property>
+ <property name="expand">True</property>
<property name="fill">True</property>
- <property name="position">2</property>
+ <property name="position">3</property>
</packing>
</child>
<child>
diff --git a/src/ui/new_contact.c b/src/ui/new_contact.c
@@ -90,7 +90,7 @@ _disable_video_processing(UI_NEW_CONTACT_Handle *handle,
static void
msg_error_cb(UNUSED GstBus *bus,
GstMessage *msg,
- gpointer *data)
+ gpointer data)
{
UI_NEW_CONTACT_Handle *handle = (UI_NEW_CONTACT_Handle*) data;
@@ -110,7 +110,7 @@ msg_error_cb(UNUSED GstBus *bus,
static void
msg_eos_cb(UNUSED GstBus *bus,
UNUSED GstMessage *msg,
- gpointer *data)
+ gpointer data)
{
UI_NEW_CONTACT_Handle *handle = (UI_NEW_CONTACT_Handle*) data;
@@ -121,7 +121,7 @@ msg_eos_cb(UNUSED GstBus *bus,
static void
msg_state_changed_cb(UNUSED GstBus *bus,
GstMessage *msg,
- gpointer *data)
+ gpointer data)
{
UI_NEW_CONTACT_Handle *handle = (UI_NEW_CONTACT_Handle*) data;
@@ -144,7 +144,7 @@ msg_state_changed_cb(UNUSED GstBus *bus,
static void
msg_barcode_cb(UNUSED GstBus *bus,
GstMessage *msg,
- gpointer *data)
+ gpointer data)
{
UI_NEW_CONTACT_Handle *handle = (UI_NEW_CONTACT_Handle*) data;
GstMessageType msg_type = GST_MESSAGE_TYPE(msg);
diff --git a/src/ui/picker.c b/src/ui/picker.c
@@ -101,6 +101,12 @@ handle_settings_button_click(UNUSED GtkButton *button,
{
MESSENGER_Application *app = (MESSENGER_Application*) user_data;
ui_play_media_window_init(app, &(app->ui.play_media));
+
+ ui_play_media_window_update(
+ &(app->ui.play_media),
+ "TODO" // uri path to video file
+ );
+
gtk_widget_show(GTK_WIDGET(app->ui.play_media.window));
}
diff --git a/src/ui/play_media.c b/src/ui/play_media.c
@@ -27,8 +27,6 @@
#include "../application.h"
#include "../ui.h"
-#include <gst/gst.h>
-
static void
handle_back_button_click(GtkButton *button,
gpointer user_data)
@@ -38,33 +36,69 @@ handle_back_button_click(GtkButton *button,
}
static void
-_disable_video_processing(UI_PLAY_MEDIA_Handle *handle,
- gboolean drop_pipeline)
+_set_media_controls_sensivity(UI_PLAY_MEDIA_Handle *handle,
+ gboolean sensitive)
{
- GNUNET_assert(handle);
-
- if (handle->preview_stack)
- gtk_stack_set_visible_child(handle->preview_stack, handle->fail_box);
+ if (handle->play_pause_button)
+ gtk_widget_set_sensitive(
+ GTK_WIDGET(handle->play_pause_button),
+ sensitive
+ );
- if ((!(handle->pipeline)) || (!drop_pipeline))
- return;
+ if (handle->volume_button)
+ gtk_widget_set_sensitive(
+ GTK_WIDGET(handle->volume_button),
+ sensitive
+ );
- gst_element_set_state(handle->pipeline, GST_STATE_NULL);
+ if (handle->timeline_scale)
+ gtk_widget_set_sensitive(
+ GTK_WIDGET(handle->timeline_scale),
+ sensitive
+ );
}
-static gboolean
-_adjust_playing_media_position(UI_PLAY_MEDIA_Handle *handle)
+static void
+handle_timeline_scale_value_changed(GtkRange *range,
+ gpointer user_data);
+
+static void
+_set_signal_connection_of_timeline(UI_PLAY_MEDIA_Handle *handle,
+ gboolean connected)
{
- gint64 pos, len;
+ if (!(handle->timeline_scale))
+ return;
- if (!(handle->pipeline))
- return FALSE;
+ if (connected == (handle->timeline_signal != 0))
+ return;
- if (!gst_element_query_position(handle->pipeline, GST_FORMAT_TIME, &pos))
- return FALSE;
+ if (connected)
+ handle->timeline_signal = g_signal_connect(
+ handle->timeline_scale,
+ "value-changed",
+ G_CALLBACK(handle_timeline_scale_value_changed),
+ handle
+ );
+ else
+ {
+ g_signal_handler_disconnect(
+ handle->timeline_scale,
+ handle->timeline_signal
+ );
- if (!gst_element_query_duration(handle->pipeline, GST_FORMAT_TIME, &len))
- return FALSE;
+ handle->timeline_signal = 0;
+ }
+}
+
+static void
+_set_media_position(UI_PLAY_MEDIA_Handle *handle,
+ gint64 pos,
+ gint64 len,
+ gboolean include_scale)
+{
+ const gdouble position = (
+ len > 0? 1.0 * pos / len : 0.0
+ );
if (handle->timeline_label)
{
@@ -74,12 +108,12 @@ _adjust_playing_media_position(UI_PLAY_MEDIA_Handle *handle)
guint len_seconds = GST_TIME_AS_SECONDS(len);
g_string_append_printf(
- str,
- "%u:%02u / %u:%02u",
- pos_seconds / 60,
- pos_seconds % 60,
- len_seconds / 60,
- len_seconds % 60
+ str,
+ "%u:%02u / %u:%02u",
+ pos_seconds / 60,
+ pos_seconds % 60,
+ len_seconds / 60,
+ len_seconds % 60
);
ui_label_set_text(handle->timeline_label, str->str);
@@ -88,52 +122,107 @@ _adjust_playing_media_position(UI_PLAY_MEDIA_Handle *handle)
if (handle->timeline_progress_bar)
gtk_progress_bar_set_fraction(
- handle->timeline_progress_bar,
- 1.0 * pos / len
+ handle->timeline_progress_bar,
+ 1.0 * position
);
- return TRUE;
+ if ((!(handle->timeline_scale)) || (!include_scale))
+ return;
+
+ _set_signal_connection_of_timeline(handle, FALSE);
+
+ gtk_range_set_value(
+ GTK_RANGE(handle->timeline_scale),
+ 100.0 * position
+ );
+
+ _set_signal_connection_of_timeline(handle, TRUE);
}
-static void
-_adjust_playing_media_state(UI_PLAY_MEDIA_Handle *handle,
- gboolean playing)
+static gboolean
+_adjust_playing_media_position(UI_PLAY_MEDIA_Handle *handle)
{
- handle->playing = playing;
+ gint64 pos, len;
- if (!(handle->play_symbol_stack))
- return;
+ if (!(handle->pipeline))
+ return FALSE;
- gtk_stack_set_visible_child_name(
- handle->play_symbol_stack,
- handle->playing? "pause_page" : "play_page"
- );
+ if (!gst_element_query_position(handle->pipeline, GST_FORMAT_TIME, &pos))
+ return FALSE;
+
+ if (!gst_element_query_duration(handle->pipeline, GST_FORMAT_TIME, &len))
+ return FALSE;
+
+ _set_media_position(handle, pos, len, TRUE);
+ return TRUE;
+}
+static void
+_set_timeout_callback_of_timeline(UI_PLAY_MEDIA_Handle *handle,
+ gboolean connected)
+{
if (handle->timeline)
g_source_remove(handle->timeline);
- if (handle->playing)
+ if (connected)
handle->timeline = g_timeout_add(
- 1000,
- G_SOURCE_FUNC(_adjust_playing_media_position),
- handle
+ 1000,
+ G_SOURCE_FUNC(_adjust_playing_media_position),
+ handle
);
else
handle->timeline = 0;
+}
- gtk_widget_set_sensitive(GTK_WIDGET(handle->play_pause_button), TRUE);
+static void
+_set_media_state(UI_PLAY_MEDIA_Handle *handle,
+ gboolean playing)
+{
+ if (handle->play_symbol_stack)
+ gtk_stack_set_visible_child_name(
+ handle->play_symbol_stack,
+ playing? "pause_page" : "play_page"
+ );
- gtk_stack_set_visible_child(
- handle->preview_stack,
- handle->video_box
- );
+ _set_timeout_callback_of_timeline(handle, playing);
+}
+
+static void
+_disable_video_processing(UI_PLAY_MEDIA_Handle *handle,
+ gboolean drop_pipeline)
+{
+ GNUNET_assert(handle);
+
+ if (handle->preview_stack)
+ gtk_stack_set_visible_child(handle->preview_stack, handle->fail_box);
+
+ _set_media_controls_sensivity(handle, FALSE);
+ _set_media_position(handle, 0, 0, TRUE);
+ _set_media_state(handle, FALSE);
+
+ if ((!(handle->pipeline)) || (!drop_pipeline))
+ return;
+
+ gst_element_set_state(handle->pipeline, GST_STATE_NULL);
+}
+
+static void
+_adjust_playing_media_state(UI_PLAY_MEDIA_Handle *handle, gboolean playing)
+{
+ _set_media_state(handle, playing);
+
+ if (handle->preview_stack)
+ gtk_stack_set_visible_child(
+ handle->preview_stack,
+ handle->video_box
+ );
}
static void
_pause_playing_media(UI_PLAY_MEDIA_Handle *handle)
{
if (!(handle->pipeline))
- _adjust_playing_media_state(handle, FALSE);
+ return;
GstStateChangeReturn ret = gst_element_set_state(
handle->pipeline,
@@ -151,7 +240,7 @@ static void
_continue_playing_media(UI_PLAY_MEDIA_Handle *handle)
{
if (!(handle->pipeline))
- _adjust_playing_media_state(handle, TRUE);
+ return;
GstStateChangeReturn ret = gst_element_set_state(
handle->pipeline,
@@ -171,9 +260,14 @@ handle_play_pause_button_click(GtkButton *button,
{
UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) user_data;
- gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
+ if (!(handle->play_symbol_stack))
+ return;
+
+ const gchar *page = gtk_stack_get_visible_child_name(
+ handle->play_symbol_stack
+ );
- if (handle->playing)
+ if (0 == g_strcmp0(page, "pause_page"))
_pause_playing_media(handle);
else
_continue_playing_media(handle);
@@ -186,18 +280,18 @@ handle_volume_button_value_changed(GtkScaleButton *button,
{
UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) user_data;
- if (!(handle->vol))
+ if (!(handle->pipeline))
return;
g_object_set(
- G_OBJECT(handle->vol),
+ G_OBJECT(handle->pipeline),
"volume",
value,
NULL
);
g_object_set(
- G_OBJECT(handle->vol),
+ G_OBJECT(handle->pipeline),
"mute",
(value <= 0.0),
NULL
@@ -205,6 +299,28 @@ handle_volume_button_value_changed(GtkScaleButton *button,
}
static void
+handle_timeline_scale_value_changed(GtkRange *range,
+ gpointer user_data)
+{
+ UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) user_data;
+ gint64 pos, len;
+
+ if (!(handle->pipeline))
+ return;
+
+ if (!gst_element_query_duration(handle->pipeline, GST_FORMAT_TIME, &len))
+ return;
+
+ pos = (gint64) (gtk_range_get_value(range) * len / 100);
+
+ if (gst_element_seek_simple(handle->pipeline,
+ GST_FORMAT_TIME,
+ GST_SEEK_FLAG_FLUSH,
+ pos))
+ _set_media_position(handle, pos, len, FALSE);
+}
+
+static void
handle_fullscreen_button_click(GtkButton *button,
gpointer user_data)
{
@@ -240,9 +356,17 @@ handle_fullscreen_button_click(GtkButton *button,
);
gtk_widget_show_all(GTK_WIDGET(handle->window));
+ hdy_flap_set_reveal_flap(handle->controls_flap, !(handle->fullscreen));
if (handle->fullscreen)
gtk_window_fullscreen(GTK_WINDOW(handle->window));
+ else
+ {
+ if (handle->motion_lost)
+ g_source_remove(handle->motion_lost);
+
+ handle->motion_lost = 0;
+ }
gtk_stack_set_visible_child_name(
handle->fullscreen_symbol_stack,
@@ -277,6 +401,10 @@ handle_media_motion_notify(GtkWidget *widget,
g_source_remove(handle->motion_lost);
hdy_flap_set_reveal_flap(handle->controls_flap, TRUE);
+
+ if (!(handle->fullscreen))
+ return FALSE;
+
handle->motion_lost = g_timeout_add_seconds(
3,
G_SOURCE_FUNC(handle_media_motion_lost),
@@ -296,7 +424,7 @@ handle_window_destroy(UNUSED GtkWidget *window,
static void
msg_error_cb(UNUSED GstBus *bus,
GstMessage *msg,
- gpointer *data)
+ gpointer data)
{
UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) data;
@@ -316,75 +444,86 @@ msg_error_cb(UNUSED GstBus *bus,
static void
msg_eos_cb(UNUSED GstBus *bus,
UNUSED GstMessage *msg,
- gpointer *data)
+ gpointer data)
{
UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) data;
- if (GST_MESSAGE_SRC(msg) == GST_OBJECT(handle->pipeline))
- _adjust_playing_media_state(handle, FALSE);
+ if (GST_MESSAGE_SRC(msg) != GST_OBJECT(handle->pipeline))
+ return;
+
+ if (handle->timeline_scale)
+ gtk_range_set_value(GTK_RANGE(handle->timeline_scale), 0.0);
+
+ _adjust_playing_media_state(handle, FALSE);
}
static void
msg_state_changed_cb(UNUSED GstBus *bus,
GstMessage *msg,
- gpointer *data)
+ gpointer data)
{
UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) data;
GstState old_state, new_state, pending_state;
gst_message_parse_state_changed(msg, &old_state, &new_state, &pending_state);
- if ((GST_MESSAGE_SRC(msg) != GST_OBJECT(handle->pipeline)) ||
- (new_state == old_state) || (!(handle->preview_stack)))
+ if (GST_MESSAGE_SRC(msg) != GST_OBJECT(handle->pipeline))
return;
if (!(handle->sink))
+ {
_disable_video_processing(handle, FALSE);
- else if (GST_STATE_PLAYING == new_state)
- _adjust_playing_media_state(handle, TRUE);
- else if (GST_STATE_PAUSED == new_state)
- _adjust_playing_media_state(handle, FALSE);
+ return;
+ }
+
+ if (GST_STATE_READY == new_state)
+ _set_media_controls_sensivity(handle, TRUE);
+
+ if ((GST_STATE_PLAYING != new_state) && (GST_STATE_PAUSED != new_state))
+ return;
+
+ _adjust_playing_media_state(handle, GST_STATE_PLAYING == new_state);
}
static void
-_setup_gst_pipeline(UI_PLAY_MEDIA_Handle *handle)
+msg_buffering_cb(UNUSED GstBus *bus,
+ GstMessage *msg,
+ gpointer data)
{
- handle->pipeline = gst_parse_launch(
- "filesrc name=source"
- " ! decodebin name=decode"
- " ! videoconvert ! video/x-raw,format=RGB"
- " ! videoconvert ! gtksink name=vsink decode."
- " ! audioconvert ! audioresample"
- " ! volume name=vol ! autoaudiosink",
- NULL
+ UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) data;
+
+ gint percent = 0;
+ gst_message_parse_buffering(msg, &percent);
+
+ GstStateChangeReturn ret = gst_element_set_state(
+ handle->pipeline,
+ (percent < 100? GST_STATE_PAUSED : GST_STATE_PLAYING)
);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ _disable_video_processing(handle, TRUE);
+}
+
+static void
+_setup_gst_pipeline(UI_PLAY_MEDIA_Handle *handle)
+{
+ handle->pipeline = gst_element_factory_make("playbin", NULL);
+
if (!(handle->pipeline))
return;
- handle->source = gst_bin_get_by_name(
- GST_BIN(handle->pipeline), "source"
- );
+ handle->sink = gst_element_factory_make("gtksink", "vsink");
+
+ if (!(handle->sink))
+ return;
g_object_set(
- G_OBJECT(handle->source),
- "location",
- "", // absolute path to video file
+ G_OBJECT(handle->pipeline),
+ "video-sink",
+ handle->sink,
NULL
);
- handle->decode = gst_bin_get_by_name(
- GST_BIN(handle->pipeline), "decode"
- );
-
- handle->sink = gst_bin_get_by_name(
- GST_BIN(handle->pipeline), "vsink"
- );
-
- handle->vol = gst_bin_get_by_name(
- GST_BIN(handle->pipeline), "vol"
- );
-
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(handle->pipeline));
if (!bus)
@@ -413,6 +552,13 @@ _setup_gst_pipeline(UI_PLAY_MEDIA_Handle *handle)
handle
);
+ g_signal_connect(
+ G_OBJECT(bus),
+ "message::buffering",
+ (GCallback) msg_buffering_cb,
+ handle
+ );
+
gst_object_unref(bus);
}
@@ -420,10 +566,7 @@ static void*
_ui_play_media_video_thread(void *args)
{
UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) args;
-
- if (!handle->playing)
- _continue_playing_media(handle);
-
+ _continue_playing_media(handle);
return NULL;
}
@@ -435,13 +578,6 @@ ui_play_media_window_init(MESSENGER_Application *app,
_setup_gst_pipeline(handle);
- pthread_create(
- &(handle->video_tid),
- NULL,
- _ui_play_media_video_thread,
- handle
- );
-
handle->parent = GTK_WINDOW(app->ui.messenger.main_window);
handle->builder = gtk_builder_new_from_resource(
@@ -555,6 +691,12 @@ ui_play_media_window_init(MESSENGER_Application *app,
gtk_builder_get_object(handle->builder, "timeline_progress_bar")
);
+ handle->timeline_scale = GTK_SCALE(
+ gtk_builder_get_object(handle->builder, "timeline_scale")
+ );
+
+ _set_signal_connection_of_timeline(handle, handle->sink? TRUE : FALSE);
+
handle->settings_button = GTK_BUTTON(
gtk_builder_get_object(handle->builder, "settings_button")
);
@@ -603,11 +745,33 @@ ui_play_media_window_init(MESSENGER_Application *app,
}
void
+ui_play_media_window_update(UI_PLAY_MEDIA_Handle *handle,
+ const gchar *uri)
+{
+ if (handle->video_tid)
+ pthread_join(handle->video_tid, NULL);
+
+ if (!(handle->pipeline))
+ return;
+
+ _disable_video_processing(handle, TRUE);
+ g_object_set(G_OBJECT(handle->pipeline), "uri", uri, NULL);
+
+ pthread_create(
+ &(handle->video_tid),
+ NULL,
+ _ui_play_media_video_thread,
+ handle
+ );
+}
+
+void
ui_play_media_window_cleanup(UI_PLAY_MEDIA_Handle *handle)
{
GNUNET_assert(handle);
- pthread_join(handle->video_tid, NULL);
+ if (handle->video_tid)
+ pthread_join(handle->video_tid, NULL);
g_object_unref(handle->builder);
diff --git a/src/ui/play_media.h b/src/ui/play_media.h
@@ -27,19 +27,16 @@
#include "messenger.h"
+#include <glib-2.0/glib.h>
#include <gstreamer-1.0/gst/gst.h>
#include <pthread.h>
typedef struct UI_PLAY_MEDIA_Handle
{
- gboolean playing;
gboolean fullscreen;
GstElement *pipeline;
- GstElement *source;
- GstElement *decode;
GstElement *sink;
- GstElement *vol;
GtkWindow *parent;
@@ -62,6 +59,7 @@ typedef struct UI_PLAY_MEDIA_Handle
GtkVolumeButton *volume_button;
GtkLabel *timeline_label;
GtkProgressBar *timeline_progress_bar;
+ GtkScale* timeline_scale;
GtkButton *settings_button;
@@ -71,6 +69,8 @@ typedef struct UI_PLAY_MEDIA_Handle
guint timeline;
guint motion_lost;
+ guint timeline_signal;
+
pthread_t video_tid;
} UI_PLAY_MEDIA_Handle;
@@ -86,6 +86,17 @@ ui_play_media_window_init(MESSENGER_Application *app,
UI_PLAY_MEDIA_Handle *handle);
/**
+ * Updates a handle for the play media window with
+ * a specific uri to play a media file from.
+ *
+ * @param handle Play media window handle
+ * @param uri URI of media file
+ */
+void
+ui_play_media_window_update(UI_PLAY_MEDIA_Handle *handle,
+ const gchar *uri);
+
+/**
* Cleans up the allocated resources and resets the
* state of a given play media window handle.
*