Allow basesrc derived classes to execute seeks in other formats by providing a prepar...
authorJan Schmidt <thaytan@mad.scientist.com>
Fri, 13 Apr 2007 14:18:44 +0000 (14:18 +0000)
committerJan Schmidt <thaytan@mad.scientist.com>
Fri, 13 Apr 2007 14:18:44 +0000 (14:18 +0000)
Original commit message from CVS:
* docs/libs/gstreamer-libs-sections.txt:
* libs/gst/base/gstbasesrc.c: (gst_base_src_class_init),
(gst_base_src_default_prepare_seek_segment),
(gst_base_src_prepare_seek_segment), (gst_base_src_perform_seek):
* libs/gst/base/gstbasesrc.h:
Allow basesrc derived classes to execute seeks in other formats
by providing a prepare_seek_segment vmethod. Sub-classes can choose
to prepare the GstSegment in any format that their perform_seek method
will be able to understand. The default implementation provides the
old behaviour of attempting to convert the seek offsets to the
configured native format.

ChangeLog
docs/libs/gstreamer-libs-sections.txt
libs/gst/base/gstbasesrc.c
libs/gst/base/gstbasesrc.h

index 67ed0a8..487ae0e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,19 @@
 2007-04-13  Jan Schmidt  <thaytan@mad.scientist.com>
 
+       * docs/libs/gstreamer-libs-sections.txt:
+       * libs/gst/base/gstbasesrc.c: (gst_base_src_class_init),
+       (gst_base_src_default_prepare_seek_segment),
+       (gst_base_src_prepare_seek_segment), (gst_base_src_perform_seek):
+       * libs/gst/base/gstbasesrc.h:
+       Allow basesrc derived classes to execute seeks in other formats
+       by providing a prepare_seek_segment vmethod. Sub-classes can choose
+       to prepare the GstSegment in any format that their perform_seek method
+       will be able to understand. The default implementation provides the
+       old behaviour of attempting to convert the seek offsets to the 
+       configured native format.
+
+2007-04-13  Jan Schmidt  <thaytan@mad.scientist.com>
+
        * gst/gstelement.c: (gst_element_get_state_func):
        Don't output the same debug statement twice.
 
index 720a20f..47f1bcb 100644 (file)
@@ -154,6 +154,7 @@ gst_base_src_wait_playing
 gst_base_src_is_live
 gst_base_src_set_live
 gst_base_src_set_format
+gst_base_src_send_new_segment
 
 GST_BASE_SRC_PAD
 <SUBSECTION Standard>
index e815a9b..6da492e 100644 (file)
@@ -288,6 +288,8 @@ static gboolean gst_base_src_default_negotiate (GstBaseSrc * basesrc);
 static gboolean gst_base_src_default_do_seek (GstBaseSrc * src,
     GstSegment * segment);
 static gboolean gst_base_src_default_query (GstBaseSrc * src, GstQuery * query);
+static gboolean gst_base_src_default_prepare_seek_segment (GstBaseSrc * src,
+    GstEvent * event, GstSegment * segment);
 
 static gboolean gst_base_src_unlock (GstBaseSrc * basesrc);
 static gboolean gst_base_src_unlock_stop (GstBaseSrc * basesrc);
@@ -353,6 +355,8 @@ gst_base_src_class_init (GstBaseSrcClass * klass)
   klass->query = GST_DEBUG_FUNCPTR (gst_base_src_default_query);
   klass->check_get_range =
       GST_DEBUG_FUNCPTR (gst_base_src_default_check_get_range);
+  klass->prepare_seek_segment =
+      GST_DEBUG_FUNCPTR (gst_base_src_default_prepare_seek_segment);
 }
 
 static void
@@ -808,6 +812,84 @@ gst_base_src_do_seek (GstBaseSrc * src, GstSegment * segment)
   return result;
 }
 
