Commit aa00eb87 authored by Wim Taymans's avatar Wim Taymans
Browse files

gst/: More work on the generic source base class, implement seeking, query.

Original commit message from CVS:
* gst/base/README:
* gst/base/gstbasesrc.c: (gst_basesrc_get_type),
(gst_basesrc_init), (gst_basesrc_get_formats), (gst_basesrc_query),
(gst_basesrc_get_event_mask), (gst_basesrc_do_seek),
(gst_basesrc_event_handler), (gst_basesrc_get_range_unlocked),
(gst_basesrc_check_get_range), (gst_basesrc_loop),
(gst_basesrc_unlock), (gst_basesrc_get_size), (gst_basesrc_start),
(gst_basesrc_stop), (gst_basesrc_activate),
(gst_basesrc_change_state), (basesrc_find_peek),
(basesrc_find_suggest), (gst_basesrc_type_find):
* gst/base/gstbasesrc.h:
* gst/elements/gstfilesrc.c: (gst_filesrc_base_init),
(gst_filesrc_class_init), (gst_filesrc_init),
(gst_filesrc_finalize), (gst_filesrc_set_location),
(gst_filesrc_set_property), (gst_filesrc_get_property),
(gst_filesrc_free_parent_mmap), (gst_filesrc_map_region),
(gst_filesrc_map_small_region), (gst_filesrc_create_mmap),
(gst_filesrc_create_read), (gst_filesrc_create),
(gst_filesrc_get_size), (gst_filesrc_start), (gst_filesrc_stop):
* gst/elements/gstfilesrc.h:
* gst/gstelement.c: (gst_element_get_state_func),
(gst_element_lost_state), (gst_element_pads_activate):
* gst/gstpad.c: (gst_pad_set_active), (gst_pad_peer_set_active),
(gst_pad_set_checkgetrange_function), (gst_pad_check_pull_range),
(gst_pad_pull_range):
* gst/gstpad.h:
More work on the generic source base class, implement seeking,
query.
Make filesrc extend the base source class.
Added gst_pad_set_checkgetrange_function to GstPad.
parent 4c712824
2005-04-06 Wim Taymans <wim@fluendo.com>
* gst/base/README:
* gst/base/gstbasesrc.c: (gst_basesrc_get_type),
(gst_basesrc_init), (gst_basesrc_get_formats), (gst_basesrc_query),
(gst_basesrc_get_event_mask), (gst_basesrc_do_seek),
(gst_basesrc_event_handler), (gst_basesrc_get_range_unlocked),
(gst_basesrc_check_get_range), (gst_basesrc_loop),
(gst_basesrc_unlock), (gst_basesrc_get_size), (gst_basesrc_start),
(gst_basesrc_stop), (gst_basesrc_activate),
(gst_basesrc_change_state), (basesrc_find_peek),
(basesrc_find_suggest), (gst_basesrc_type_find):
* gst/base/gstbasesrc.h:
* gst/elements/gstfilesrc.c: (gst_filesrc_base_init),
(gst_filesrc_class_init), (gst_filesrc_init),
(gst_filesrc_finalize), (gst_filesrc_set_location),
(gst_filesrc_set_property), (gst_filesrc_get_property),
(gst_filesrc_free_parent_mmap), (gst_filesrc_map_region),
(gst_filesrc_map_small_region), (gst_filesrc_create_mmap),
(gst_filesrc_create_read), (gst_filesrc_create),
(gst_filesrc_get_size), (gst_filesrc_start), (gst_filesrc_stop):
* gst/elements/gstfilesrc.h:
* gst/gstelement.c: (gst_element_get_state_func),
(gst_element_lost_state), (gst_element_pads_activate):
* gst/gstpad.c: (gst_pad_set_active), (gst_pad_peer_set_active),
(gst_pad_set_checkgetrange_function), (gst_pad_check_pull_range),
(gst_pad_pull_range):
* gst/gstpad.h:
More work on the generic source base class, implement seeking,
query.
Make filesrc extend the base source class.
Added gst_pad_set_checkgetrange_function to GstPad.
2005-04-06 Andy Wingo <wingo@pobox.com>
* pkgconfig/gstreamer-base.pc.in:
......
......@@ -33,3 +33,4 @@ GstBaseSrc
- one sinkpad
- handles state changes
- pull/push mode
- handles seeking/query
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
* 2005 Wim Taymans <wim@fluendo.com>
* 2000,2005 Wim Taymans <wim@fluendo.com>
*
* gstbasesrc.c:
*
......@@ -92,12 +91,20 @@ static gboolean gst_basesrc_query (GstPad * pad, GstQueryType type,
GstFormat * format, gint64 * value);
static const GstFormat *gst_basesrc_get_formats (GstPad * pad);
static gboolean gst_basesrc_unlock (GstBaseSrc * basesrc);
static gboolean gst_basesrc_get_size (GstBaseSrc * basesrc, guint64 * size);
static gboolean gst_basesrc_start (GstBaseSrc * basesrc);
static gboolean gst_basesrc_stop (GstBaseSrc * basesrc);
static GstElementStateReturn gst_basesrc_change_state (GstElement * element);
static void gst_basesrc_loop (GstPad * pad);
static gboolean gst_basesrc_check_get_range (GstPad * pad);
static GstFlowReturn gst_basesrc_get_range (GstPad * pad, guint64 offset,
guint length, GstBuffer ** buf);
static GstCaps *gst_basesrc_type_find (GstBaseSrc * src);
static void
gst_basesrc_base_init (gpointer g_class)
{
......@@ -145,12 +152,17 @@ gst_basesrc_init (GstBaseSrc * basesrc, gpointer g_class)
gst_pad_set_query_type_function (pad, gst_basesrc_get_query_types);
gst_pad_set_formats_function (pad, gst_basesrc_get_formats);
gst_pad_set_loop_function (pad, gst_basesrc_loop);
gst_pad_set_checkgetrange_function (pad, gst_basesrc_check_get_range);
gst_pad_set_getrange_function (pad, gst_basesrc_get_range);
/* hold ref to pad */
basesrc->srcpad = pad;
gst_element_add_pad (GST_ELEMENT (basesrc), pad);
basesrc->segment_start = -1;
basesrc->segment_end = -1;
basesrc->blocksize = DEFAULT_BLOCKSIZE;
GST_FLAG_UNSET (basesrc, GST_BASESRC_STARTED);
}
static const GstFormat *
......@@ -158,6 +170,7 @@ gst_basesrc_get_formats (GstPad * pad)
{
static const GstFormat formats[] = {
GST_FORMAT_DEFAULT,
GST_FORMAT_BYTES,
0,
};
......@@ -184,31 +197,156 @@ gst_basesrc_query (GstPad * pad, GstQueryType type,
{
GstBaseSrc *src = GST_BASESRC (GST_PAD_PARENT (pad));
if (*format == GST_FORMAT_DEFAULT)
*format = GST_FORMAT_BYTES;
switch (type) {
case GST_QUERY_TOTAL:
switch (*format) {
case GST_FORMAT_BYTES:
{
gboolean ret;
ret = gst_basesrc_get_size (src, value);
GST_DEBUG ("getting length %d %lld", ret, *value);
return ret;
}
case GST_FORMAT_PERCENT:
*value = GST_FORMAT_PERCENT_MAX;
return TRUE;
default:
return FALSE;
}
break;
case GST_QUERY_POSITION:
switch (*format) {
case GST_FORMAT_BYTES:
*value = src->offset;
break;
case GST_FORMAT_PERCENT:
if (!gst_basesrc_get_size (src, value))
return FALSE;
*value = src->offset * GST_FORMAT_PERCENT_MAX / *value;
return TRUE;
default:
return FALSE;
}
break;
case GST_QUERY_START:
*value = src->segment_start;
break;
return TRUE;
case GST_QUERY_SEGMENT_END:
*value = src->segment_end;
break;
return TRUE;
default:
return FALSE;
}
return TRUE;
return FALSE;
}
static const GstEventMask *
gst_basesrc_get_event_mask (GstPad * pad)
{
static const GstEventMask masks[] = {
{GST_EVENT_SEEK, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT_LOOP},
{GST_EVENT_SEEK, GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_SET |
GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH |
GST_SEEK_FLAG_SEGMENT_LOOP},
{GST_EVENT_FLUSH, 0},
{GST_EVENT_SIZE, 0},
{0, 0},
};
return masks;
}
static gboolean
gst_basesrc_do_seek (GstBaseSrc * src, GstEvent * event)
{
GstFormat format;
gint64 offset;
format = GST_EVENT_SEEK_FORMAT (event);
/* get seek format */
if (format == GST_FORMAT_DEFAULT)
format = GST_FORMAT_BYTES;
/* we can only seek bytes */
if (format != GST_FORMAT_BYTES)
return FALSE;
/* get seek positions */
offset = GST_EVENT_SEEK_OFFSET (event);
src->segment_start = offset;
src->segment_end = GST_EVENT_SEEK_ENDOFFSET (event);
src->segment_loop = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_SEGMENT_LOOP;
/* send flush start */
gst_pad_push_event (src->srcpad, gst_event_new_flush (FALSE));
/* unblock streaming thread */
gst_basesrc_unlock (src);
/* grab streaming lock */
GST_STREAM_LOCK (src->srcpad);
switch (GST_EVENT_SEEK_METHOD (event)) {
case GST_SEEK_METHOD_SET:
if (offset < 0)
goto error;
src->offset = MIN (offset, src->size);
GST_DEBUG_OBJECT (src, "seek set pending to %" G_GINT64_FORMAT,
src->offset);
break;
case GST_SEEK_METHOD_CUR:
offset += src->offset;
src->offset = CLAMP (offset, 0, src->size);
GST_DEBUG_OBJECT (src, "seek cur pending to %" G_GINT64_FORMAT,
src->offset);
break;
case GST_SEEK_METHOD_END:
if (offset > 0)
goto error;
offset = src->size + offset;
src->offset = MAX (0, offset);
GST_DEBUG_OBJECT (src, "seek end pending to %" G_GINT64_FORMAT,
src->offset);
break;
default:
goto error;
}
/* send flush end */
gst_pad_push_event (src->srcpad, gst_event_new_flush (TRUE));
/* now send discont */
{
GstEvent *event;
event = gst_event_new_discontinuous (1.0,
GST_FORMAT_TIME,
(gint64) src->segment_start, (gint64) src->segment_end, NULL);
gst_pad_push_event (src->srcpad, event);
}
/* and restart the task */
if (GST_RPAD_TASK (src->srcpad)) {
gst_task_start (GST_RPAD_TASK (src->srcpad));
}
GST_STREAM_UNLOCK (src->srcpad);
gst_event_unref (event);
return TRUE;
/* ERROR */
error:
{
GST_DEBUG_OBJECT (src, "seek error");
GST_STREAM_UNLOCK (src->srcpad);
gst_event_unref (event);
return FALSE;
}
}
static gboolean
gst_basesrc_event_handler (GstPad * pad, GstEvent * event)
{
......@@ -218,12 +356,26 @@ gst_basesrc_event_handler (GstPad * pad, GstEvent * event)
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
src->segment_start = GST_EVENT_SEEK_OFFSET (event);
src->segment_end = GST_EVENT_SEEK_ENDOFFSET (event);
src->segment_loop =
GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_SEGMENT_LOOP;
return gst_basesrc_do_seek (src, event);
case GST_EVENT_SIZE:
{
GstFormat format;
format = GST_EVENT_SIZE_FORMAT (event);
if (format == GST_FORMAT_DEFAULT)
format = GST_FORMAT_BYTES;
/* we can only accept bytes */
if (format != GST_FORMAT_BYTES)
return FALSE;
src->blocksize = GST_EVENT_SIZE_VALUE (event);
g_object_notify (G_OBJECT (src), "blocksize");
break;
}
case GST_EVENT_FLUSH:
/* cancel any blocking getrange */
if (!GST_EVENT_FLUSH_DONE (event))
gst_basesrc_unlock (src);
break;
default:
break;
......@@ -280,12 +432,41 @@ gst_basesrc_get_range_unlocked (GstPad * pad, guint64 offset, guint length,
src = GST_BASESRC (GST_OBJECT_PARENT (pad));
bclass = GST_BASESRC_GET_CLASS (src);
if (bclass->create)
ret = bclass->create (src, offset, length, buf);
else
ret = GST_FLOW_ERROR;
if (!GST_FLAG_IS_SET (src, GST_BASESRC_STARTED))
goto not_started;
if (!bclass->create)
goto no_function;
/* check size */
if (src->size != -1) {
if (offset + length > src->size) {
if (bclass->get_size)
bclass->get_size (src, &src->size);
if (offset + length > src->size) {
length = src->size - offset;
}
}
}
if (length == 0)
return GST_FLOW_UNEXPECTED;
ret = bclass->create (src, offset, length, buf);
return ret;
/* ERROR */
not_started:
{
GST_DEBUG_OBJECT (src, "getrange but not started");
return GST_FLOW_WRONG_STATE;
}
no_function:
{
GST_DEBUG_OBJECT (src, "no create function");
return GST_FLOW_ERROR;
}
}
static GstFlowReturn
......@@ -303,6 +484,21 @@ gst_basesrc_get_range (GstPad * pad, guint64 offset, guint length,
return fret;
}
static gboolean
gst_basesrc_check_get_range (GstPad * pad)
{
GstBaseSrc *src;
src = GST_BASESRC (GST_OBJECT_PARENT (pad));
if (!GST_FLAG_IS_SET (src, GST_BASESRC_STARTED)) {
gst_basesrc_start (src);
gst_basesrc_stop (src);
}
return src->seekable;
}
static void
gst_basesrc_loop (GstPad * pad)
{
......@@ -314,11 +510,9 @@ gst_basesrc_loop (GstPad * pad)
GST_STREAM_LOCK (pad);
ret =
gst_basesrc_get_range_unlocked (pad, src->offset, DEFAULT_BLOCKSIZE,
&buf);
ret = gst_basesrc_get_range_unlocked (pad, src->offset, src->blocksize, &buf);
if (ret != GST_FLOW_OK)
goto pause;
goto eos;
src->offset += GST_BUFFER_SIZE (buf);
......@@ -329,8 +523,17 @@ gst_basesrc_loop (GstPad * pad)
GST_STREAM_UNLOCK (pad);
return;
eos:
{
GST_DEBUG_OBJECT (src, "going to EOS");
gst_task_pause (GST_RPAD_TASK (pad));
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
GST_STREAM_UNLOCK (pad);
return;
}
pause:
{
GST_DEBUG_OBJECT (src, "pausing task");
gst_task_pause (GST_RPAD_TASK (pad));
GST_STREAM_UNLOCK (pad);
return;
......@@ -338,13 +541,134 @@ pause:
}
static gboolean
gst_basesrc_activate (GstPad * pad, GstActivateMode mode)
gst_basesrc_unlock (GstBaseSrc * basesrc)
{
GstBaseSrcClass *bclass;
gboolean result = FALSE;
bclass = GST_BASESRC_GET_CLASS (basesrc);
if (bclass->unlock)
result = bclass->unlock (basesrc);
return result;
}
static gboolean
gst_basesrc_get_size (GstBaseSrc * basesrc, guint64 * size)
{
GstBaseSrcClass *bclass;
gboolean result = FALSE;
bclass = GST_BASESRC_GET_CLASS (basesrc);
if (bclass->get_size)
result = bclass->get_size (basesrc, size);
if (result)
basesrc->size = *size;
return result;
}
static gboolean
gst_basesrc_start (GstBaseSrc * basesrc)
{
GstBaseSrcClass *bclass;
gboolean result;
if (GST_FLAG_IS_SET (basesrc, GST_BASESRC_STARTED))
return TRUE;
bclass = GST_BASESRC_GET_CLASS (basesrc);
if (bclass->start)
result = bclass->start (basesrc);
else
result = TRUE;
if (!result)
goto could_not_start;
GST_FLAG_SET (basesrc, GST_BASESRC_STARTED);
/* start in the beginning */
basesrc->offset = 0;
basesrc->segment_start = 0;
/* figure out the size */
if (bclass->get_size)
result = bclass->get_size (basesrc, &basesrc->size);
else
result = FALSE;
GST_DEBUG ("size %lld", basesrc->size);
/* we always run to the end */
basesrc->segment_end = -1;
/* check if we can seek */
if (bclass->is_seekable)
basesrc->seekable = bclass->is_seekable (basesrc);
else
basesrc->seekable = FALSE;
/* run typefind */
if (basesrc->seekable) {
GstCaps *caps;
caps = gst_basesrc_type_find (basesrc);
gst_pad_set_caps (basesrc->srcpad, caps);
}
return TRUE;
/* ERROR */
could_not_start:
{
GST_DEBUG_OBJECT (basesrc, "could not start");
return FALSE;
}
}
static gboolean
gst_basesrc_stop (GstBaseSrc * basesrc)
{
GstBaseSrcClass *bclass;
gboolean result = TRUE;
if (!GST_FLAG_IS_SET (basesrc, GST_BASESRC_STARTED))
return TRUE;
bclass = GST_BASESRC_GET_CLASS (basesrc);
if (bclass->stop)
result = bclass->stop (basesrc);
if (result)
GST_FLAG_UNSET (basesrc, GST_BASESRC_STARTED);
return result;
}
static gboolean
gst_basesrc_activate (GstPad * pad, GstActivateMode mode)
{
gboolean result;
GstBaseSrc *basesrc;
basesrc = GST_BASESRC (GST_OBJECT_PARENT (pad));
/* prepare subclass first */
switch (mode) {
case GST_ACTIVATE_PUSH:
case GST_ACTIVATE_PULL:
result = gst_basesrc_start (basesrc);
break;
default:
result = TRUE;
break;
}
/* if that failed we can stop here */
if (!result)
goto error_start;
result = FALSE;
switch (mode) {
case GST_ACTIVATE_PUSH:
/* if we have a scheduler we can start the task */
......@@ -379,6 +703,13 @@ gst_basesrc_activate (GstPad * pad, GstActivateMode mode)
break;
}
return result;
/* ERROR */
error_start:
{
GST_DEBUG_OBJECT (basesrc, "failed to start");
return FALSE;
}
}
static GstElementStateReturn
......@@ -396,10 +727,7 @@ gst_basesrc_change_state (GstElement * element)
case GST_STATE_NULL_TO_READY:
break;
case GST_STATE_READY_TO_PAUSED:
{
basesrc->offset = 0;
break;
}
case GST_STATE_PAUSED_TO_PLAYING:
break;
default:
......@@ -412,6 +740,7 @@ gst_basesrc_change_state (GstElement * element)
case GST_STATE_PLAYING_TO_PAUSED:
break;
case GST_STATE_PAUSED_TO_READY:
result = gst_basesrc_stop (basesrc);
break;
case GST_STATE_READY_TO_NULL:
break;
......@@ -421,3 +750,100 @@ gst_basesrc_change_state (GstElement * element)
return result;
}
/**
* typefind code here
*/
typedef struct
{
GstBaseSrc *src;
guint best_probability;
GstCaps *caps;
GstBuffer *buffer;
}
BaseSrcTypeFind;
static guint8 *
basesrc_find_peek (gpointer data, gint64 offset, guint size)
{
BaseSrcTypeFind *find;
GstBuffer *buffer;
GstBaseSrc *src;
GstFlowReturn ret;
if (size == 0)
return NULL;
find = (BaseSrcTypeFind *) data;
src = find->src;
if (offset < 0) {
offset += src->size;
}
buffer = NULL;
ret = gst_basesrc_get_range_unlocked (src->srcpad, offset, size, &buffer);
if (find->buffer) {
gst_buffer_unref (find->buffer);
find->buffer = NULL;
}
if (ret != GST_FLOW_OK)
goto error;
find->buffer = buffer;
return GST_BUFFER_DATA (buffer);
error:
{
return NULL;
}
}
static void
basesrc_find_suggest (gpointer data, guint probability, const GstCaps * caps)
{
BaseSrcTypeFind *find = (BaseSrcTypeFind *) data;
if (probability > find->best_probability) {
gst_caps_replace (&find->caps, gst_caps_copy (caps));
find->best_probability = probability;
}
}
static GstCaps *
gst_basesrc_type_find (GstBaseSrc * src)
{
GstTypeFind gst_find;
BaseSrcTypeFind find;
GList *walk, *type_list = NULL;
GstCaps *result = NULL;
walk = type_list = gst_type_find_factory_get_list ();
find.src = src;
find.best_probability = 0;