]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gstreamer0-10.git/commitdiff
check/: Add tests for fdsrc seekability
authorJan Schmidt <thaytan@mad.scientist.com>
Sun, 27 Nov 2005 22:50:09 +0000 (22:50 +0000)
committerJan Schmidt <thaytan@mad.scientist.com>
Sun, 27 Nov 2005 22:50:09 +0000 (22:50 +0000)
Original commit message from CVS:
* check/Makefile.am:
* check/elements/fdsrc.c: (event_func), (setup_fdsrc),
(cleanup_fdsrc), (GST_START_TEST), (fdsrc_suite), (main):
Add tests for fdsrc seekability

* gst/elements/gstfdsrc.c: (gst_fdsrc_class_init),
(gst_fdsrc_init), (gst_fdsrc_update_fd), (gst_fdsrc_start),
(gst_fdsrc_set_property), (gst_fdsrc_is_seekable),
(gst_fdsrc_get_size), (gst_fdsrc_uri_set_uri):
* gst/elements/gstfdsrc.h:
fdsrc should not be a 'live' source.
Implement seeking on seekable fd's.

* gst/gstquery.c: (gst_query_new_seeking),
(gst_query_parse_seeking):
* gst/gstquery.h:
Implement SEEKING query functions:
*_new_seeking and *_parse_seeking

ChangeLog
check/Makefile.am
check/elements/fdsrc.c [new file with mode: 0644]
gst/elements/gstfdsrc.c
gst/elements/gstfdsrc.h
gst/gstquery.c
gst/gstquery.h
plugins/elements/gstfdsrc.c
plugins/elements/gstfdsrc.h
tests/check/Makefile.am
tests/check/elements/fdsrc.c [new file with mode: 0644]

index fcf7d02642aa99ce5065bf5c6826faa3e682a0ca..50c03353ce0b6ce0288d335c0f0d50812a46c2b5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2005-11-27  Jan Schmidt  <thaytan@mad.scientist.com>
+
+       * check/Makefile.am:
+       * check/elements/fdsrc.c: (event_func), (setup_fdsrc),
+       (cleanup_fdsrc), (GST_START_TEST), (fdsrc_suite), (main):
+         Add tests for fdsrc seekability
+
+       * gst/elements/gstfdsrc.c: (gst_fdsrc_class_init),
+       (gst_fdsrc_init), (gst_fdsrc_update_fd), (gst_fdsrc_start),
+       (gst_fdsrc_set_property), (gst_fdsrc_is_seekable),
+       (gst_fdsrc_get_size), (gst_fdsrc_uri_set_uri):
+       * gst/elements/gstfdsrc.h:
+         fdsrc should not be a 'live' source.
+         Implement seeking on seekable fd's.
+
+       * gst/gstquery.c: (gst_query_new_seeking),
+       (gst_query_parse_seeking):
+       * gst/gstquery.h:
+         Implement SEEKING query functions: 
+           *_new_seeking and *_parse_seeking
+
 2005-11-27  Stefan Kost  <ensonic@users.sf.net>
 
        * gst/gstelement.c: (gst_element_dispose):
index ada19183b4b840edff571710ac00370d549b93c8..ec0930a9f51c6b1b44d5457e91cb23b5eea80011 100644 (file)
@@ -51,6 +51,7 @@ check_PROGRAMS =                              \
        gst/gstvalue                            \
        elements/fakesrc                        \
        elements/identity                       \
+       elements/fdsrc                          \
        generic/states                          \
        pipelines/simple_launch_lines           \
        pipelines/stress                        \
