libs/gst/base/gstbasesrc.c: Update docs. blocksize == 0 now means the default blocksi...
authorWim Taymans <wim.taymans@gmail.com>
Thu, 6 Jul 2006 15:46:25 +0000 (15:46 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Thu, 6 Jul 2006 15:46:25 +0000 (15:46 +0000)
Original commit message from CVS:
* libs/gst/base/gstbasesrc.c: (gst_base_src_class_init),
(gst_base_src_wait), (gst_base_src_update_length),
(gst_base_src_get_range), (gst_base_src_default_check_get_range),
(gst_base_src_check_get_range), (gst_base_src_pad_check_get_range),
(gst_base_src_loop), (gst_base_src_start),
(gst_base_src_activate_pull):
Update docs.
blocksize == 0 now means the default blocksize when working in push
based mode.
Remove some pointless asserts in _wait function.
Fix offset/length calculations and EOS handling. We can now pull 0
bytes as well, which is allowed.
use _check_get_range() to decide if we can operate in _pull based
mode.
Fix refcounting leak when check_get_range function was not
implemented.
API GstBaseSrc::blocksize range can be 0 too now (default)
* tests/check/elements/filesrc.c: (GST_START_TEST),
(filesrc_suite):
Added check to test _get_range() behaviour.

ChangeLog
libs/gst/base/gstbasesrc.c
tests/check/elements/filesrc.c

index 40082cd..0389601 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,29 @@
 2006-07-06  Wim Taymans  <wim@fluendo.com>
 
+       * libs/gst/base/gstbasesrc.c: (gst_base_src_class_init),
+       (gst_base_src_wait), (gst_base_src_update_length),
+       (gst_base_src_get_range), (gst_base_src_default_check_get_range),
+       (gst_base_src_check_get_range), (gst_base_src_pad_check_get_range),
+       (gst_base_src_loop), (gst_base_src_start),
+       (gst_base_src_activate_pull):
+       Update docs.
+       blocksize == 0 now means the default blocksize when working in push
+       based mode.
+       Remove some pointless asserts in _wait function.
+       Fix offset/length calculations and EOS handling. We can now pull 0
+       bytes as well, which is allowed.
+       use _check_get_range() to decide if we can operate in _pull based
+       mode.
+       Fix refcounting leak when check_get_range function was not 
+       implemented.
+       API GstBaseSrc::blocksize range can be 0 too now (default)
+
+       * tests/check/elements/filesrc.c: (GST_START_TEST),
+       (filesrc_suite):
+       Added check to test _get_range() behaviour.
+
+2006-07-06  Wim Taymans  <wim@fluendo.com>
+
        * gst/gstpad.c: (gst_pad_chain_unchecked), (gst_pad_chain),
        (gst_pad_push), (gst_pad_check_pull_range), (gst_pad_get_range),
        (gst_pad_pull_range):
index 2032afe..3c961be 100644 (file)
  * </itemizedlist>
  * </para>
  * <para>
+ * Since 0.10.9, any #GstBaseSrc can enable pull based scheduling at any 
+ * time by overriding #GstBaseSrc::check_get_range so that it returns TRUE. 
+ * </para>
+ * <para>
  * If all the conditions are met for operating in pull mode, #GstBaseSrc is
  * automatically seekable in push mode as well. The following conditions must 
  * be met to make the element seekable in push mode when the format is not
  * </itemizedlist>
  * </para>
  * <para>
- * When the default format is not GST_FORMAT_BYTES, the subclass should ignore 
- * the offset and length in the #GstBaseSrc::create method. It is recommended 
- * to subclass #GstPushSrc instead in this situation.
+ * When the element does not meet the requirements to operate in pull mode,
+ * the offset and length in the #GstBaseSrc::create method should be ignored.
+ * It is recommended to subclass #GstPushSrc instead, in this situation. If the
+ * element can operate in pull mode but only with specific offsets and
+ * lengths, it is allowed to generate an error when the wrong values are passed
+ * to the #GstBaseSrc::create function.
  * </para>
  * <para>
  * #GstBaseSrc has support for live sources. Live sources are sources that 
  * thread might be blocked in PREROLL.
  * </para>
  * <para>
- * Last reviewed on 2006-04-14 (0.10.6)
+ * Last reviewed on 2006-07-06 (0.10.9)
  * </para>
  * </refsect2>
  */
@@ -316,8 +323,8 @@ gst_base_src_class_init (GstBaseSrcClass * klass)
 
   g_object_class_install_property (gobject_class, PROP_BLOCKSIZE,
       g_param_spec_ulong ("blocksize", "Block size",
-          "Size in bytes to read per buffer", 1, G_MAXULONG, DEFAULT_BLOCKSIZE,
-          G_PARAM_READWRITE));
+          "Size in bytes to read per buffer (0 = default)", 0, G_MAXULONG,
+          DEFAULT_BLOCKSIZE, G_PARAM_READWRITE));
   g_object_class_install_property (gobject_class, PROP_NUM_BUFFERS,
       g_param_spec_int ("num-buffers", "num-buffers",
           "Number of buffers to output before sending EOS", -1, G_MAXINT,
@@ -1107,7 +1114,7 @@ gst_base_src_get_property (GObject * object, guint prop_id, GValue * value,
   }
 }
 
-/* with STREAM_LOCK and LOCK*/
+/* with STREAM_LOCK and LOCK */
 static GstClockReturn
 gst_base_src_wait (GstBaseSrc * basesrc, GstClockTime time)
 {
@@ -1119,10 +1126,6 @@ gst_base_src_wait (GstBaseSrc * basesrc, GstClockTime time)
   if ((clock = GST_ELEMENT_CLOCK (basesrc)) == NULL)
     return GST_CLOCK_OK;
 
-  /* clock_id should be NULL outside of this function */
-  g_assert (basesrc->clock_id == NULL);
-  g_assert (GST_CLOCK_TIME_IS_VALID (time));
-
   id = gst_clock_new_single_shot_id (clock, time);
 
   basesrc->clock_id = id;
@@ -1195,6 +1198,7 @@ gst_base_src_update_length (GstBaseSrc * src, guint64 offset, guint * length)
   if (src->segment.format != GST_FORMAT_BYTES)
     return TRUE;
 
+  /* get total file size */
   size = (guint64) src->segment.duration;
 
   /* the max amount of bytes to read is the total size or
@@ -1209,30 +1213,33 @@ gst_base_src_update_length (GstBaseSrc * src, guint64 offset, guint * length)
       ", segment.stop %" G_GINT64_FORMAT ", maxsize %" G_GINT64_FORMAT, offset,
       *length, size, src->segment.stop, maxsize);
 
-  /* check size */
+  /* check size if we have one */
   if (maxsize != -1) {
-    if (offset > maxsize)
-      goto unexpected_length;
-
-    if (offset + *length > maxsize) {
+    /* if we run past the end, check if the file became bigger and 
+     * retry. */
+    if (G_UNLIKELY (offset + *length >= maxsize)) {
       /* see if length of the file changed */
       if (bclass->get_size)
         bclass->get_size (src, &size);
 
+      /* make sure we don't exceed the configured segment stop
+       * if it was set */
       if (src->segment.stop != -1)
         maxsize = MIN (size, src->segment.stop);
       else
         maxsize = size;
 
-      if (offset + *length > maxsize) {
+      /* if we are at or past the end, EOS */
+      if (G_UNLIKELY (offset >= maxsize))
+        goto unexpected_length;
+
+      /* else we can clip to the end */
+      if (G_UNLIKELY (offset + *length >= maxsize))
         *length = maxsize - offset;
-      }
     }
   }
-  if (*length == 0)
-    goto unexpected_length;
 
-  /* keep track f current position. segment is in bytes, we checked 
+  /* keep track of current position. segment is in bytes, we checked 
    * that above. */
   gst_segment_set_last_stop (&src->segment, GST_FORMAT_BYTES, offset);
 
@@ -1257,46 +1264,48 @@ gst_base_src_get_range (GstBaseSrc * src, guint64 offset, guint length,
 
   GST_LIVE_LOCK (src);
   if (src->is_live) {
-    while (!src->live_running) {
+    while (G_UNLIKELY (!src->live_running)) {
       GST_DEBUG ("live source signal waiting");
       GST_LIVE_SIGNAL (src);
       GST_DEBUG ("live source waiting for running state");
       GST_LIVE_WAIT (src);
       GST_DEBUG ("live source unlocked");
     }
-    /* FIXME, use another variable to signal stopping */
+    /* FIXME, use another variable to signal stopping so that we don't
+     * have to grab another lock. */
     GST_OBJECT_LOCK (src->srcpad);
-    if (GST_PAD_IS_FLUSHING (src->srcpad))
+    if (G_UNLIKELY (GST_PAD_IS_FLUSHING (src->srcpad)))
       goto flushing;
     GST_OBJECT_UNLOCK (src->srcpad);
   }
   GST_LIVE_UNLOCK (src);
 
-  if (!GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_STARTED))
+  if (G_UNLIKELY (!GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_STARTED)))
     goto not_started;
 
   if (G_UNLIKELY (!bclass->create))
     goto no_function;
 
-  if (!gst_base_src_update_length (src, offset, &length))
+  if (G_UNLIKELY (!gst_base_src_update_length (src, offset, &length)))
     goto unexpected_length;
 
-  if (src->num_buffers_left == 0) {
-    goto reached_num_buffers;
-  } else {
-    if (src->num_buffers_left > 0)
+  /* normally we don't count buffers */
+  if (G_UNLIKELY (src->num_buffers_left >= 0)) {
+    if (src->num_buffers_left == 0)
+      goto reached_num_buffers;
+    else
       src->num_buffers_left--;
   }
 
   GST_DEBUG_OBJECT (src,
-      "calling create offset %" G_GUINT64_FORMAT " %" G_GINT64_FORMAT, offset,
-      src->segment.time);
+      "calling create offset %" G_GUINT64_FORMAT " length %u, time %"
+      G_GINT64_FORMAT, offset, length, src->segment.time);
 
   ret = bclass->create (src, offset, length, buf);
-  if (ret != GST_FLOW_OK)
+  if (G_UNLIKELY (ret != GST_FLOW_OK))
     goto done;
 
-  /* no timestamp set and we are at offset 0 */
+  /* no timestamp set and we are at offset 0, we can timestamp with 0 */
   if (offset == 0 && src->segment.time == 0
       && GST_BUFFER_TIMESTAMP (*buf) == -1)
     GST_BUFFER_TIMESTAMP (*buf) = 0;
@@ -1311,6 +1320,10 @@ gst_base_src_get_range (GstBaseSrc * src, guint64 offset, guint length,
       GST_DEBUG_OBJECT (src, "buffer ok");
       break;
     default:
+      /* this case is triggered when we were waiting for the clock and
+       * it got unlocked because we did a state change. We return 
+       * WRONG_STATE in this case to stop the dataflow also get rid of the
+       * produced buffer. */
       GST_DEBUG_OBJECT (src, "clock returned %d, not returning", status);
       gst_buffer_unref (*buf);
       *buf = NULL;
@@ -1368,45 +1381,60 @@ gst_base_src_pad_get_range (GstPad * pad, guint64 offset, guint length,
 }
 
 static gboolean
-gst_base_src_pad_check_get_range (GstPad * pad)
+gst_base_src_default_check_get_range (GstBaseSrc * src)
 {
-  GstBaseSrcClass *bclass;
-  GstBaseSrc *src;
   gboolean res;
 
-  src = GST_BASE_SRC (gst_pad_get_parent (pad));
+  if (!GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_STARTED)) {
+    GST_LOG_OBJECT (src, "doing start/stop to check get_range support");
+    gst_base_src_start (src);
+    gst_base_src_stop (src);
+  }
+
+  /* we can operate in getrange mode if the native format is bytes
+   * and we are seekable, this condition is set in the random_access
+   * flag and is set in the _start() method. */
+  res = src->random_access;
+
+  return res;
+}
+
+static gboolean
+gst_base_src_check_get_range (GstBaseSrc * src)
+{
+  GstBaseSrcClass *bclass;
+  gboolean res;
 
   bclass = GST_BASE_SRC_GET_CLASS (src);
 
-  if (bclass->check_get_range == NULL) {
-    GST_WARNING_OBJECT (src, "no check_get_range function set");
-    return FALSE;
-  }
+  if (bclass->check_get_range == NULL)
+    goto no_function;
 
   res = bclass->check_get_range (src);
   GST_LOG_OBJECT (src, "%s() returned %d",
       GST_DEBUG_FUNCPTR_NAME (bclass->check_get_range), (gint) res);
 
-  gst_object_unref (src);
-
   return res;
+
+  /* ERRORS */
+no_function:
+  {
+    GST_WARNING_OBJECT (src, "no check_get_range function set");
+    return FALSE;
+  }
 }
 
 static gboolean
-gst_base_src_default_check_get_range (GstBaseSrc * src)
+gst_base_src_pad_check_get_range (GstPad * pad)
 {
+  GstBaseSrc *src;
   gboolean res;
 
-  if (!GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_STARTED)) {
-    GST_LOG_OBJECT (src, "doing start/stop to check get_range support");
-    gst_base_src_start (src);
-    gst_base_src_stop (src);
-  }
+  src = GST_BASE_SRC (gst_pad_get_parent (pad));
 
-  /* we can operate in getrange mode if the native format is bytes
-   * and we are seekable, this condition is set in the random_access
-   * flag and is set in the _start() method. */
-  res = src->random_access;
+  res = gst_base_src_check_get_range (src);
+
+  gst_object_unref (src);
 
   return res;
 }
@@ -1444,7 +1472,7 @@ gst_base_src_loop (GstPad * pad)
 
   ret = gst_base_src_get_range (src, position, src->blocksize, &buf);
   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
-    GST_INFO_OBJECT (src, "pausing after gst_base_src_get_range() =  %d", ret);
+    GST_INFO_OBJECT (src, "pausing after gst_base_src_get_range() = %d", ret);
     goto pause;
   }
   if (G_UNLIKELY (buf == NULL))
@@ -1496,7 +1524,7 @@ gst_base_src_loop (GstPad * pad)
 
   ret = gst_pad_push (pad, buf);
   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
-    GST_INFO_OBJECT (src, "pausing after gst_pad_push() =  %d", ret);
+    GST_INFO_OBJECT (src, "pausing after gst_pad_push() = %d", ret);
     goto pause;
   }
 
