gst/base/gstbasesrc.c: Move some stuff around and cleanup things.

Original commit message from CVS:
* gst/base/gstbasesrc.c: (gst_base_src_class_init),
(gst_base_src_init), (gst_base_src_query),
(gst_base_src_default_newsegment),
(gst_base_src_configure_segment), (gst_base_src_do_seek),
(gst_base_src_send_event), (gst_base_src_event_handler),
(gst_base_src_pad_get_range), (gst_base_src_loop),
(gst_base_src_unlock), (gst_base_src_default_negotiate),
(gst_base_src_start), (gst_base_src_deactivate),
(gst_base_src_activate_push), (gst_base_src_change_state):
Move some stuff around and cleanup things.
This commit is contained in:
Wim Taymans 2005-10-27 19:37:25 +00:00
parent 27918022a2
commit 2e7411e8b8
3 changed files with 521 additions and 170 deletions

View file

@ -1,3 +1,16 @@
2005-10-27 Wim Taymans <wim@fluendo.com>
* gst/base/gstbasesrc.c: (gst_base_src_class_init),
(gst_base_src_init), (gst_base_src_query),
(gst_base_src_default_newsegment),
(gst_base_src_configure_segment), (gst_base_src_do_seek),
(gst_base_src_send_event), (gst_base_src_event_handler),
(gst_base_src_pad_get_range), (gst_base_src_loop),
(gst_base_src_unlock), (gst_base_src_default_negotiate),
(gst_base_src_start), (gst_base_src_deactivate),
(gst_base_src_activate_push), (gst_base_src_change_state):
Move some stuff around and cleanup things.
2005-10-27 Tim-Philipp Müller <tim at centricular dot net>
* gst/base/gstbasesrc.c: (gst_base_src_query):

View file

