mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-30 11:08:34 +00:00
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
This commit is contained in:
parent
7b347681d5
commit
84279849a8
11 changed files with 699 additions and 36 deletions
21
ChangeLog
21
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):
|
||||
|
|
|
@ -51,6 +51,7 @@ check_PROGRAMS = \
|
|||
gst/gstvalue \
|
||||
elements/fakesrc \
|
||||
elements/identity \
|
||||
elements/fdsrc \
|
||||
generic/states \
|
||||
pipelines/simple_launch_lines \
|
||||
pipelines/stress \
|
||||
|
|
208
check/elements/fdsrc.c
Normal file
208
check/elements/fdsrc.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
||||
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 {
|
||||
|
|
|
@ -51,6 +51,7 @@ check_PROGRAMS = \
|
|||
gst/gstvalue \
|
||||
elements/fakesrc \
|
||||
elements/identity \
|
||||
elements/fdsrc \
|
||||
generic/states \
|
||||
pipelines/simple_launch_lines \
|
||||
pipelines/stress \
|
||||
|
|
208
tests/check/elements/fdsrc.c
Normal file
208
tests/check/elements/fdsrc.c
Normal file
|
@ -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;
|
||||
}
|
Loading…
Reference in a new issue