diff --git a/check/elements/fdsrc.c b/check/elements/fdsrc.c
new file mode 100644 (file)
index 0000000..65e1b9f
--- /dev/null
@@ -0,0 +1,208 @@
+/* GStreamer
+ *
+ * unit test for fdsrc
+ *
+ * Copyright (C) <2005> Jan Schmidt <thaytan at mad dot scientist dot com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <gst/check/gstcheck.h>
+
+gboolean have_eos = FALSE;
+
+GstPad *mysinkpad;
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+gboolean
+event_func (GstPad * pad, GstEvent * event)
+{
+  if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
+    have_eos = TRUE;
+    gst_event_unref (event);
+    return TRUE;
+  }
+
+  gst_event_unref (event);
+  return FALSE;
+}
+
+GstElement *
+setup_fdsrc ()
+{
+  GstElement *fdsrc;
+
+  GST_DEBUG ("setup_fdsrc");
+  fdsrc = gst_check_setup_element ("fdsrc");
+  mysinkpad = gst_check_setup_sink_pad (fdsrc, &sinktemplate, NULL);
+  gst_pad_set_event_function (mysinkpad, event_func);
+  gst_pad_set_active (mysinkpad, TRUE);
+  return fdsrc;
+}
+
+void
+cleanup_fdsrc (GstElement * fdsrc)
+{
+  gst_check_teardown_sink_pad (fdsrc);
+  gst_check_teardown_element (fdsrc);
+}
+
+GST_START_TEST (test_num_buffers)
+{
+  GstElement *src;
+  gint pipe_fd[2];
+  gchar data[4096];
+
+  fail_if (pipe (pipe_fd) < 0);
+
+  src = setup_fdsrc ();
+  g_object_set (G_OBJECT (src), "num-buffers", 3, NULL);
+  g_object_set (G_OBJECT (src), "fd", pipe_fd[0], NULL);
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+      "could not set to playing");
+
+  memset (data, 0, 4096);
+  while (!have_eos) {
+    fail_if (write (pipe_fd[1], data, 4096) < 0);
+    g_usleep (100);
+  }
+
+  fail_unless (g_list_length (buffers) == 3);
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+
+  /* cleanup */
+  cleanup_fdsrc (src);
+  close (pipe_fd[0]);
+  close (pipe_fd[1]);
+  g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+  g_list_free (buffers);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_nonseeking)
+{
+  GstElement *src;
+  GstQuery *seeking_query;
+  gint pipe_fd[2];
+  gchar data[4096];
+  gboolean seekable;
+
+  fail_if (pipe (pipe_fd) < 0);
+
+  src = setup_fdsrc ();
+  g_object_set (G_OBJECT (src), "num-buffers", 3, NULL);
+  g_object_set (G_OBJECT (src), "fd", pipe_fd[0], NULL);
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_PAUSED) == GST_STATE_CHANGE_SUCCESS,
+      "could not set to paused");
+
+  memset (data, 0, 4096);
+  fail_if (write (pipe_fd[1], data, 4096) < 0);
+
+  /* Test that fdsrc is non-seekable with a pipe */
+  fail_unless ((seeking_query = gst_query_new_seeking (GST_FORMAT_BYTES))
+      != NULL);
+  fail_unless (gst_element_query (src, seeking_query) == TRUE);
+  gst_query_parse_seeking (seeking_query, NULL, &seekable, NULL, NULL);
+  fail_unless (seekable == FALSE);
+  gst_query_unref (seeking_query);
+
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+
+  /* cleanup */
+  cleanup_fdsrc (src);
+  close (pipe_fd[0]);
+  close (pipe_fd[1]);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_seeking)
+{
+  GstElement *src;
+  gint in_fd;
+  GstQuery *seeking_query;
+  gboolean seekable;
+
+  fail_if ((in_fd = open ("elements/fdsrc.c", O_RDONLY)) < 0);
+  src = setup_fdsrc ();
+
+  g_object_set (G_OBJECT (src), "fd", in_fd, NULL);
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_PAUSED) == GST_STATE_CHANGE_SUCCESS,
+      "could not set to paused");
+
+  /* Test that fdsrc 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);
+  gst_query_parse_seeking (seeking_query, NULL, &seekable, NULL, NULL);
+  fail_unless (seekable == TRUE);
+  gst_query_unref (seeking_query);
+
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+
+  /* cleanup */
+  cleanup_fdsrc (src);
+  close (in_fd);
+}
+
+GST_END_TEST;
+
+Suite *
+fdsrc_suite (void)
+{
+  Suite *s = suite_create ("fdsrc");
+  TCase *tc_chain = tcase_create ("general");
+
+  suite_add_tcase (s, tc_chain);
+  tcase_add_test (tc_chain, test_num_buffers);
+  tcase_add_test (tc_chain, test_nonseeking);
+  tcase_add_test (tc_chain, test_seeking);
+
+  return s;
+}
+
+int
+main (int argc, char **argv)
+{
+  int nf;
+
+  Suite *s = fdsrc_suite ();
+  SRunner *sr = srunner_create (s);
+
+  gst_check_init (&argc, &argv);
+
+  srunner_run_all (sr, CK_NORMAL);
+  nf = srunner_ntests_failed (sr);
+  srunner_free (sr);
+
+  return nf;
+}
index 84a1688f51100906935809bba19496c3c849888a..135c98db188533087fd418afca946294b0ca807b 100644 (file)
@@ -110,6 +110,8 @@ static void gst_fdsrc_dispose (GObject * obj);
 static gboolean gst_fdsrc_start (GstBaseSrc * bsrc);
 static gboolean gst_fdsrc_stop (GstBaseSrc * bsrc);
 static gboolean gst_fdsrc_unlock (GstBaseSrc * bsrc);