@ -113,6 +113,7 @@ static void gst_base_src_set_property (GObject * object, guint prop_id,
static void gst_base_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static gboolean gst_base_src_event_handler (GstPad * pad, GstEvent * event);
static gboolean gst_base_src_send_event (GstElement * elem, GstEvent * event);
static gboolean gst_base_src_query (GstPad * pad, GstQuery * query);
@ -167,6 +168,7 @@ gst_base_src_class_init (GstBaseSrcClass * klass)
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_base_src_change_state);
gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_src_send_event);
klass->negotiate = gst_base_src_default_negotiate;
klass->newsegment = gst_base_src_default_newsegment;
@ -214,10 +216,6 @@ gst_base_src_init (GstBaseSrc * basesrc, gpointer g_class)
GST_DEBUG_OBJECT (basesrc, "adding src pad");
gst_element_add_pad (GST_ELEMENT (basesrc), pad);
basesrc->segment_loop = FALSE;
basesrc->segment_start = -1;
basesrc->segment_end = -1;
basesrc->need_newsegment = TRUE;
basesrc->blocksize = DEFAULT_BLOCKSIZE;
basesrc->clock_id = NULL;
@ -392,10 +390,26 @@ gst_base_src_query (GstPad * pad, GstQuery * query)
case GST_QUERY_SEEKING:
gst_query_set_seeking (query, GST_FORMAT_BYTES,
src->seekable, src->segment_start, src->segment_end);
src->seekable, 0, src->size);
res = TRUE;
break;
case GST_QUERY_SEGMENT:
{
gint64 start, stop;
start = src->segment_start;
/* no end segment configured, current size then */
if ((stop = src->segment_end) == -1)
stop = src->size;
/* FIXME, we can't report our rate as we did not store it, d'oh!.
* Also, subclasses might want to support other formats. */
gst_query_set_segment (query, 1.0, GST_FORMAT_BYTES, start, stop);
res = TRUE;
break;
}
case GST_QUERY_FORMATS:
gst_query_set_formats (query, 3, GST_FORMAT_DEFAULT,
GST_FORMAT_BYTES, GST_FORMAT_PERCENT);
@ -421,12 +435,11 @@ gst_base_src_default_newsegment (GstBaseSrc * src)
GstEvent *event;
GST_DEBUG_OBJECT (src, "Sending newsegment from %" G_GINT64_FORMAT
" to %" G_GINT64_FORMAT, (gint64) src->segment_start,
(gint64) src->segment_end);
" to %" G_GINT64_FORMAT, src->segment_start, src->segment_end);
event = gst_event_new_newsegment (FALSE, 1.0,
GST_FORMAT_BYTES,
(gint64) src->segment_start, (gint64) src->segment_end,
(gint64) src->segment_start);
GST_FORMAT_BYTES, src->segment_start, src->segment_end,
src->segment_start);
return gst_pad_push_event (src->srcpad, event);
}
@ -445,45 +458,31 @@ gst_base_src_newsegment (GstBaseSrc * src)
return result;
}
/* based on the event parameters configure the segment_start/stop
* times. Called with STREAM_LOCK.
*/
static gboolean
gst_base_src_do_seek (GstBaseSrc * src, GstEvent * event)
gst_base_src_configure_segment (GstBaseSrc * src, GstEvent * event)
{
gdouble rate;
GstFormat format;
GstSeekFlags flags;
GstSeekType cur_type, stop_type;
gint64 cur, stop;
gboolean flush;
gboolean update_stop = TRUE, update_start = TRUE;
gboolean update_stop, update_start;
gst_event_parse_seek (event, &rate, &format, &flags,
&cur_type, &cur, &stop_type, &stop);
/* get seek format */
if (format == GST_FORMAT_DEFAULT)
format = GST_FORMAT_BYTES;
/* we can only seek bytes */
if (format != GST_FORMAT_BYTES)
goto unsupported_seek;
flush = flags & GST_SEEK_FLAG_FLUSH;
/* get seek positions */
/* parse the loop flag */
/* FIXME, need to store other flags and rate too */
src->segment_loop = (flags & GST_SEEK_FLAG_SEGMENT) != 0;
/* send flush start */
if (flush)
gst_pad_push_event (src->srcpad, gst_event_new_flush_start ());
else
gst_pad_pause_task (src->srcpad);
/* assume we'll update both start and stop values */
update_start = TRUE;
update_stop = TRUE;
/* unblock streaming thread */
gst_base_src_unlock (src);
/* grab streaming lock */
GST_STREAM_LOCK (src->srcpad);
/* perform the seek */
/* perform the seek, segment_start is never invalid */
switch (cur_type) {
case GST_SEEK_TYPE_NONE:
/* no update to segment */
@ -491,7 +490,7 @@ gst_base_src_do_seek (GstBaseSrc * src, GstEvent * event)
update_start = FALSE;
break;
case GST_SEEK_TYPE_SET:
/* cur hold desired position */
/* cur holds desired position */
break;
case GST_SEEK_TYPE_CUR:
/* add cur to currently configure segment */
@ -503,64 +502,187 @@ gst_base_src_do_seek (GstBaseSrc * src, GstEvent * event)
break;
}
/* bring in sane range */
cur = CLAMP (cur, 0, src->size);
if (src->size != -1)
cur = CLAMP (cur, 0, src->size);
else
cur = MAX (cur, 0);
/* segment_end can be -1 if we have not configured a stop. */
switch (stop_type) {
case GST_SEEK_TYPE_NONE:
stop = src->segment_end;
update_stop = FALSE;
break;
case GST_SEEK_TYPE_SET:
/* stop folds required value */
break;
case GST_SEEK_TYPE_CUR:
stop = src->segment_end + stop;
if (src->segment_end != -1)
stop = src->segment_end + stop;
else
stop = -1;
break;
case GST_SEEK_TYPE_END:
stop = src->size + stop;
if (src->size != -1)
stop = src->size + stop;
else
stop = -1;
break;
}
stop = CLAMP (stop, 0, src->size);
/* if we have a valid stop time, make sure it is clipped */
if (stop != -1) {
if (src->size != -1)
stop = CLAMP (stop, 0, src->size);
else
stop = MAX (stop, 0);
}
src->segment_start = cur;
src->segment_end = stop;
/* update our offset */
src->offset = cur;
/* update our offset if it was updated */
if (update_start)
src->offset = cur;
GST_DEBUG_OBJECT (src, "seek pending for segment from %" G_GINT64_FORMAT
GST_DEBUG_OBJECT (src, "segment configured from %" G_GINT64_FORMAT
" to %" G_GINT64_FORMAT, src->segment_start, src->segment_end);
/* now make sure the newsegment will be send */
src->need_newsegment = TRUE;
return TRUE;
}
/* this code implements the seeking. It is a good example
* handling all cases (modulo the FIXMEs).
*
* A seek updates the currently configured segment_start
* and segment_stop values based on the SEEK_TYPE. If the
* segment_start value is updated, a seek to this new position
* should be performed.
*
* The seek can only be executed when we are not currently
* streaming any data, to make sure that this is the case, we
* acquire the STREAM_LOCK which is taken when we are in the
* _loop() function or when a getrange() is called. Normally
* we will not receive a seek if we are operating in pull mode
* though.
*
* When we are in the loop() function, we might be in the middle
* of pushing a buffer, which might block in a sink. To make sure
* that the push gets unblocked we push out a FLUSH_START event.
* Our loop function will get a WRONG_STATE return value from
* the push and will pause, effectively releasing the STREAM_LOCK.
*
* For a non-flushing seek, we pause the task, which might eventually
* release the STREAM_LOCK. We say eventually because when the sink
* blocks on the sample we might wait a very long time until the sink
* unblocks the sample. In any case we acquire the STREAM_LOCK and
* can continue the seek. A non-flushing seek is normally done in a
* running pipeline to perform seamless playback.
*
* After updating the segment_start/stop values, we prepare for
* streaming again. We push out a FLUSH_STOP to make the peer pad
* accept data again and we start our task again.
*
* A segment seek posts a message on the bus saying that the playback
* of the segment started. We store the segment flag internally because
* when we reach the segment_stop we have to post a segment_done
* instead of EOS when doing a segment seek.
*/
static gboolean
gst_base_src_do_seek (GstBaseSrc * src, GstEvent * event)
{
gdouble rate;
GstFormat format;
GstSeekFlags flags;
gboolean flush;
gst_event_parse_seek (event, &rate, &format, &flags, NULL, NULL, NULL, NULL);
/* FIXME subclasses should be able to provide other formats */
/* get seek format */
if (format == GST_FORMAT_DEFAULT)
format = GST_FORMAT_BYTES;
/* we can only seek bytes */
if (format != GST_FORMAT_BYTES)
goto unsupported_seek;
flush = flags & GST_SEEK_FLAG_FLUSH;
/* send flush start */
if (flush)
/* send flush stop */
gst_pad_push_event (src->srcpad, gst_event_new_flush_start ());
else
gst_pad_pause_task (src->srcpad);
/* unblock streaming thread */
gst_base_src_unlock (src);
/* grab streaming lock, this should eventually be possible, either
* because the task is paused or out streaming thread stopped
* because our peer is flushing. */
GST_STREAM_LOCK (src->srcpad);
/* now configure the segment */
gst_base_src_configure_segment (src, event);
/* and prepare to continue streaming */
if (flush)
/* send flush stop, peer will accept data and events again. We
* are not yet providing data as we still have the STREAM_LOCK. */
gst_pad_push_event (src->srcpad, gst_event_new_flush_stop ());
/* now make sure the newsegment will be send from the streaming
* thread. We could opt to send it here too. */
src->need_newsegment = TRUE;
if (src->segment_loop) {
/* FIXME subclasses should be able to provide other formats */
gst_element_post_message (GST_ELEMENT (src),
gst_message_new_segment_start (GST_OBJECT (src), GST_FORMAT_BYTES,
cur));
src->segment_start));
}
/* and restart the task */
/* and restart the task in case it got paused explicitely or by
* the FLUSH_START event we pushed out. */
gst_pad_start_task (src->srcpad, (GstTaskFunction) gst_base_src_loop,
src->srcpad);
/* and release the lock again so we can continue streaming */
GST_STREAM_UNLOCK (src->srcpad);
gst_event_unref (event);
return TRUE;
/* ERROR */
unsupported_seek:
{
GST_DEBUG_OBJECT (src, "invalid format, seek aborted.");
return FALSE;
}
}
/* all events send to this element directly
*/
static gboolean
gst_base_src_send_event (GstElement * element, GstEvent * event)
{
GstBaseSrc *src;
gboolean result;
src = GST_BASE_SRC (element);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
result = gst_base_src_configure_segment (src, event);
break;
default:
result = FALSE;
break;
}
return result;
}
static gboolean
gst_base_src_event_handler (GstPad * pad, GstEvent * event)
{
@ -568,31 +690,52 @@ gst_base_src_event_handler (GstPad * pad, GstEvent * event)
GstBaseSrcClass *bclass;
gboolean result;
src = GST_BASE_SRC (GST_PAD_PARENT (pad));
src = GST_BASE_SRC (gst_pad_get_parent (pad));
bclass = GST_BASE_SRC_GET_CLASS (src);
if (bclass->event)
result = bclass->event (src, event);
if (bclass->event) {
if (!(result = bclass->event (src, event)))
goto subclass_failed;
}
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
if (!src->seekable) {
gst_event_unref (event);
return FALSE;
}
return gst_base_src_do_seek (src, event);
/* is normally called when in push mode */
if (!src->seekable)
goto not_seekable;
result = gst_base_src_do_seek (src, event);
break;
case GST_EVENT_FLUSH_START:
/* cancel any blocking getrange */
gst_base_src_unlock (src);
/* cancel any blocking getrange, is normally called
* when in pull mode. */
result = gst_base_src_unlock (src);
break;
case GST_EVENT_FLUSH_STOP:
break;
default:
result = TRUE;
break;
}
gst_event_unref (event);
gst_object_unref (src);
return TRUE;
return result;
/* ERRORS */
subclass_failed:
{
GST_DEBUG_OBJECT (src, "subclass refused event");
gst_object_unref (src);
gst_event_unref (event);
return result;
}
not_seekable:
{
GST_DEBUG_OBJECT (src, "is not seekable");
gst_object_unref (src);
gst_event_unref (event);
return FALSE;
}
}
static void
@ -772,6 +915,7 @@ gst_base_src_loop (GstPad * pad)
src = GST_BASE_SRC (gst_pad_get_parent (pad));
/* only send segments when operating in push mode */
if (G_UNLIKELY (src->need_newsegment)) {
/* now send newsegment */
gst_base_src_newsegment (src);
@ -800,9 +944,10 @@ gst_base_src_loop (GstPad * pad)
/* special cases */
eos:
{
GST_DEBUG_OBJECT (src, "going to EOS");
GST_DEBUG_OBJECT (src, "going to EOS, getrange returned UNEXPECTED");
gst_pad_pause_task (pad);
if (src->segment_loop) {
/* FIXME, subclass might want to use another format */
gst_element_post_message (GST_ELEMENT (src),
gst_message_new_segment_done (GST_OBJECT (src),
GST_FORMAT_BYTES, src->segment_end));
@ -815,9 +960,7 @@ eos:
}
pause:
{
const gchar *reason;
reason = gst_flow_get_name (ret);
const gchar *reason = gst_flow_get_name (ret);
GST_DEBUG_OBJECT (src, "pausing task, reason %s", reason);
gst_pad_pause_task (pad);
@ -850,7 +993,7 @@ static gboolean
gst_base_src_unlock (GstBaseSrc * basesrc)
{
GstBaseSrcClass *bclass;
gboolean result = FALSE;
gboolean result = TRUE;
GST_DEBUG ("unlock");
/* unblock whatever the subclass is doing */
@ -905,6 +1048,7 @@ gst_base_src_is_seekable (GstBaseSrc * basesrc)
return basesrc->seekable;
}
/* default negotiation code */
static gboolean
gst_base_src_default_negotiate (GstBaseSrc * basesrc)
{
@ -913,38 +1057,48 @@ gst_base_src_default_negotiate (GstBaseSrc * basesrc)
GstCaps *peercaps = NULL;
gboolean result = FALSE;
/* first see what is possible on our source pad */
thiscaps = gst_pad_get_caps (GST_BASE_SRC_PAD (basesrc));
GST_DEBUG ("caps of src: %" GST_PTR_FORMAT, thiscaps);
/* nothing or anything is allowed, we're done */
if (thiscaps == NULL || gst_caps_is_any (thiscaps))
goto no_nego_needed;
/* get the peer caps */
peercaps = gst_pad_peer_get_caps (GST_BASE_SRC_PAD (basesrc));
GST_DEBUG ("caps of peer: %" GST_PTR_FORMAT, peercaps);
if (peercaps) {
GstCaps *icaps;
/* get intersection */
icaps = gst_caps_intersect (thiscaps, peercaps);
GST_DEBUG ("intersect: %" GST_PTR_FORMAT, icaps);
gst_caps_unref (thiscaps);
gst_caps_unref (peercaps);
if (icaps) {
/* take first (and best) possibility */
caps = gst_caps_copy_nth (icaps, 0);
gst_caps_unref (icaps);
}
} else {
/* no peer, work with our own caps then */
caps = thiscaps;
}
if (caps) {
caps = gst_caps_make_writable (caps);
gst_caps_truncate (caps);
/* now fixate */
gst_pad_fixate_caps (GST_BASE_SRC_PAD (basesrc), caps);
GST_DEBUG ("fixated to: %" GST_PTR_FORMAT, caps);
if (gst_caps_is_any (caps)) {
/* hmm, still anything, so element can do anything and
* nego is not needed */
gst_caps_unref (caps);
result = TRUE;
} else if (gst_caps_is_fixed (caps)) {
/* yay, fixed caps, use those then */
gst_pad_set_caps (GST_BASE_SRC_PAD (basesrc), caps);
gst_caps_unref (caps);
result = TRUE;
@ -999,9 +1153,6 @@ gst_base_src_start (GstBaseSrc * basesrc)
GST_OBJECT_FLAG_SET (basesrc, GST_BASE_SRC_STARTED);
/* start in the beginning */
basesrc->offset = 0;
/* figure out the size */
if (bclass->get_size) {
result = bclass->get_size (basesrc, &basesrc->size);
@ -1014,15 +1165,11 @@ gst_base_src_start (GstBaseSrc * basesrc)
GST_DEBUG ("size %d %lld", result, basesrc->size);
/* we always run to the end when starting */
basesrc->segment_loop = FALSE;
basesrc->segment_start = 0;
basesrc->segment_end = basesrc->size;
basesrc->need_newsegment = TRUE;
/* check if we can seek, updates ->seekable */
gst_base_src_is_seekable (basesrc);
basesrc->need_newsegment = TRUE;
/* run typefind */
#if 0
if (basesrc->seekable) {
@ -1088,10 +1235,10 @@ gst_base_src_deactivate (GstBaseSrc * basesrc, GstPad * pad)
GST_LIVE_UNLOCK (basesrc);
/* step 1, unblock clock sync (if any) */
gst_base_src_unlock (basesrc);
result = gst_base_src_unlock (basesrc);
/* step 2, make sure streaming finishes */
result = gst_pad_stop_task (pad);
result &= gst_pad_stop_task (pad);
return result;
}
@ -1100,6 +1247,7 @@ static gboolean
gst_base_src_activate_push (GstPad * pad, gboolean active)
{
GstBaseSrc *basesrc;
gboolean res;
basesrc = GST_BASE_SRC (GST_OBJECT_PARENT (pad));
@ -1113,12 +1261,14 @@ gst_base_src_activate_push (GstPad * pad, gboolean active)
if (!gst_base_src_start (basesrc))
goto error_start;
return gst_pad_start_task (pad, (GstTaskFunction) gst_base_src_loop, pad);
res = gst_pad_start_task (pad, (GstTaskFunction) gst_base_src_loop, pad);
} else {
GST_DEBUG_OBJECT (basesrc, "Deactivating in push mode");
return gst_base_src_deactivate (basesrc, pad);
res = gst_base_src_deactivate (basesrc, pad);
}
return res;
/* ERRORS */
no_push_activation:
{
GST_DEBUG_OBJECT (basesrc, "Subclass disabled push-mode activation");
@ -1182,7 +1332,6 @@ gst_base_src_change_state (GstElement * element, GstStateChange transition)
basesrc = GST_BASE_SRC (element);
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
break;
@ -1212,6 +1361,15 @@ gst_base_src_change_state (GstElement * element, GstStateChange transition)
goto failure;
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
/* we always run from start to end when in READY, after putting
* the element to READY a seek can be done on the element to
* configure the segment when going to PAUSED. */
basesrc->segment_loop = FALSE;
basesrc->segment_start = 0;
basesrc->segment_end = -1;
basesrc->offset = 0;
break;
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
GST_LIVE_LOCK (element);
if (basesrc->is_live) {
@ -1222,7 +1380,12 @@ gst_base_src_change_state (GstElement * element, GstStateChange transition)
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
if (!gst_base_src_stop (basesrc))
result = GST_STATE_CHANGE_FAILURE;
goto error_stop;
/* we always run from start to end when in READY */
basesrc->segment_loop = FALSE;
basesrc->segment_start = 0;
basesrc->segment_end = -1;
basesrc->offset = 0;
break;
case GST_STATE_CHANGE_READY_TO_NULL:
break;
@ -1231,14 +1394,20 @@ gst_base_src_change_state (GstElement * element, GstStateChange transition)
}
if (no_preroll && result == GST_STATE_CHANGE_SUCCESS)
return GST_STATE_CHANGE_NO_PREROLL;
else
return result;
result = GST_STATE_CHANGE_NO_PREROLL;
return result;
/* ERRORS */
failure:
{
GST_DEBUG_OBJECT (basesrc, "parent failed state change");
gst_base_src_stop (basesrc);
return result;
}
error_stop:
{
GST_DEBUG_OBJECT (basesrc, "Failed to stop");
return GST_STATE_CHANGE_FAILURE;
}
}

View file

@ -113,6 +113,7 @@ static void gst_base_src_set_property (GObject * object, guint prop_id,
static void gst_base_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static gboolean gst_base_src_event_handler (GstPad * pad, GstEvent * event);
static gboolean gst_base_src_send_event (GstElement * elem, GstEvent * event);
static gboolean gst_base_src_query (GstPad * pad, GstQuery * query);
@ -167,6 +168,7 @@ gst_base_src_class_init (GstBaseSrcClass * klass)
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_base_src_change_state);
gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_src_send_event);
klass->negotiate = gst_base_src_default_negotiate;
klass->newsegment = gst_base_src_default_newsegment;
@ -214,10 +216,6 @@ gst_base_src_init (GstBaseSrc * basesrc, gpointer g_class)
GST_DEBUG_OBJECT (basesrc, "adding src pad");
gst_element_add_pad (GST_ELEMENT (basesrc), pad);
basesrc->segment_loop = FALSE;
basesrc->segment_start = -1;
basesrc->segment_end = -1;
basesrc->need_newsegment = TRUE;
basesrc->blocksize = DEFAULT_BLOCKSIZE;
basesrc->clock_id = NULL;
@ -392,10 +390,26 @@ gst_base_src_query (GstPad * pad, GstQuery * query)
case GST_QUERY_SEEKING:
gst_query_set_seeking (query, GST_FORMAT_BYTES,
src->seekable, src->segment_start, src->segment_end);
src->seekable, 0, src->size);
res = TRUE;
break;
case GST_QUERY_SEGMENT:
{
gint64 start, stop;
start = src->segment_start;
/* no end segment configured, current size then */
if ((stop = src->segment_end) == -1)
stop = src->size;
/* FIXME, we can't report our rate as we did not store it, d'oh!.
* Also, subclasses might want to support other formats. */
gst_query_set_segment (query, 1.0, GST_FORMAT_BYTES, start, stop);
res = TRUE;
break;
}
case GST_QUERY_FORMATS:
gst_query_set_formats (query, 3, GST_FORMAT_DEFAULT,
GST_FORMAT_BYTES, GST_FORMAT_PERCENT);
@ -421,12 +435,11 @@ gst_base_src_default_newsegment (GstBaseSrc * src)
GstEvent *event;
GST_DEBUG_OBJECT (src, "Sending newsegment from %" G_GINT64_FORMAT
" to %" G_GINT64_FORMAT, (gint64) src->segment_start,
(gint64) src->segment_end);
" to %" G_GINT64_FORMAT, src->segment_start, src->segment_end);
event = gst_event_new_newsegment (FALSE, 1.0,
GST_FORMAT_BYTES,
(gint64) src->segment_start, (gint64) src->segment_end,
(gint64) src->segment_start);
GST_FORMAT_BYTES, src->segment_start, src->segment_end,
src->segment_start);
return gst_pad_push_event (src->srcpad, event);
}
@ -445,45 +458,31 @@ gst_base_src_newsegment (GstBaseSrc * src)
return result;
}
/* based on the event parameters configure the segment_start/stop
* times. Called with STREAM_LOCK.
*/
static gboolean
gst_base_src_do_seek (GstBaseSrc * src, GstEvent * event)
gst_base_src_configure_segment (GstBaseSrc * src, GstEvent * event)
{
gdouble rate;
GstFormat format;
GstSeekFlags flags;
GstSeekType cur_type, stop_type;
gint64 cur, stop;
gboolean flush;
gboolean update_stop = TRUE, update_start = TRUE;
gboolean update_stop, update_start;
gst_event_parse_seek (event, &rate, &format, &flags,
&cur_type, &cur, &stop_type, &stop);
/* get seek format */
if (format == GST_FORMAT_DEFAULT)
format = GST_FORMAT_BYTES;
/* we can only seek bytes */
if (format != GST_FORMAT_BYTES)
goto unsupported_seek;
flush = flags & GST_SEEK_FLAG_FLUSH;
/* get seek positions */
/* parse the loop flag */
/* FIXME, need to store other flags and rate too */
src->segment_loop = (flags & GST_SEEK_FLAG_SEGMENT) != 0;
/* send flush start */
if (flush)
gst_pad_push_event (src->srcpad, gst_event_new_flush_start ());
else
gst_pad_pause_task (src->srcpad);
/* assume we'll update both start and stop values */
update_start = TRUE;
update_stop = TRUE;
/* unblock streaming thread */
gst_base_src_unlock (src);
/* grab streaming lock */
GST_STREAM_LOCK (src->srcpad);
/* perform the seek */
/* perform the seek, segment_start is never invalid */
switch (cur_type) {
case GST_SEEK_TYPE_NONE:
/* no update to segment */
@ -491,7 +490,7 @@ gst_base_src_do_seek (GstBaseSrc * src, GstEvent * event)
update_start = FALSE;
break;
case GST_SEEK_TYPE_SET:
/* cur hold desired position */
/* cur holds desired position */
break;
case GST_SEEK_TYPE_CUR:
/* add cur to currently configure segment */
@ -503,64 +502,187 @@ gst_base_src_do_seek (GstBaseSrc * src, GstEvent * event)
break;
}
/* bring in sane range */
cur = CLAMP (cur, 0, src->size);
if (src->size != -1)
cur = CLAMP (cur, 0, src->size);
else
cur = MAX (cur, 0);
/* segment_end can be -1 if we have not configured a stop. */
switch (stop_type) {
case GST_SEEK_TYPE_NONE:
stop = src->segment_end;
update_stop = FALSE;
break;
case GST_SEEK_TYPE_SET:
/* stop folds required value */
break;
case GST_SEEK_TYPE_CUR:
stop = src->segment_end + stop;
if (src->segment_end != -1)
stop = src->segment_end + stop;
else
stop = -1;
break;
case GST_SEEK_TYPE_END:
stop = src->size + stop;
if (src->size != -1)
stop = src->size + stop;
else
stop = -1;
break;
}
stop = CLAMP (stop, 0, src->size);
/* if we have a valid stop time, make sure it is clipped */
if (stop != -1) {
if (src->size != -1)
stop = CLAMP (stop, 0, src->size);
else
stop = MAX (stop, 0);
}
src->segment_start = cur;
src->segment_end = stop;
/* update our offset */
src->offset = cur;
/* update our offset if it was updated */
if (update_start)
src->offset = cur;
GST_DEBUG_OBJECT (src, "seek pending for segment from %" G_GINT64_FORMAT
GST_DEBUG_OBJECT (src, "segment configured from %" G_GINT64_FORMAT
" to %" G_GINT64_FORMAT, src->segment_start, src->segment_end);
/* now make sure the newsegment will be send */
src->need_newsegment = TRUE;
return TRUE;
}
/* this code implements the seeking. It is a good example
* handling all cases (modulo the FIXMEs).
*
* A seek updates the currently configured segment_start
* and segment_stop values based on the SEEK_TYPE. If the
* segment_start value is updated, a seek to this new position
* should be performed.
*
* The seek can only be executed when we are not currently
* streaming any data, to make sure that this is the case, we
* acquire the STREAM_LOCK which is taken when we are in the
* _loop() function or when a getrange() is called. Normally
* we will not receive a seek if we are operating in pull mode
* though.
*
* When we are in the loop() function, we might be in the middle
* of pushing a buffer, which might block in a sink. To make sure
* that the push gets unblocked we push out a FLUSH_START event.
* Our loop function will get a WRONG_STATE return value from
* the push and will pause, effectively releasing the STREAM_LOCK.
*
* For a non-flushing seek, we pause the task, which might eventually
* release the STREAM_LOCK. We say eventually because when the sink
* blocks on the sample we might wait a very long time until the sink
* unblocks the sample. In any case we acquire the STREAM_LOCK and
* can continue the seek. A non-flushing seek is normally done in a
* running pipeline to perform seamless playback.
*
* After updating the segment_start/stop values, we prepare for
* streaming again. We push out a FLUSH_STOP to make the peer pad
* accept data again and we start our task again.
*
* A segment seek posts a message on the bus saying that the playback
* of the segment started. We store the segment flag internally because
* when we reach the segment_stop we have to post a segment_done
* instead of EOS when doing a segment seek.
*/
static gboolean
gst_base_src_do_seek (GstBaseSrc * src, GstEvent * event)
{
gdouble rate;
GstFormat format;
GstSeekFlags flags;
gboolean flush;
gst_event_parse_seek (event, &rate, &format, &flags, NULL, NULL, NULL, NULL);
/* FIXME subclasses should be able to provide other formats */
/* get seek format */
if (format == GST_FORMAT_DEFAULT)
format = GST_FORMAT_BYTES;
/* we can only seek bytes */
if (format != GST_FORMAT_BYTES)
goto unsupported_seek;
flush = flags & GST_SEEK_FLAG_FLUSH;
/* send flush start */
if (flush)
/* send flush stop */
gst_pad_push_event (src->srcpad, gst_event_new_flush_start ());
else
gst_pad_pause_task (src->srcpad);
/* unblock streaming thread */
gst_base_src_unlock (src);
/* grab streaming lock, this should eventually be possible, either
* because the task is paused or out streaming thread stopped
* because our peer is flushing. */
GST_STREAM_LOCK (src->srcpad);
/* now configure the segment */
gst_base_src_configure_segment (src, event);
/* and prepare to continue streaming */
if (flush)
/* send flush stop, peer will accept data and events again. We
* are not yet providing data as we still have the STREAM_LOCK. */
gst_pad_push_event (src->srcpad, gst_event_new_flush_stop ());
/* now make sure the newsegment will be send from the streaming
* thread. We could opt to send it here too. */
src->need_newsegment = TRUE;
if (src->segment_loop) {
/* FIXME subclasses should be able to provide other formats */
gst_element_post_message (GST_ELEMENT (src),
gst_message_new_segment_start (GST_OBJECT (src), GST_FORMAT_BYTES,
cur));
src->segment_start));
}
/* and restart the task */
/* and restart the task in case it got paused explicitely or by
* the FLUSH_START event we pushed out. */
gst_pad_start_task (src->srcpad, (GstTaskFunction) gst_base_src_loop,
src->srcpad);
/* and release the lock again so we can continue streaming */
GST_STREAM_UNLOCK (src->srcpad);
gst_event_unref (event);
return TRUE;
/* ERROR */
unsupported_seek:
{
GST_DEBUG_OBJECT (src, "invalid format, seek aborted.");
return FALSE;
}
}
/* all events send to this element directly
*/
static gboolean
gst_base_src_send_event (GstElement * element, GstEvent * event)
{
GstBaseSrc *src;
gboolean result;
src = GST_BASE_SRC (element);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
result = gst_base_src_configure_segment (src, event);
break;
default:
result = FALSE;
break;
}
return result;
}
static gboolean
gst_base_src_event_handler (GstPad * pad, GstEvent * event)
{
@ -568,31 +690,52 @@ gst_base_src_event_handler (GstPad * pad, GstEvent * event)
GstBaseSrcClass *bclass;
gboolean result;
src = GST_BASE_SRC (GST_PAD_PARENT (pad));
src = GST_BASE_SRC (gst_pad_get_parent (pad));
bclass = GST_BASE_SRC_GET_CLASS (src);
if (bclass->event)
result = bclass->event (src, event);
if (bclass->event) {
if (!(result = bclass->event (src, event)))
goto subclass_failed;
}
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
if (!src->seekable) {
gst_event_unref (event);
return FALSE;
}
return gst_base_src_do_seek (src, event);
/* is normally called when in push mode */
if (!src->seekable)
goto not_seekable;
result = gst_base_src_do_seek (src, event);
break;
case GST_EVENT_FLUSH_START:
/* cancel any blocking getrange */
gst_base_src_unlock (src);
/* cancel any blocking getrange, is normally called
* when in pull mode. */
result = gst_base_src_unlock (src);
break;
case GST_EVENT_FLUSH_STOP:
break;
default:
result = TRUE;
break;
}
gst_event_unref (event);
gst_object_unref (src);
return TRUE;
return result;
/* ERRORS */
subclass_failed:
{
GST_DEBUG_OBJECT (src, "subclass refused event");
gst_object_unref (src);
gst_event_unref (event);
return result;
}
not_seekable:
{
GST_DEBUG_OBJECT (src, "is not seekable");
gst_object_unref (src);
gst_event_unref (event);
return FALSE;
}
}
static void
@ -772,6 +915,7 @@ gst_base_src_loop (GstPad * pad)
src = GST_BASE_SRC (gst_pad_get_parent (pad));
/* only send segments when operating in push mode */
if (G_UNLIKELY (src->need_newsegment)) {
/* now send newsegment */
gst_base_src_newsegment (src);
@ -800,9 +944,10 @@ gst_base_src_loop (GstPad * pad)
/* special cases */
eos:
{
GST_DEBUG_OBJECT (src, "going to EOS");
GST_DEBUG_OBJECT (src, "going to EOS, getrange returned UNEXPECTED");
gst_pad_pause_task (pad);
if (src->segment_loop) {
/* FIXME, subclass might want to use another format */
gst_element_post_message (GST_ELEMENT (src),
gst_message_new_segment_done (GST_OBJECT (src),
GST_FORMAT_BYTES, src->segment_end));
@ -815,9 +960,7 @@ eos:
}
pause:
{
const gchar *reason;
reason = gst_flow_get_name (ret);
const gchar *reason = gst_flow_get_name (ret);
GST_DEBUG_OBJECT (src, "pausing task, reason %s", reason);
gst_pad_pause_task (pad);
@ -850,7 +993,7 @@ static gboolean
gst_base_src_unlock (GstBaseSrc * basesrc)
{
GstBaseSrcClass *bclass;
gboolean result = FALSE;
gboolean result = TRUE;
GST_DEBUG ("unlock");
/* unblock whatever the subclass is doing */
@ -905,6 +1048,7 @@ gst_base_src_is_seekable (GstBaseSrc * basesrc)
return basesrc->seekable;
}
/* default negotiation code */
static gboolean
gst_base_src_default_negotiate (GstBaseSrc * basesrc)
{
@ -913,38 +1057,48 @@ gst_base_src_default_negotiate (GstBaseSrc * basesrc)
GstCaps *peercaps = NULL;
gboolean result = FALSE;
/* first see what is possible on our source pad */
thiscaps = gst_pad_get_caps (GST_BASE_SRC_PAD (basesrc));
GST_DEBUG ("caps of src: %" GST_PTR_FORMAT, thiscaps);
/* nothing or anything is allowed, we're done */
if (thiscaps == NULL || gst_caps_is_any (thiscaps))
goto no_nego_needed;
/* get the peer caps */
peercaps = gst_pad_peer_get_caps (GST_BASE_SRC_PAD (basesrc));
GST_DEBUG ("caps of peer: %" GST_PTR_FORMAT, peercaps);
if (peercaps) {
GstCaps *icaps;
/* get intersection */
icaps = gst_caps_intersect (thiscaps, peercaps);
GST_DEBUG ("intersect: %" GST_PTR_FORMAT, icaps);
gst_caps_unref (thiscaps);
gst_caps_unref (peercaps);
if (icaps) {
/* take first (and best) possibility */
caps = gst_caps_copy_nth (icaps, 0);
gst_caps_unref (icaps);
}
} else {
/* no peer, work with our own caps then */
caps = thiscaps;
}
if (caps) {
caps = gst_caps_make_writable (caps);
gst_caps_truncate (caps);
/* now fixate */
gst_pad_fixate_caps (GST_BASE_SRC_PAD (basesrc), caps);
GST_DEBUG ("fixated to: %" GST_PTR_FORMAT, caps);
if (gst_caps_is_any (caps)) {
/* hmm, still anything, so element can do anything and
* nego is not needed */
gst_caps_unref (caps);
result = TRUE;
} else if (gst_caps_is_fixed (caps)) {
/* yay, fixed caps, use those then */
gst_pad_set_caps (GST_BASE_SRC_PAD (basesrc), caps);
gst_caps_unref (caps);
result = TRUE;
@ -999,9 +1153,6 @@ gst_base_src_start (GstBaseSrc * basesrc)
GST_OBJECT_FLAG_SET (basesrc, GST_BASE_SRC_STARTED);
/* start in the beginning */
basesrc->offset = 0;
/* figure out the size */
if (bclass->get_size) {
result = bclass->get_size (basesrc, &basesrc->size);
@ -1014,15 +1165,11 @@ gst_base_src_start (GstBaseSrc * basesrc)
GST_DEBUG ("size %d %lld", result, basesrc->size);
/* we always run to the end when starting */
basesrc->segment_loop = FALSE;
basesrc->segment_start = 0;
basesrc->segment_end = basesrc->size;
basesrc->need_newsegment = TRUE;
/* check if we can seek, updates ->seekable */
gst_base_src_is_seekable (basesrc);
basesrc->need_newsegment = TRUE;
/* run typefind */
#if 0
if (basesrc->seekable) {
@ -1088,10 +1235,10 @@ gst_base_src_deactivate (GstBaseSrc * basesrc, GstPad * pad)
GST_LIVE_UNLOCK (basesrc);
/* step 1, unblock clock sync (if any) */
gst_base_src_unlock (basesrc);
result = gst_base_src_unlock (basesrc);
/* step 2, make sure streaming finishes */
result = gst_pad_stop_task (pad);
result &= gst_pad_stop_task (pad);
return result;
}
@ -1100,6 +1247,7 @@ static gboolean
gst_base_src_activate_push (GstPad * pad, gboolean active)
{
GstBaseSrc *basesrc;
gboolean res;
basesrc = GST_BASE_SRC (GST_OBJECT_PARENT (pad));
@ -1113,12 +1261,14 @@ gst_base_src_activate_push (GstPad * pad, gboolean active)
if (!gst_base_src_start (basesrc))
goto error_start;
return gst_pad_start_task (pad, (GstTaskFunction) gst_base_src_loop, pad);
res = gst_pad_start_task (pad, (GstTaskFunction) gst_base_src_loop, pad);
} else {
GST_DEBUG_OBJECT (basesrc, "Deactivating in push mode");
return gst_base_src_deactivate (basesrc, pad);
res = gst_base_src_deactivate (basesrc, pad);
}
return res;
/* ERRORS */
no_push_activation:
{
GST_DEBUG_OBJECT (basesrc, "Subclass disabled push-mode activation");
@ -1182,7 +1332,6 @@ gst_base_src_change_state (GstElement * element, GstStateChange transition)
basesrc = GST_BASE_SRC (element);
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
break;
@ -1212,6 +1361,15 @@ gst_base_src_change_state (GstElement * element, GstStateChange transition)
goto failure;
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
/* we always run from start to end when in READY, after putting
* the element to READY a seek can be done on the element to
* configure the segment when going to PAUSED. */
basesrc->segment_loop = FALSE;
basesrc->segment_start = 0;
basesrc->segment_end = -1;
basesrc->offset = 0;
break;
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
GST_LIVE_LOCK (element);
if (basesrc->is_live) {
@ -1222,7 +1380,12 @@ gst_base_src_change_state (GstElement * element, GstStateChange transition)
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
if (!gst_base_src_stop (basesrc))
result = GST_STATE_CHANGE_FAILURE;
goto error_stop;
/* we always run from start to end when in READY */
basesrc->segment_loop = FALSE;
basesrc->segment_start = 0;
basesrc->segment_end = -1;
basesrc->offset = 0;
break;
case GST_STATE_CHANGE_READY_TO_NULL:
break;
@ -1231,14 +1394,20 @@ gst_base_src_change_state (GstElement * element, GstStateChange transition)
}
if (no_preroll && result == GST_STATE_CHANGE_SUCCESS)
return GST_STATE_CHANGE_NO_PREROLL;
else
return result;
result = GST_STATE_CHANGE_NO_PREROLL;
return result;
/* ERRORS */
failure:
{
GST_DEBUG_OBJECT (basesrc, "parent failed state change");
gst_base_src_stop (basesrc);
return result;
}
error_stop:
{
GST_DEBUG_OBJECT (basesrc, "Failed to stop");
return GST_STATE_CHANGE_FAILURE;
}
}