@@ -1534,7 +1562,7 @@ pause:
          * first so the app knows about the error first. */
         GST_ELEMENT_ERROR (src, STREAM, FAILED,
             (_("Internal data flow error.")),
-            ("streaming task paused, reason %s", reason));
+            ("streaming task paused, reason %s (%d)", reason, ret));
         gst_pad_push_event (pad, gst_event_new_eos ());
         src->priv->last_sent_eos = TRUE;
       }
@@ -1748,6 +1776,7 @@ gst_base_src_start (GstBaseSrc * basesrc)
 could_not_start:
   {
     GST_DEBUG_OBJECT (basesrc, "could not start");
+    /* subclass is supposed to post a message */
     return FALSE;
   }
 could_not_negotiate:
@@ -1868,10 +1897,9 @@ gst_base_src_activate_pull (GstPad * pad, gboolean active)
       goto error_start;
 
     /* if not random_access, we cannot operate in pull mode for now */
-    if (!basesrc->random_access) {
-      gst_base_src_stop (basesrc);
-      return FALSE;
-    }
+    if (!gst_base_src_check_get_range (basesrc))
+      goto no_get_range;
+
     return TRUE;
   } else {
     GST_DEBUG_OBJECT (basesrc, "Deactivating in pull mode");
@@ -1891,6 +1919,12 @@ error_start:
     GST_DEBUG_OBJECT (basesrc, "Failed to start in pull mode");
     return FALSE;
   }