+static gboolean gst_fdsrc_is_seekable (GstBaseSrc * bsrc);
+static gboolean gst_fdsrc_get_size (GstBaseSrc * src, guint64 * size);
 
 static GstFlowReturn gst_fdsrc_create (GstPushSrc * psrc, GstBuffer ** outbuf);
 
@@ -146,21 +148,21 @@ gst_fdsrc_class_init (GstFdSrcClass * klass)
       g_param_spec_int ("fd", "fd", "An open file descriptor to read from",
           0, G_MAXINT, 0, G_PARAM_READWRITE));
 
-  gstbasesrc_class->start = gst_fdsrc_start;
-  gstbasesrc_class->stop = gst_fdsrc_stop;
-  gstbasesrc_class->unlock = gst_fdsrc_unlock;
+  gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_fdsrc_start);
+  gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_fdsrc_stop);
+  gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_fdsrc_unlock);
+  gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_fdsrc_is_seekable);
+  gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_fdsrc_get_size);
 
-  gstpush_src_class->create = gst_fdsrc_create;
+  gstpush_src_class->create = GST_DEBUG_FUNCPTR (gst_fdsrc_create);
 }
 
 static void
 gst_fdsrc_init (GstFdSrc * fdsrc, GstFdSrcClass * klass)
 {
-  /* TODO set live only if it's actually a live source (check
-   * for seekable fd) */
-  gst_base_src_set_live (GST_BASE_SRC (fdsrc), TRUE);
-
   fdsrc->fd = 0;
+  fdsrc->new_fd = 0;
+  fdsrc->seekable_fd = FALSE;
   fdsrc->uri = g_strdup_printf ("fd://%d", fdsrc->fd);
   fdsrc->curoffset = 0;
 }
@@ -176,6 +178,32 @@ gst_fdsrc_dispose (GObject * obj)
   G_OBJECT_CLASS (parent_class)->dispose (obj);
 }
 
