diff --git a/data/com.github.rafostar.Clapper.gschema.xml b/data/com.github.rafostar.Clapper.gschema.xml index 5e4ef144..ed1c57e2 100644 --- a/data/com.github.rafostar.Clapper.gschema.xml +++ b/data/com.github.rafostar.Clapper.gschema.xml @@ -14,6 +14,10 @@ 100 Custom initial volume value in percentage after startup + + false + Keep showing last video frame after playback finishes + false Automatically close the app after playback finishes diff --git a/lib/gst/clapper/gtk4/gstclapperglsink.c b/lib/gst/clapper/gtk4/gstclapperglsink.c index 73ef41dc..57511c50 100644 --- a/lib/gst/clapper/gtk4/gstclapperglsink.c +++ b/lib/gst/clapper/gtk4/gstclapperglsink.c @@ -58,6 +58,7 @@ static gboolean gst_clapper_gl_sink_propose_allocation (GstBaseSink * bsink, static gboolean gst_clapper_gl_sink_query (GstBaseSink * bsink, GstQuery * query); static gboolean gst_clapper_gl_sink_start (GstBaseSink * bsink); static gboolean gst_clapper_gl_sink_stop (GstBaseSink * bsink); +static GstFlowReturn gst_clapper_gl_sink_wait_event (GstBaseSink * bsink, GstEvent * event); static GstStateChangeReturn gst_clapper_gl_sink_change_state (GstElement * element, @@ -119,6 +120,7 @@ gst_clapper_gl_sink_class_init (GstClapperGLSinkClass * klass) gstbasesink_class->query = gst_clapper_gl_sink_query; gstbasesink_class->start = gst_clapper_gl_sink_start; gstbasesink_class->stop = gst_clapper_gl_sink_stop; + gstbasesink_class->wait_event = gst_clapper_gl_sink_wait_event; gstvideosink_class->show_frame = gst_clapper_gl_sink_show_frame; @@ -143,6 +145,9 @@ gst_clapper_gl_sink_init (GstClapperGLSink * clapper_sink) clapper_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO; clapper_sink->par_n = DEFAULT_PAR_N; clapper_sink->par_d = DEFAULT_PAR_D; + clapper_sink->keep_last_frame = DEFAULT_KEEP_LAST_FRAME; + + clapper_sink->had_eos = FALSE; } static void @@ -205,12 +210,12 @@ gst_clapper_gl_sink_get_widget (GstClapperGLSink * clapper_sink) clapper_sink->widget = (GtkClapperGLWidget *) GST_CLAPPER_GL_SINK_GET_CLASS (clapper_sink)->create_widget (); - clapper_sink->bind_aspect_ratio = - g_object_bind_property (clapper_sink, "force-aspect-ratio", clapper_sink->widget, + g_object_bind_property (clapper_sink, "force-aspect-ratio", clapper_sink->widget, "force-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); - clapper_sink->bind_pixel_aspect_ratio = - g_object_bind_property (clapper_sink, "pixel-aspect-ratio", clapper_sink->widget, + g_object_bind_property (clapper_sink, "pixel-aspect-ratio", clapper_sink->widget, "pixel-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property (clapper_sink, "keep-last-frame", clapper_sink->widget, + "keep-last-frame", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); /* Take the floating ref, other wise the destruction of the container will * make this widget disappear possibly before we are done. */ @@ -256,6 +261,9 @@ gst_clapper_gl_sink_get_property (GObject * object, guint prop_id, case PROP_PIXEL_ASPECT_RATIO: gst_value_set_fraction (value, clapper_sink->par_n, clapper_sink->par_d); break; + case PROP_KEEP_LAST_FRAME: + g_value_set_boolean (value, clapper_sink->keep_last_frame); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -276,6 +284,9 @@ gst_clapper_gl_sink_set_property (GObject * object, guint prop_id, clapper_sink->par_n = gst_value_get_fraction_numerator (value); clapper_sink->par_d = gst_value_get_fraction_denominator (value); break; + case PROP_KEEP_LAST_FRAME: + clapper_sink->keep_last_frame = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -566,6 +577,7 @@ gst_clapper_gl_sink_change_state (GstElement * element, GstStateChange transitio switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: GST_OBJECT_LOCK (clapper_sink); + clapper_sink->had_eos = FALSE; if (clapper_sink->widget) { GTK_CLAPPER_GL_WIDGET_LOCK (clapper_sink->widget); clapper_sink->widget->ignore_buffers = FALSE; @@ -591,7 +603,8 @@ gst_clapper_gl_sink_change_state (GstElement * element, GstStateChange transitio GST_OBJECT_LOCK (clapper_sink); if (clapper_sink->widget) { GTK_CLAPPER_GL_WIDGET_LOCK (clapper_sink->widget); - clapper_sink->widget->ignore_buffers = TRUE; + clapper_sink->widget->ignore_buffers = + !clapper_sink->had_eos || !clapper_sink->keep_last_frame; GTK_CLAPPER_GL_WIDGET_UNLOCK (clapper_sink->widget); } GST_OBJECT_UNLOCK (clapper_sink); @@ -683,6 +696,29 @@ gst_clapper_gl_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) return TRUE; } +static GstFlowReturn +gst_clapper_gl_sink_wait_event (GstBaseSink * bsink, GstEvent * event) +{ + GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (bsink); + GstFlowReturn ret; + + ret = GST_BASE_SINK_CLASS (parent_class)->wait_event (bsink, event); + + switch (event->type) { + case GST_EVENT_EOS: + if (ret == GST_FLOW_OK) { + GST_OBJECT_LOCK (clapper_sink); + clapper_sink->had_eos = TRUE; + GST_OBJECT_UNLOCK (clapper_sink); + } + break; + default: + break; + } + + return ret; +} + static GstFlowReturn gst_clapper_gl_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf) { diff --git a/lib/gst/clapper/gtk4/gstclapperglsink.h b/lib/gst/clapper/gtk4/gstclapperglsink.h index 32796dbc..d277ab7b 100644 --- a/lib/gst/clapper/gtk4/gstclapperglsink.h +++ b/lib/gst/clapper/gtk4/gstclapperglsink.h @@ -58,15 +58,14 @@ struct _GstClapperGLSink GtkClapperGLWidget *widget; + gboolean had_eos; + /* properties */ gboolean force_aspect_ratio; - GBinding *bind_aspect_ratio; - gint par_n, par_d; - GBinding *bind_pixel_aspect_ratio; + gboolean keep_last_frame; gboolean ignore_textures; - GBinding *bind_ignore_textures; GtkWidget *window; gulong widget_destroy_id; diff --git a/lib/gst/clapper/gtk4/gstgtkutils.c b/lib/gst/clapper/gtk4/gstgtkutils.c index c658a3eb..10e9d720 100644 --- a/lib/gst/clapper/gtk4/gstgtkutils.c +++ b/lib/gst/clapper/gtk4/gstgtkutils.c @@ -85,4 +85,11 @@ gst_gtk_install_shared_properties (GObjectClass *gobject_class) gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio", "The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D, G_MAXINT, 1, 1, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_KEEP_LAST_FRAME, + g_param_spec_boolean ("keep-last-frame", + "Keep last frame", + "Keep showing last video frame after playback instead of black screen", + DEFAULT_KEEP_LAST_FRAME, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } diff --git a/lib/gst/clapper/gtk4/gstgtkutils.h b/lib/gst/clapper/gtk4/gstgtkutils.h index 4fce7449..ced23142 100644 --- a/lib/gst/clapper/gtk4/gstgtkutils.h +++ b/lib/gst/clapper/gtk4/gstgtkutils.h @@ -25,6 +25,7 @@ #define DEFAULT_FORCE_ASPECT_RATIO TRUE #define DEFAULT_PAR_N 0 #define DEFAULT_PAR_D 1 +#define DEFAULT_KEEP_LAST_FRAME FALSE #include #include @@ -35,6 +36,7 @@ enum PROP_WIDGET, PROP_FORCE_ASPECT_RATIO, PROP_PIXEL_ASPECT_RATIO, + PROP_KEEP_LAST_FRAME }; gpointer gst_gtk_invoke_on_main (GThreadFunc func, gpointer data); diff --git a/lib/gst/clapper/gtk4/gtkclapperglwidget.c b/lib/gst/clapper/gtk4/gtkclapperglwidget.c index ff25433b..bb1cfcd5 100644 --- a/lib/gst/clapper/gtk4/gtkclapperglwidget.c +++ b/lib/gst/clapper/gtk4/gtkclapperglwidget.c @@ -163,6 +163,9 @@ gtk_clapper_gl_widget_set_property (GObject * object, guint prop_id, clapper_widget->par_n = gst_value_get_fraction_numerator (value); clapper_widget->par_d = gst_value_get_fraction_denominator (value); break; + case PROP_KEEP_LAST_FRAME: + clapper_widget->keep_last_frame = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -182,6 +185,9 @@ gtk_clapper_gl_widget_get_property (GObject * object, guint prop_id, case PROP_PIXEL_ASPECT_RATIO: gst_value_set_fraction (value, clapper_widget->par_n, clapper_widget->par_d); break; + case PROP_KEEP_LAST_FRAME: + g_value_set_boolean (value, clapper_widget->keep_last_frame); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -924,6 +930,7 @@ gtk_clapper_gl_widget_init (GtkClapperGLWidget * clapper_widget) clapper_widget->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO; clapper_widget->par_n = DEFAULT_PAR_N; clapper_widget->par_d = DEFAULT_PAR_D; + clapper_widget->keep_last_frame = DEFAULT_KEEP_LAST_FRAME; clapper_widget->ignore_buffers = FALSE; clapper_widget->last_pos_x = 0; clapper_widget->last_pos_y = 0; diff --git a/lib/gst/clapper/gtk4/gtkclapperglwidget.h b/lib/gst/clapper/gtk4/gtkclapperglwidget.h index 4f61aaba..acfbe412 100644 --- a/lib/gst/clapper/gtk4/gtkclapperglwidget.h +++ b/lib/gst/clapper/gtk4/gtkclapperglwidget.h @@ -52,6 +52,7 @@ struct _GtkClapperGLWidget /* properties */ gboolean force_aspect_ratio; gint par_n, par_d; + gboolean keep_last_frame; gint display_width; gint display_height; diff --git a/src/player.js b/src/player.js index c230f9c1..d75b94d0 100644 --- a/src/player.js +++ b/src/player.js @@ -81,7 +81,8 @@ class ClapperPlayer extends GstClapper.Clapper this._onSettingsKeyChanged(settings, key); const flag = Gio.SettingsBindFlags.GET; - settings.bind('subtitle-font', this.pipeline, 'subtitle_font_desc', flag); + settings.bind('keep-last-frame', this.widget, 'keep-last-frame', flag); + settings.bind('subtitle-font', this.pipeline, 'subtitle-font-desc', flag); } set_initial_config() diff --git a/src/prefs.js b/src/prefs.js index 2c0fb907..6d4047fb 100644 --- a/src/prefs.js +++ b/src/prefs.js @@ -41,6 +41,7 @@ class ClapperGeneralPage extends PrefsBase.Grid comboBox.connect('changed', this._onVolumeInitialChanged.bind(this, spinButton)); this.addTitle('Finish'); + this.addCheckButton('Keep showing last frame', 'keep-last-frame'); this.addCheckButton('Close after playback', 'close-auto'); }