basesrc: do not send EOS when automatic_eos is FALSE
authorMathieu Duponchelle <mathieu@centricular.com>
Tue, 13 Nov 2018 20:19:22 +0000 (21:19 +0100)
committerMathieu Duponchelle <mduponchelle1@gmail.com>
Fri, 5 Apr 2019 18:00:29 +0000 (18:00 +0000)
libs/gst/base/gstbasesrc.c
tests/check/libs/basesrc.c

index efe52e7..3902e5a 100644 (file)
@@ -656,6 +656,12 @@ gst_base_src_set_dynamic_size (GstBaseSrc * src, gboolean dynamic)
  * that can't return an authoritative size and only know that they're EOS
  * when trying to read more should set this to %FALSE.
  *
+ * When @src operates in %GST_FORMAT_TIME, #GstBaseSrc will send an EOS
+ * when a buffer outside of the currently configured segment is pushed if
+ * @automatic_eos is %TRUE. Since 1.16, if @automatic_eos is %FALSE an
+ * EOS will be pushed only when the #GstBaseSrc.create implementation
+ * returns %GST_FLOW_EOS.
+ *
  * Since: 1.4
  */
 void
@@ -2931,7 +2937,8 @@ gst_base_src_loop (GstPad * pad)
       /* positive rate, check if we reached the stop */
       if (src->segment.stop != -1) {
         if (position >= src->segment.stop) {
-          eos = TRUE;
+          if (g_atomic_int_get (&src->priv->automatic_eos))
+            eos = TRUE;
           position = src->segment.stop;
         }
       }
@@ -2939,7 +2946,8 @@ gst_base_src_loop (GstPad * pad)
       /* negative rate, check if we reached the start. start is always set to
        * something different from -1 */
       if (position <= src->segment.start) {
-        eos = TRUE;
+        if (g_atomic_int_get (&src->priv->automatic_eos))
+          eos = TRUE;
         position = src->segment.start;
       }
       /* when going reverse, all buffers are DISCONT */
index ee360ce..afd5da8 100644 (file)
@@ -904,6 +904,134 @@ GST_START_TEST (basesrc_create_bufferlist)
 
 GST_END_TEST;
 
+typedef struct
+{
+  GstBaseSrc parent;
+  GstSegment *segment;
+  gboolean n_output_buffers;
+} TimeSrc;
+
+typedef GstBaseSrcClass TimeSrcClass;
+
+static GType time_src_get_type (void);
+
+G_DEFINE_TYPE (TimeSrc, time_src, GST_TYPE_BASE_SRC);
+
+static void
+time_src_init (TimeSrc * src)
+{
+  gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
+  gst_base_src_set_automatic_eos (GST_BASE_SRC (src), FALSE);
+  src->n_output_buffers = 0;
+}
+
+/* This test src outputs a compressed format, with a single GOP
+ * starting at PTS 0.
+ *
+ * This means that in reverse playback, we may want to output
+ * buffers outside the segment bounds, and decide when to EOS
+ * from the create function.
+ */
+static GstFlowReturn
+time_src_create (GstBaseSrc * bsrc, guint64 offset, guint size,
+    GstBuffer ** p_buf)
+{
+  TimeSrc *src = (TimeSrc *) bsrc;
+
+  if (src->segment->position >= src->segment->stop)
+    return GST_FLOW_EOS;
+
+  *p_buf = gst_buffer_new ();
+  GST_BUFFER_PTS (*p_buf) = src->segment->position;
+  GST_BUFFER_DURATION (*p_buf) = GST_SECOND;
+  src->segment->position += GST_SECOND;
+
+  src->n_output_buffers++;
+
+  return GST_FLOW_OK;
+}
+
+static gboolean
+time_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment)
+{
+  TimeSrc *src = (TimeSrc *) bsrc;
+  fail_unless (segment->format == GST_FORMAT_TIME);
+
+  if (src->segment)
+    gst_segment_free (src->segment);
+
+  src->segment = gst_segment_copy (segment);
+
+  ((TimeSrc *) bsrc)->segment->position = 0;
+
+  return TRUE;
+}
+
+static gboolean
+time_src_is_seekable (GstBaseSrc * bsrc)
+{
+  return TRUE;
+}
+
+static void
+time_src_class_init (TimeSrcClass * klass)
+{
+  GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
+
+  gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
+      &src_template);
+
+  gstbasesrc_class->create = time_src_create;
+  gstbasesrc_class->do_seek = time_src_do_seek;
+  gstbasesrc_class->is_seekable = time_src_is_seekable;
+}
+
+GST_START_TEST (basesrc_time_automatic_eos)
+{
+  GstElement *src, *sink;
+  GstElement *pipe;
+  GstBus *bus;
+
+  src = g_object_new (time_src_get_type (), NULL);
+  sink = gst_element_factory_make ("fakesink", NULL);
+
+  pipe = gst_pipeline_new (NULL);
+
+  gst_bin_add (GST_BIN (pipe), src);
+  gst_bin_add (GST_BIN (pipe), sink);
+
+  gst_element_link (src, sink);
+
+  gst_element_set_state (pipe, GST_STATE_PAUSED);
+  gst_element_get_state (pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
+
+  gst_element_seek (pipe, -1.0, GST_FORMAT_TIME,
+      GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET,
+      GST_SECOND, GST_SEEK_TYPE_SET, 2 * GST_SECOND);
+
+  gst_element_set_state (pipe, GST_STATE_PLAYING);
+
+  bus = gst_pipeline_get_bus (GST_PIPELINE (pipe));
+
+  gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_EOS);
+
+  gst_element_set_state (pipe, GST_STATE_NULL);
+
+  /* - One preroll buffer
+   * - One (1-second) buffer outside the segment, our "keyframe"
+   * - One buffer inside the segment
+   */
+  fail_unless_equals_int (((TimeSrc *) src)->n_output_buffers, 3);
+
+  if (((TimeSrc *) src)->segment)
+    gst_segment_free (((TimeSrc *) src)->segment);
+
+  gst_object_unref (bus);
+  gst_object_unref (pipe);
+}
+
+GST_END_TEST;
+
 static Suite *
 gst_basesrc_suite (void)
 {
@@ -920,6 +1048,7 @@ gst_basesrc_suite (void)
   tcase_add_test (tc, basesrc_seek_events_rate_update);
   tcase_add_test (tc, basesrc_seek_on_last_buffer);
   tcase_add_test (tc, basesrc_create_bufferlist);
+  tcase_add_test (tc, basesrc_time_automatic_eos);
 
   return s;
 }