+static void
+gst_fdsrc_update_fd (GstFdSrc * src)
+{
+  struct stat stat_results;
+
+  src->fd = src->new_fd;
+  g_free (src->uri);
+  src->uri = g_strdup_printf ("fd://%d", src->fd);
+
+  if (fstat (src->fd, &stat_results) < 0)
+    goto not_seekable;
+
+  if (!S_ISREG (stat_results.st_mode))
+    goto not_seekable;
+
+  /* Try a seek of 0 bytes offset to check for seekability */
+  if (lseek (src->fd, SEEK_CUR, 0) < 0)
+    goto not_seekable;
+
+  src->seekable_fd = TRUE;
+  return;
+
+not_seekable:
+  src->seekable_fd = FALSE;
+}
+
 static gboolean
 gst_fdsrc_start (GstBaseSrc * bsrc)
 {
@@ -184,6 +212,8 @@ gst_fdsrc_start (GstBaseSrc * bsrc)
 
   src->curoffset = 0;
 
+  gst_fdsrc_update_fd (src);
+
   if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
     goto socket_pair;
 
@@ -233,9 +263,15 @@ gst_fdsrc_set_property (GObject * object, guint prop_id, const GValue * value,
 
   switch (prop_id) {
     case PROP_FD:
-      src->fd = g_value_get_int (value);
-      g_free (src->uri);
-      src->uri = g_strdup_printf ("fd://%d", src->fd);
+      src->new_fd = g_value_get_int (value);
+
+      /* If state is ready or below, update the current fd immediately
+       * so it is reflected in get_properties and uri */
+      GST_OBJECT_LOCK (object);
+      if (GST_STATE (GST_ELEMENT (src)) <= GST_STATE_READY) {
+        gst_fdsrc_update_fd (src);
+      }
+      GST_OBJECT_UNLOCK (object);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -361,6 +397,41 @@ read_error:
   }
 }
 
+gboolean
+gst_fdsrc_is_seekable (GstBaseSrc * bsrc)
+{
+  GstFdSrc *src = GST_FDSRC (bsrc);
+
+  return src->seekable_fd;
+}
+
+gboolean
+gst_fdsrc_get_size (GstBaseSrc * bsrc, guint64 * size)
+{
+  GstFdSrc *src = GST_FDSRC (bsrc);
+  struct stat stat_results;
+
+  if (!src->seekable_fd) {
+    /* If it isn't seekable, we won't know the length (but fstat will still
+     * succeed, and wrongly say our length is zero. */
+    return FALSE;
+  }
+
+  if (fstat (src->fd, &stat_results) < 0)
+    goto could_not_stat;
+
+  *size = stat_results.st_size;
+
+  return TRUE;
+
+  /* ERROR */
+could_not_stat:
+  {
+    return FALSE;
+  }
+
+}
+
 /*** GSTURIHANDLER INTERFACE *************************************************/
 
 static guint
@@ -388,7 +459,7 @@ gst_fdsrc_uri_set_uri (GstURIHandler * handler, const gchar * uri)
 {
   gchar *protocol;
   GstFdSrc *src = GST_FDSRC (handler);
-  gint fd = src->fd;
+  gint fd;
 
   protocol = gst_uri_get_protocol (uri);
   if (strcmp (protocol, "fd") != 0) {
@@ -400,9 +471,13 @@ gst_fdsrc_uri_set_uri (GstURIHandler * handler, const gchar * uri)
   if (sscanf (uri, "fd://%d", &fd) != 1)
     return FALSE;
 
-  src->fd = fd;
-  g_free (src->uri);
-  src->uri = g_strdup (uri);
+  src->new_fd = fd;
+
+  GST_OBJECT_LOCK (src);
+  if (GST_STATE (GST_ELEMENT (src)) <= GST_STATE_READY) {
+    gst_fdsrc_update_fd (src);
+  }
+  GST_OBJECT_UNLOCK (src);
 
   return TRUE;
 }
index 32e09381766411c5117986c44d492552466d8ae4..5fbfda224fc59c7aeb6bd25166439089b0b1612b 100644 (file)
@@ -49,14 +49,18 @@ typedef struct _GstFdSrcClass GstFdSrcClass;
 struct _GstFdSrc {
   GstPushSrc element;
 
-  /* fd */
+  /* new_fd is copied to fd on READY->PAUSED */
+  gint new_fd;
+
+  /* fd and flag indicating whether fd is seekable */
   gint fd;
+  gboolean seekable_fd;
+
+  gchar *uri;
 
   gint control_sock[2];
 
   gulong curoffset; /* current offset in file */
-
-  gchar *uri;
 };
 
 struct _GstFdSrcClass {
index 6cf6d96511997053755f3385ee34866f897f94d0..83452f17a0cd52961c881a455a1100788fad9538 100644 (file)
@@ -713,6 +713,31 @@ gst_query_get_structure (GstQuery * query)
   return query->structure;
 }
 
+/**
+ * gst_query_new_seeking (GstFormat *format)
+ * @format: the default #GstFormat for the new query
+ *
+ * Constructs a new query object for querying seeking properties of
+ * the stream. 
+ *
+ * Returns: A #GstQuery
+ */
+GstQuery *
+gst_query_new_seeking (GstFormat format)
+{
+  GstQuery *query;
+  GstStructure *structure;
+
+  structure = gst_structure_new ("GstQuerySeeking",
+      "format", GST_TYPE_FORMAT, format,
+      "seekable", G_TYPE_BOOLEAN, FALSE,
+      "segment-start", G_TYPE_INT64, (gint64) - 1,
+      "segment-end", G_TYPE_INT64, (gint64) - 1, NULL);
+  query = gst_query_new (GST_QUERY_SEEKING, structure);
+
+  return query;
+}
+
 /**
  * gst_query_set_seeking:
  * @query: a #GstQuery
@@ -739,6 +764,41 @@ gst_query_set_seeking (GstQuery * query, GstFormat format,
       "segment-end", G_TYPE_INT64, segment_end, NULL);
 }
 
+/**
+ * gst_query_parse_seeking:
+ * @query: a GST_QUERY_SEEKING type query #GstQuery
+ * @format: the format to set for the @segment_start and @segment_end values
+ * @seekable: the seekable flag to set
+ * @segment_start: the segment_start to set
+ * @segment_end: the segment_end to set
+ *
+ * Parse a seeking query, writing the format into @format, and 
+ * other results into the passed parameters, if the respective parameters
+ * are non-NULL
+ */
+void
+gst_query_parse_seeking (GstQuery * query, GstFormat * format,
+    gboolean * seekable, gint64 * segment_start, gint64 * segment_end)
+{
+  GstStructure *structure;
+
+  g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_SEEKING);
+
+  structure = gst_query_get_structure (query);
+  if (format)
+    *format = g_value_get_enum (gst_structure_get_value (structure, "format"));
+  if (seekable)
+    *seekable =
+        g_value_get_boolean (gst_structure_get_value (structure, "seekable"));
+  if (segment_start)
+    *segment_start =
+        g_value_get_int64 (gst_structure_get_value (structure,
+            "segment-start"));
+  if (segment_end)
+    *segment_end =
+        g_value_get_int64 (gst_structure_get_value (structure, "segment-end"));
+}
+
 /**
  * gst_query_set_formats:
  * @query: a #GstQuery
index 7032e7416ab96a03c63ad0c52ff6897a9b0fc2fb..329c450337340ac9a34741858ffc7d4bee05f2d9 100644 (file)
@@ -217,10 +217,16 @@ GstQuery *        gst_query_new_application       (GstQueryType type,
 GstStructure *  gst_query_get_structure                (GstQuery *query);
 
 /* moved from old gstqueryutils.h */
+GstQuery*       gst_query_new_seeking           (GstFormat format);
 void            gst_query_set_seeking           (GstQuery *query, GstFormat format,
                                                  gboolean seekable,
                                                  gint64 segment_start,
                                                  gint64 segment_end);
+void            gst_query_parse_seeking         (GstQuery *query, GstFormat *format,
+                                                 gboolean *seekable,
+                                                 gint64 *segment_start,
+                                                 gint64 *segment_end);
+
 void            gst_query_set_formats           (GstQuery *query, gint n_formats, ...);
 
 G_END_DECLS
index 84a1688f51100906935809bba19496c3c849888a..135c98db188533087fd418afca946294b0ca807b 100644 (file)
@@ -110,6 +110,8 @@ static void gst_fdsrc_dispose (GObject * obj);
 static gboolean gst_fdsrc_start (GstBaseSrc * bsrc);
 static gboolean gst_fdsrc_stop (GstBaseSrc * bsrc);
 static gboolean gst_fdsrc_unlock (GstBaseSrc * bsrc);
+static gboolean gst_fdsrc_is_seekable (GstBaseSrc * bsrc);
+static gboolean gst_fdsrc_get_size (GstBaseSrc * src, guint64 * size);
 
 static GstFlowReturn gst_fdsrc_create (GstPushSrc * psrc, GstBuffer ** outbuf);
 
@@ -146,21 +148,21 @@ gst_fdsrc_class_init (GstFdSrcClass * klass)
       g_param_spec_int ("fd", "fd", "An open file descriptor to read from",
           0, G_MAXINT, 0, G_PARAM_READWRITE));
 
