Commit 84279849 authored by Jan Schmidt's avatar Jan Schmidt

check/: Add tests for fdsrc seekability

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
parent 7b347681
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):
......
......@@ -51,6 +51,7 @@ check_PROGRAMS = \
gst/gstvalue \
elements/fakesrc \
elements/identity \
elements/fdsrc \
generic/states \
pipelines/simple_launch_lines \
pipelines/stress \
......
/* 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;
}
......@@ -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;
}
......
......@@ -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 {
......
......@@ -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
......
......@@ -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
......
......@@ -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);