diff --git a/ChangeLog b/ChangeLog index fcf7d02642..50c03353ce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2005-11-27 Jan Schmidt + + * 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 * gst/gstelement.c: (gst_element_dispose): diff --git a/check/Makefile.am b/check/Makefile.am index ada19183b4..ec0930a9f5 100644 --- a/check/Makefile.am +++ b/check/Makefile.am @@ -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 index 0000000000..65e1b9f3f8 --- /dev/null +++ b/check/elements/fdsrc.c @@ -0,0 +1,208 @@ +/* GStreamer + * + * unit test for fdsrc + * + * Copyright (C) <2005> Jan Schmidt + * + * 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 +#include +#include +#include + +#include + +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; +} diff --git a/gst/elements/gstfdsrc.c b/gst/elements/gstfdsrc.c index 84a1688f51..135c98db18 100644 --- a/gst/elements/gstfdsrc.c +++ b/gst/elements/gstfdsrc.c @@ -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; } diff --git a/gst/elements/gstfdsrc.h b/gst/elements/gstfdsrc.h index 32e0938176..5fbfda224f 100644 --- a/gst/elements/gstfdsrc.h +++ b/gst/elements/gstfdsrc.h @@ -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 { diff --git a/gst/gstquery.c b/gst/gstquery.c index 6cf6d96511..83452f17a0 100644 --- a/gst/gstquery.c +++ b/gst/gstquery.c @@ -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 diff --git a/gst/gstquery.h b/gst/gstquery.h index 7032e7416a..329c450337 100644 --- a/gst/gstquery.h +++ b/gst/gstquery.h @@ -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 diff --git a/plugins/elements/gstfdsrc.c b/plugins/elements/gstfdsrc.c index 84a1688f51..135c98db18 100644 --- a/plugins/elements/gstfdsrc.c +++ b/plugins/elements/gstfdsrc.c @@ -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; } diff --git a/plugins/elements/gstfdsrc.h b/plugins/elements/gstfdsrc.h index 32e0938176..5fbfda224f 100644 --- a/plugins/elements/gstfdsrc.h +++ b/plugins/elements/gstfdsrc.h @@ -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 { diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index ada19183b4..ec0930a9f5 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -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 index 0000000000..65e1b9f3f8 --- /dev/null +++ b/tests/check/elements/fdsrc.c @@ -0,0 +1,208 @@ +/* GStreamer + * + * unit test for fdsrc + * + * Copyright (C) <2005> Jan Schmidt + * + * 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 +#include +#include +#include + +#include + +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; +}