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');
}