+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):
gst/gstvalue \
elements/fakesrc \
elements/identity \
+ elements/fdsrc \
generic/states \
pipelines/simple_launch_lines \
pipelines/stress \
--- /dev/null
+/* 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;
+}
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);
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;
}
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)
{
src->curoffset = 0;
+ gst_fdsrc_update_fd (src);
+
if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
goto socket_pair;
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);
}
}
+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
{
gchar *protocol;
GstFdSrc *src = GST_FDSRC (handler);
- gint fd = src->fd;
+ gint fd;
protocol = gst_uri_get_protocol (uri);
if (strcmp (protocol, "fd") != 0) {
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;
}
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 {
}
/**
+ * 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
* @format: the format to set for the @segment_start and @segment_end values
}
/**
+ * 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
* @n_formats: the number of formats to set.
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
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);
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;
}
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)
{
src->curoffset = 0;
+ gst_fdsrc_update_fd (src);
+
if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
goto socket_pair;
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);
}
}
+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
{
gchar *protocol;
GstFdSrc *src = GST_FDSRC (handler);
- gint fd = src->fd;
+ gint fd;
protocol = gst_uri_get_protocol (uri);
if (strcmp (protocol, "fd") != 0) {
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;
}
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 {
gst/gstvalue \
elements/fakesrc \
elements/identity \
+ elements/fdsrc \
generic/states \
pipelines/simple_launch_lines \
pipelines/stress \
--- /dev/null
+/* 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;
+}