+#define SEEK_TYPE_IS_RELATIVE(t) (((t) != GST_SEEK_TYPE_NONE) && ((t) != GST_SEEK_TYPE_SET))
+
+static gboolean
+gst_base_src_default_prepare_seek_segment (GstBaseSrc * src, GstEvent * event,
+    GstSegment * segment)
+{
+  /* By default, we try one of 2 things:
+   *   - For absolute seek positions, convert the requested position to our 
+   *     configured processing format and place it in the output segment \
+   *   - For relative seek positions, convert our current (input) values to the
+   *     seek format, adjust by the relative seek offset and then convert back to
+   *     the processing format
+   */
+  GstSeekType cur_type, stop_type;
+  gint64 cur, stop;
+  GstSeekFlags flags;
+  GstFormat seek_format, dest_format;
+  gdouble rate;
+  gboolean update;
+  gboolean res = TRUE;
+
+  gst_event_parse_seek (event, &rate, &seek_format, &flags,
+      &cur_type, &cur, &stop_type, &stop);
+  dest_format = segment->format;
+
+  if (seek_format == dest_format) {
+    gst_segment_set_seek (segment, rate, seek_format, flags,
+        cur_type, cur, stop_type, stop, &update);
+    return TRUE;
+  }
+
+  if (cur_type != GST_SEEK_TYPE_NONE) {
+    /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */
+    res =
+        gst_pad_query_convert (src->srcpad, seek_format, cur, &dest_format,
+        &cur);
+    cur_type = GST_SEEK_TYPE_SET;
+  }
+
+  if (res && stop_type != GST_SEEK_TYPE_NONE) {
+    /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */
+    res =
+        gst_pad_query_convert (src->srcpad, seek_format, stop, &dest_format,
+        &stop);
+    stop_type = GST_SEEK_TYPE_SET;
+  }
+
+  /* And finally, configure our output segment in the desired format */
+  gst_segment_set_seek (segment, rate, dest_format, flags, cur_type, cur,
+      stop_type, stop, &update);
+
+  if (!res)
+    goto no_format;
+
+  return res;
+
+no_format:
+  {
+    GST_DEBUG_OBJECT (src, "undefined format given, seek aborted.");
+    return FALSE;
+  }
+}
+
+static gboolean
+gst_base_src_prepare_seek_segment (GstBaseSrc * src, GstEvent * event,
+    GstSegment * seeksegment)
+{
+  GstBaseSrcClass *bclass;
+  gboolean result = FALSE;
+
+  bclass = GST_BASE_SRC_GET_CLASS (src);
+
+  if (bclass->prepare_seek_segment)
+    result = bclass->prepare_seek_segment (src, event, seeksegment);
+
+  return result;
+}
+
 /* this code implements the seeking. It is a good example
  * handling all cases.
  *
@@ -861,44 +943,46 @@ gst_base_src_do_seek (GstBaseSrc * src, GstSegment * segment)
 static gboolean
 gst_base_src_perform_seek (GstBaseSrc * src, GstEvent * event, gboolean unlock)
 {
-  gboolean res;
+  gboolean res = TRUE;
   gdouble rate;
-  GstFormat format;
+  GstFormat seek_format, dest_format;
   GstSeekFlags flags;
   GstSeekType cur_type, stop_type;
   gint64 cur, stop;
   gboolean flush;
   gboolean update;
+  gboolean relative_seek = FALSE;
+  gboolean seekseg_configured = FALSE;
   GstSegment seeksegment;
 
   GST_DEBUG_OBJECT (src, "doing seek");
 
+  dest_format = src->segment.format;
+
   if (event) {
-    gst_event_parse_seek (event, &rate, &format, &flags,
+    gst_event_parse_seek (event, &rate, &seek_format, &flags,
         &cur_type, &cur, &stop_type, &stop);
 
-    /* we have to have a format as the segment format. Try to convert
-     * if not. */
-    if (src->segment.format != format) {
-      GstFormat fmt;
+    relative_seek = SEEK_TYPE_IS_RELATIVE (cur_type) ||
+        SEEK_TYPE_IS_RELATIVE (stop_type);
 
-      fmt = src->segment.format;
-      res = TRUE;
-      if (cur_type != GST_SEEK_TYPE_NONE)
-        res = gst_pad_query_convert (src->srcpad, format, cur, &fmt, &cur);
-      if (res && stop_type != GST_SEEK_TYPE_NONE)
-        res = gst_pad_query_convert (src->srcpad, format, stop, &fmt, &stop);
-      if (!res)
-        goto no_format;
-
-      format = fmt;
+    if (dest_format != seek_format && !relative_seek) {
+      /* If we have an ABSOLUTE position (SEEK_SET only), we can convert it
+       * here before taking the stream lock, otherwise we must convert it later,
+       * once we have the stream lock and can read the current position */
+      gst_segment_init (&seeksegment, dest_format);
+
+      if (!gst_base_src_prepare_seek_segment (src, event, &seeksegment))
+        goto prepare_failed;
+
+      seekseg_configured = TRUE;
     }
+
+    flush = flags & GST_SEEK_FLAG_FLUSH;
   } else {
-    flags = 0;
+    flush = FALSE;
   }
 
-  flush = flags & GST_SEEK_FLAG_FLUSH;
-
   /* send flush start */
   if (flush)
     gst_pad_push_event (src->srcpad, gst_event_new_flush_start ());
@@ -917,22 +1001,42 @@ gst_base_src_perform_seek (GstBaseSrc * src, GstEvent * event, gboolean unlock)
   if (unlock)
     gst_base_src_unlock_stop (src);
 