-  gstbasesrc_class->start = gst_fdsrc_start;
-  gstbasesrc_class->stop = gst_fdsrc_stop;
-  gstbasesrc_class->unlock = gst_fdsrc_unlock;
+  gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_fdsrc_start);
+  gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_fdsrc_stop);
+  gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_fdsrc_unlock);
+  gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_fdsrc_is_seekable);
+  gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_fdsrc_get_size);
 
-  gstpush_src_class->create = gst_fdsrc_create;
+  gstpush_src_class->create = GST_DEBUG_FUNCPTR (gst_fdsrc_create);
 }
 
 static void
 gst_fdsrc_init (GstFdSrc * fdsrc, GstFdSrcClass * klass)
 {
-  /* TODO set live only if it's actually a live source (check
-   * for seekable fd) */
-  gst_base_src_set_live (GST_BASE_SRC (fdsrc), TRUE);
-
   fdsrc->fd = 0;
+  fdsrc->new_fd = 0;
+  fdsrc->seekable_fd = FALSE;
   fdsrc->uri = g_strdup_printf ("fd://%d", fdsrc->fd);
   fdsrc->curoffset = 0;
 }
@@ -176,6 +178,32 @@ gst_fdsrc_dispose (GObject * obj)
   G_OBJECT_CLASS (parent_class)->dispose (obj);
 }
 