+no_get_range:
+  {
+    GST_DEBUG_OBJECT (basesrc, "Cannot operate in pull mode, stopping");
+    gst_base_src_stop (basesrc);
+    return FALSE;
+  }
 error_stop:
   {
     GST_DEBUG_OBJECT (basesrc, "Failed to stop in pull mode");
index ae0ffea..bb4a3fd 100644 (file)
@@ -100,6 +100,132 @@ GST_START_TEST (test_seeking)
 
 GST_END_TEST;
 
+GST_START_TEST (test_pull)
+{
+  GstElement *src;
+  GstQuery *seeking_query;
+  gboolean res, seekable;
+  gint64 start, stop;
+  GstPad *pad;
+  GstFlowReturn ret;
+  GstBuffer *buffer1, *buffer2;
+
+  src = setup_filesrc ();
+
+  g_object_set (G_OBJECT (src), "location", TESTFILE, NULL);
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS,
+      "could not set to ready");
+
+  /* get the source pad */
+  pad = gst_element_get_pad (src, "src");
+  fail_unless (pad != NULL);
+
+  /* activate the pad in pull mode */
+  res = gst_pad_activate_pull (pad, TRUE);
+  fail_unless (res == TRUE);
+
+  /* not start playing */
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+      "could not set to paused");
+
+  /* Test that filesrc is seekable with a file fd */
+  fail_unless ((seeking_query = gst_query_new_seeking (GST_FORMAT_BYTES))
+      != NULL);
+  fail_unless (gst_element_query (src, seeking_query) == TRUE);
+
+  /* get the seeking capabilities */
+  gst_query_parse_seeking (seeking_query, NULL, &seekable, &start, &stop);
+  fail_unless (seekable == TRUE);
+  fail_unless (start == 0);
+  fail_unless (start != -1);
+  gst_query_unref (seeking_query);
+
+  /* do some pulls */
+  ret = gst_pad_get_range (pad, 0, 100, &buffer1);
+  fail_unless (ret == GST_FLOW_OK);
+  fail_unless (buffer1 != NULL);
+  fail_unless (GST_BUFFER_SIZE (buffer1) == 100);
+
+  ret = gst_pad_get_range (pad, 0, 50, &buffer2);
+  fail_unless (ret == GST_FLOW_OK);
+  fail_unless (buffer2 != NULL);
+  fail_unless (GST_BUFFER_SIZE (buffer2) == 50);
+
+  /* this should be the same */
+  fail_unless (memcmp (GST_BUFFER_DATA (buffer1), GST_BUFFER_DATA (buffer2),
+          50) == 0);
+
+  gst_buffer_unref (buffer2);
+
+  /* read next 50 bytes */
+  ret = gst_pad_get_range (pad, 50, 50, &buffer2);
+  fail_unless (ret == GST_FLOW_OK);
+  fail_unless (buffer2 != NULL);
+  fail_unless (GST_BUFFER_SIZE (buffer2) == 50);
+
+  /* compare with previously read data */
+  fail_unless (memcmp (GST_BUFFER_DATA (buffer1) + 50,
+          GST_BUFFER_DATA (buffer2), 50) == 0);
+
+  gst_buffer_unref (buffer1);
+  gst_buffer_unref (buffer2);
+
+  /* read 10 bytes at end-10 should give exactly 10 bytes */
+  ret = gst_pad_get_range (pad, stop - 10, 10, &buffer1);
+  fail_unless (ret == GST_FLOW_OK);
+  fail_unless (buffer1 != NULL);
+  fail_unless (GST_BUFFER_SIZE (buffer1) == 10);
+  gst_buffer_unref (buffer1);
+
+  /* read 20 bytes at end-10 should give exactly 10 bytes */
+  ret = gst_pad_get_range (pad, stop - 10, 20, &buffer1);
+  fail_unless (ret == GST_FLOW_OK);
+  fail_unless (buffer1 != NULL);
+  fail_unless (GST_BUFFER_SIZE (buffer1) == 10);
+  gst_buffer_unref (buffer1);
+
+  /* read 0 bytes at end-1 should return 0 bytes */
+  ret = gst_pad_get_range (pad, stop - 1, 0, &buffer1);
+  fail_unless (ret == GST_FLOW_OK);
+  fail_unless (buffer1 != NULL);
+  fail_unless (GST_BUFFER_SIZE (buffer1) == 0);
+  gst_buffer_unref (buffer1);
+
+  /* read 10 bytes at end-1 should return 1 byte */
+  ret = gst_pad_get_range (pad, stop - 1, 10, &buffer1);
+  fail_unless (ret == GST_FLOW_OK);
+  fail_unless (buffer1 != NULL);
+  fail_unless (GST_BUFFER_SIZE (buffer1) == 1);
+  gst_buffer_unref (buffer1);
+
+  /* read 0 bytes at end should EOS */
+  ret = gst_pad_get_range (pad, stop, 0, &buffer1);
+  fail_unless (ret == GST_FLOW_UNEXPECTED);
+
+  /* read 10 bytes before end should EOS */
+  ret = gst_pad_get_range (pad, stop, 10, &buffer1);
+  fail_unless (ret == GST_FLOW_UNEXPECTED);
+
+  /* read 0 bytes after end should EOS */
+  ret = gst_pad_get_range (pad, stop + 10, 0, &buffer1);
+  fail_unless (ret == GST_FLOW_UNEXPECTED);
+
+  /* read 10 bytes after end should EOS too */
+  ret = gst_pad_get_range (pad, stop + 10, 10, &buffer1);
+  fail_unless (ret == GST_FLOW_UNEXPECTED);
+
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+
+  /* cleanup */
+  gst_object_unref (pad);
+  cleanup_filesrc (src);
+}
+
+GST_END_TEST;
+
 GST_START_TEST (test_coverage)
 {
   GstElement *src;
@@ -147,6 +273,7 @@ filesrc_suite (void)
 
   suite_add_tcase (s, tc_chain);
   tcase_add_test (tc_chain, test_seeking);
+  tcase_add_test (tc_chain, test_pull);
   tcase_add_test (tc_chain, test_coverage);
 
   return s;