-  /* make copy into temp structure, we can only update the main one
-   * when the subclass actually could do the seek. */
-  memcpy (&seeksegment, &src->segment, sizeof (GstSegment));
-
-  /* now configure the seek segment */
-  if (event) {
-    gst_segment_set_seek (&seeksegment, rate, format, flags,
-        cur_type, cur, stop_type, stop, &update);
+  /* If we configured the seeksegment above, don't overwrite it now. Otherwise
+   * copy the current segment info into the temp segment that we can actually
+   * attempt the seek with. We only update the real segment if the seek suceeds. */
+  if (!seekseg_configured) {
+    memcpy (&seeksegment, &src->segment, sizeof (GstSegment));
+
+    /* now configure the final seek segment */
+    if (event) {
+      if (src->segment.format != seek_format) {
+        /* OK, here's where we give the subclass a chance to convert the relative
+         * seek into an absolute one in the processing format. We set up any
+         * absolute seek above, before taking the stream lock. */
+        if (!gst_base_src_prepare_seek_segment (src, event, &seeksegment)) {
+          GST_DEBUG_OBJECT (src, "Preparing the seek failed after flushing. "
+              "Aborting seek");
+          res = FALSE;
+        }
+      } else {
+        /* The seek format matches our processing format, no need to ask the
+         * the subclass to configure the segment. */
+        gst_segment_set_seek (&seeksegment, rate, seek_format, flags,
+            cur_type, cur, stop_type, stop, &update);
+      }
+    }
+    /* Else, no seek event passed, so we're just (re)starting the 
+       current segment. */
   }
 
-  GST_DEBUG_OBJECT (src, "segment configured from %" G_GINT64_FORMAT
-      " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT,
-      seeksegment.start, seeksegment.stop, seeksegment.last_stop);
+  if (res) {
+    GST_DEBUG_OBJECT (src, "segment configured from %" G_GINT64_FORMAT
+        " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT,
+        seeksegment.start, seeksegment.stop, seeksegment.last_stop);
 
-  /* do the seek, segment.last_stop contains new position. */
-  res = gst_base_src_do_seek (src, &seeksegment);
+    /* do the seek, segment.last_stop contains the new position. */
+    res = gst_base_src_do_seek (src, &seeksegment);
+  }
 
   /* and prepare to continue streaming */
   if (flush) {
@@ -954,6 +1058,14 @@ gst_base_src_perform_seek (GstBaseSrc * src, GstEvent * event, gboolean unlock)
         src->segment.start, src->segment.last_stop, src->segment.time);
   }
 
+  /* The subclass must have converted the segment to the processing format 
+   * by now */
+  if (res && seeksegment.format != dest_format) {
+    GST_DEBUG_OBJECT (src, "Subclass failed to prepare a seek segment "
+        "in the correct format. Aborting seek.");
+    res = FALSE;
+  }
+
   /* if successfull seek, we update our real segment and push
    * out the new segment. */
   if (res) {
@@ -996,11 +1108,10 @@ gst_base_src_perform_seek (GstBaseSrc * src, GstEvent * event, gboolean unlock)
   return res;
 
   /* ERROR */
-no_format:
-  {
-    GST_DEBUG_OBJECT (src, "undefined format given, seek aborted.");
-    return FALSE;
-  }
+prepare_failed:
+  GST_DEBUG_OBJECT (src, "Preparing the seek failed before flushing. "
+      "Aborting seek");
+  return FALSE;
 }
 
 static const GstQueryType *
index 0acf741..e34a9c5 100644 (file)
@@ -137,6 +137,11 @@ struct _GstBaseSrc {
  * @event: Override this to implement custom event handling.
  * @create: Ask the subclass to create a buffer with offset and size.
  * @do_seek: Perform seeking on the resource to the indicated segment.
+ * @prepare_seek_segment: Prepare the GstSegment that will be passed to the 
+ *   do_seek vmethod for executing a seek request. Sub-classes should override 
+ *   this if they support seeking in formats other than the configured native 
+ *   format. By default, it tries to convert the seek arguments to the 
+ *   configured native format and prepare a segment in that format.
  * @query: Handle a requested query. 
  * @check_get_range: Check whether the source would support pull-based 
  *   operation if it were to be opened now. This vfunc is optional, but 
@@ -213,8 +218,13 @@ struct _GstBaseSrcClass {
   /* Clear any pending unlock request, as we succeeded in unlocking */
   gboolean      (*unlock_stop)  (GstBaseSrc *src);
 
+  /* Prepare the segment on which to perform do_seek(), converting to the
+   * current basesrc format. */
+  gboolean      (*prepare_seek_segment) (GstBaseSrc *src, GstEvent *seek, 
+                                         GstSegment *segment); 
+
   /*< private >*/
-  gpointer       _gst_reserved[GST_PADDING_LARGE - 5];
+  gpointer       _gst_reserved[GST_PADDING_LARGE - 6];
 };
 
 GType gst_base_src_get_type (void);