+static void
+gst_fdsrc_update_fd (GstFdSrc * src)
+{
+  struct stat stat_results;
+
+  src->fd = src->new_fd;
+  g_free (src->uri);
+  src->uri = g_strdup_printf ("fd://%d", src->fd);
+
+  if (fstat (src->fd, &stat_results) < 0)
+    goto not_seekable;
+
+  if (!S_ISREG (stat_results.st_mode))
+    goto not_seekable;
+
+  /* Try a seek of 0 bytes offset to check for seekability */
+  if (lseek (src->fd, SEEK_CUR, 0) < 0)
+    goto not_seekable;
+
+  src->seekable_fd = TRUE;
+  return;
+
+not_seekable:
+  src->seekable_fd = FALSE;
+}
+
 static gboolean
 gst_fdsrc_start (GstBaseSrc * bsrc)
 {
@@ -184,6 +212,8 @@ gst_fdsrc_start (GstBaseSrc * bsrc)
 
   src->curoffset = 0;
 
+  gst_fdsrc_update_fd (src);
+
   if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
     goto socket_pair;
 
@@ -233,9 +263,15 @@ gst_fdsrc_set_property (GObject * object, guint prop_id, const GValue * value,
 
   switch (prop_id) {
     case PROP_FD:
-      src->fd = g_value_get_int (value);
-      g_free (src->uri);
-      src->uri = g_strdup_printf ("fd://%d", src->fd);
+      src->new_fd = g_value_get_int (value);
+
+      /* If state is ready or below, update the current fd immediately
+       * so it is reflected in get_properties and uri */
+      GST_OBJECT_LOCK (object);
+      if (GST_STATE (GST_ELEMENT (src)) <= GST_STATE_READY) {
+        gst_fdsrc_update_fd (src);
+      }
+      GST_OBJECT_UNLOCK (object);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -361,6 +397,41 @@ read_error:
   }
 }
 
+gboolean
+gst_fdsrc_is_seekable (GstBaseSrc * bsrc)
+{
+  GstFdSrc *src = GST_FDSRC (bsrc);
+
+  return src->seekable_fd;
+}
+
+gboolean
+gst_fdsrc_get_size (GstBaseSrc * bsrc, guint64 * size)
+{
+  GstFdSrc *src = GST_FDSRC (bsrc);
+  struct stat stat_results;
+
+  if (!src->seekable_fd) {
+    /* If it isn't seekable, we won't know the length (but fstat will still
+     * succeed, and wrongly say our length is zero. */
+    return FALSE;
+  }
+
+  if (fstat (src->fd, &stat_results) < 0)
+    goto could_not_stat;
+
+  *size = stat_results.st_size;
+
+  return TRUE;
+
+  /* ERROR */
+could_not_stat:
+  {
+    return FALSE;
+  }
+
+}
+
 /*** GSTURIHANDLER INTERFACE *************************************************/
 
 static guint
@@ -388,7 +459,7 @@ gst_fdsrc_uri_set_uri (GstURIHandler * handler, const gchar * uri)
 {
   gchar *protocol;
   GstFdSrc *src = GST_FDSRC (handler);
-  gint fd = src->fd;
+  gint fd;
 
   protocol = gst_uri_get_protocol (uri);
   if (strcmp (protocol, "fd") != 0) {
@@ -400,9 +471,13 @@ gst_fdsrc_uri_set_uri (GstURIHandler * handler, const gchar * uri)
   if (sscanf (uri, "fd://%d", &fd) != 1)
     return FALSE;
 
-  src->fd = fd;
-  g_free (src->uri);
-  src->uri = g_strdup (uri);
+  src->new_fd = fd;
+
+  GST_OBJECT_LOCK (src);
+  if (GST_STATE (GST_ELEMENT (src)) <= GST_STATE_READY) {
+    gst_fdsrc_update_fd (src);
+  }
+  GST_OBJECT_UNLOCK (src);
 
   return TRUE;
 }
index 32e09381766411c5117986c44d492552466d8ae4..5fbfda224fc59c7aeb6bd25166439089b0b1612b 100644 (file)
@@ -49,14 +49,18 @@ typedef struct _GstFdSrcClass GstFdSrcClass;
 struct _GstFdSrc {
   GstPushSrc element;
 
-  /* fd */
+  /* new_fd is copied to fd on READY->PAUSED */
+  gint new_fd;
+
+  /* fd and flag indicating whether fd is seekable */
   gint fd;
+  gboolean seekable_fd;
+
+  gchar *uri;
 
   gint control_sock[2];
 
   gulong curoffset; /* current offset in file */
-
-  gchar *uri;
 };
 
 struct _GstFdSrcClass {
index ada19183b4b840edff571710ac00370d549b93c8..ec0930a9f51c6b1b44d5457e91cb23b5eea80011 100644 (file)
@@ -51,6 +51,7 @@ check_PROGRAMS =                              \
        gst/gstvalue                            \
        elements/fakesrc                        \
        elements/identity                       \
+       elements/fdsrc                          \
        generic/states                          \
        pipelines/simple_launch_lines           \
        pipelines/stress                        \
diff --git a/tests/check/elements/fdsrc.c b/tests/check/elements/fdsrc.c
new file mode 100644 (file)
index 0000000..65e1b9f
--- /dev/null
@@ -0,0 +1,208 @@
+/* GStreamer
+ *
+ * unit test for fdsrc
+ *
+ * Copyright (C) <2005> Jan Schmidt <thaytan at mad dot scientist dot com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <gst/check/gstcheck.h>
+
+gboolean have_eos = FALSE;
+
+GstPad *mysinkpad;
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+gboolean
+event_func (GstPad * pad, GstEvent * event)
+{
+  if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
+    have_eos = TRUE;
+    gst_event_unref (event);
+    return TRUE;
+  }
+
+  gst_event_unref (event);
+  return FALSE;
+}
+
+GstElement *
+setup_fdsrc ()
+{
+  GstElement *fdsrc;
+
+  GST_DEBUG ("setup_fdsrc");
+  fdsrc = gst_check_setup_element ("fdsrc");
+  mysinkpad = gst_check_setup_sink_pad (fdsrc, &sinktemplate, NULL);
+  gst_pad_set_event_function (mysinkpad, event_func);
+  gst_pad_set_active (mysinkpad, TRUE);
+  return fdsrc;
+}
+
+void
+cleanup_fdsrc (GstElement * fdsrc)
+{
+  gst_check_teardown_sink_pad (fdsrc);
+  gst_check_teardown_element (fdsrc);
+}
+
+GST_START_TEST (test_num_buffers)
+{
+  GstElement *src;
+  gint pipe_fd[2];
+  gchar data[4096];
+
+  fail_if (pipe (pipe_fd) < 0);
+
+  src = setup_fdsrc ();
+  g_object_set (G_OBJECT (src), "num-buffers", 3, NULL);
+  g_object_set (G_OBJECT (src), "fd", pipe_fd[0], NULL);
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+      "could not set to playing");
+
+  memset (data, 0, 4096);
+  while (!have_eos) {
+    fail_if (write (pipe_fd[1], data, 4096) < 0);
+    g_usleep (100);
+  }
+
+  fail_unless (g_list_length (buffers) == 3);
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+
+  /* cleanup */
+  cleanup_fdsrc (src);
+  close (pipe_fd[0]);
+  close (pipe_fd[1]);
+  g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+  g_list_free (buffers);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_nonseeking)
+{
+  GstElement *src;
+  GstQuery *seeking_query;
+  gint pipe_fd[2];
+  gchar data[4096];
+  gboolean seekable;
+
+  fail_if (pipe (pipe_fd) < 0);
+
+  src = setup_fdsrc ();
+  g_object_set (G_OBJECT (src), "num-buffers", 3, NULL);
+  g_object_set (G_OBJECT (src), "fd", pipe_fd[0], NULL);
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_PAUSED) == GST_STATE_CHANGE_SUCCESS,
+      "could not set to paused");
+
+  memset (data, 0, 4096);
+  fail_if (write (pipe_fd[1], data, 4096) < 0);
+
+  /* Test that fdsrc is non-seekable with a pipe */
+  fail_unless ((seeking_query = gst_query_new_seeking (GST_FORMAT_BYTES))
+      != NULL);
+  fail_unless (gst_element_query (src, seeking_query) == TRUE);
+  gst_query_parse_seeking (seeking_query, NULL, &seekable, NULL, NULL);
+  fail_unless (seekable == FALSE);
+  gst_query_unref (seeking_query);
+
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+
+  /* cleanup */
+  cleanup_fdsrc (src);
+  close (pipe_fd[0]);
+  close (pipe_fd[1]);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_seeking)
+{
+  GstElement *src;
+  gint in_fd;
+  GstQuery *seeking_query;
+  gboolean seekable;
+
+  fail_if ((in_fd = open ("elements/fdsrc.c", O_RDONLY)) < 0);
+  src = setup_fdsrc ();
+
+  g_object_set (G_OBJECT (src), "fd", in_fd, NULL);
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_PAUSED) == GST_STATE_CHANGE_SUCCESS,
+      "could not set to paused");
+
+  /* Test that fdsrc 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);
+  gst_query_parse_seeking (seeking_query, NULL, &seekable, NULL, NULL);
+  fail_unless (seekable == TRUE);
+  gst_query_unref (seeking_query);
+
+  fail_unless (gst_element_set_state (src,
+          GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+
+  /* cleanup */
+  cleanup_fdsrc (src);
+  close (in_fd);
+}
+
+GST_END_TEST;
+
+Suite *
+fdsrc_suite (void)
+{
+  Suite *s = suite_create ("fdsrc");
+  TCase *tc_chain = tcase_create ("general");
+
+  suite_add_tcase (s, tc_chain);
+  tcase_add_test (tc_chain, test_num_buffers);
+  tcase_add_test (tc_chain, test_nonseeking);
+  tcase_add_test (tc_chain, test_seeking);
+
+  return s;
+}
+
+int
+main (int argc, char **argv)
+{
+  int nf;
+
+  Suite *s = fdsrc_suite ();
+  SRunner *sr = srunner_create (s);
+
+  gst_check_init (&argc, &argv);
+
+  srunner_run_all (sr, CK_NORMAL);
+  nf = srunner_ntests_failed (sr);
+  srunner_free (sr);
+
+  return nf;
+}