diff --git a/ChangeLog b/ChangeLog index cf9f020f3c..2fd5dcd0ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +2005-07-18 Wim Taymans + + * docs/design/part-dynamic.txt: + * docs/design/part-events.txt: + * docs/design/part-seeking.txt: + Some more docs in the works. + + * gst/base/gstbasetransform.c: (gst_base_transform_transform_caps), + (gst_base_transform_getcaps), (gst_base_transform_configure_caps), + (gst_base_transform_setcaps), (gst_base_transform_get_size), + (gst_base_transform_buffer_alloc), (gst_base_transform_event), + (gst_base_transform_handle_buffer), + (gst_base_transform_sink_activate_push), + (gst_base_transform_src_activate_pull), + (gst_base_transform_set_passthrough), + (gst_base_transform_is_passthrough): + Refcounting fixes. + + * gst/gstbus.c: (gst_bus_source_dispatch), (gst_bus_poll): + Cleanups. + + * gst/gstevent.c: (gst_event_finalize): + Set SRC to NULL. + + * gst/gstutils.c: (gst_element_unlink), + (gst_pad_get_parent_element), (gst_pad_proxy_getcaps), + (gst_pad_proxy_setcaps): + * gst/gstutils.h: + Add _get_parent_element() to get a pads parent as an element. + 2005-07-18 Wim Taymans * check/gst/gstbin.c: (GST_START_TEST): diff --git a/docs/design/part-dynamic.txt b/docs/design/part-dynamic.txt new file mode 100644 index 0000000000..e605fde026 --- /dev/null +++ b/docs/design/part-dynamic.txt @@ -0,0 +1,6 @@ +Dynamic pipelines +----------------- + +This document describes many use cases for dynamically constructing and +manipulating a running or paused pipeline and the features provided by +GStreamer. diff --git a/docs/design/part-events.txt b/docs/design/part-events.txt index 5fa6d6490a..03f83b3f95 100644 --- a/docs/design/part-events.txt +++ b/docs/design/part-events.txt @@ -163,6 +163,9 @@ The general flow of executing the seek with FLUSH is as follows: 6) start stopped tasks and unlock the STREAM_LOCK, dataflow will continue now from the new position. +More information about the different seek types can be found in +part-seeking.txt. + SIZE ---- diff --git a/docs/design/part-seeking.txt b/docs/design/part-seeking.txt new file mode 100644 index 0000000000..b2c81c4aa3 --- /dev/null +++ b/docs/design/part-seeking.txt @@ -0,0 +1,70 @@ +Seeking +------- + +Seeking in GStreamer means configuring the pipeline for playback of the +media between a certain start and stop time. + +Different kinds of seeking exist: + + - immeditate seeking with low latency (FLUSH seek) + - seeking without flush, playback will start from the new + position after all the queues are emptied with old data. + - segment seeking with and without FLUSH, this can be used to + implement seamless looping or NLE functionality. + +Seeking can be performed in different formats such as time, frames +or samples. + +Seeking can be performed to an absolute position or relative to the +current playback position. + +For seeking to work reliably, all plugins in the pipeline need to follow +the well-defined rules in this document. + +Non segment seeking will make the pipeline emit EOS when the configured +playback range has been played. + +Segment seeking will not emit an EOS at the end of the range but will +post a SEGMENT_STOP message on the bus. This message is posted by the +earliest element in the pipeline, typically a demuxer. After receiving +the message, the application can reconnect the pipeline or issue other +seek events in the pipeline. + + +Generating seeking events +------------------------- + + + + + +The different kinds of seeking methods and their internal workings are +described below. + + +FLUSH seeking +------------- + +This is the most common way of performing a seek in a playback application. +The application issues a seek on the pipeline and the new media is immediatly +played after the seek calls returns. + + +seeking without FLUSH +--------------------- + +This seek type is typically performed after issuing segment seeks to finish +the playback of the pipeline. + + +segment seeking with FLUSH +-------------------------- + +This seek is typically performed when starting seamless looping. + + +segment seeking without FLUSH +----------------------------- + +This seek is typically performed when continuing seamless looping. + diff --git a/gst/base/gstbasetransform.c b/gst/base/gstbasetransform.c index 375e316d4b..4384ab49f6 100644 --- a/gst/base/gstbasetransform.c +++ b/gst/base/gstbasetransform.c @@ -231,7 +231,7 @@ gst_base_transform_getcaps (GstPad * pad) GstPad *otherpad; GstCaps *caps; - trans = GST_BASE_TRANSFORM (GST_PAD_PARENT (pad)); + trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad; @@ -272,6 +272,8 @@ gst_base_transform_getcaps (GstPad * pad) done: GST_DEBUG ("returning %" GST_PTR_FORMAT, caps); + gst_object_unref (trans); + return caps; } @@ -306,7 +308,7 @@ gst_base_transform_setcaps (GstPad * pad, GstCaps * caps) GstCaps *othercaps = NULL; gboolean ret = TRUE; - trans = GST_BASE_TRANSFORM (GST_PAD_PARENT (pad)); + trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); klass = GST_BASE_TRANSFORM_GET_CLASS (trans); otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad; @@ -408,6 +410,8 @@ done: if (othercaps) gst_caps_unref (othercaps); + gst_object_unref (trans); + return ret; /* ERRORS */ @@ -479,7 +483,7 @@ gst_base_transform_event (GstPad * pad, GstEvent * event) gboolean ret = FALSE; gboolean unlock; - trans = GST_BASE_TRANSFORM (GST_PAD_PARENT (pad)); + trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); if (bclass->event) @@ -505,6 +509,8 @@ gst_base_transform_event (GstPad * pad, GstEvent * event) if (unlock) GST_STREAM_UNLOCK (pad); + gst_object_unref (trans); + return ret; } @@ -670,13 +676,14 @@ gst_base_transform_sink_activate_push (GstPad * pad, gboolean active) GstBaseTransform *trans; GstBaseTransformClass *bclass; - trans = GST_BASE_TRANSFORM (GST_OBJECT_PARENT (pad)); + trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); if (active) { if (bclass->start) result = bclass->start (trans); } + gst_object_unref (trans); return result; } @@ -688,7 +695,7 @@ gst_base_transform_src_activate_pull (GstPad * pad, gboolean active) GstBaseTransform *trans; GstBaseTransformClass *bclass; - trans = GST_BASE_TRANSFORM (GST_OBJECT_PARENT (pad)); + trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); result = gst_pad_activate_pull (trans->sinkpad, active); @@ -697,6 +704,7 @@ gst_base_transform_src_activate_pull (GstPad * pad, gboolean active) if (result && bclass->start) result &= bclass->start (trans); } + gst_object_unref (trans); return result; } diff --git a/gst/gstbus.c b/gst/gstbus.c index 0509a12bfe..1b4dbeee97 100644 --- a/gst/gstbus.c +++ b/gst/gstbus.c @@ -420,28 +420,30 @@ gst_bus_source_dispatch (GSource * source, GSourceFunc callback, GstBusSource *bsource = (GstBusSource *) source; GstMessage *message; gboolean needs_pop = TRUE; + GstBus *bus; - g_return_val_if_fail (GST_IS_BUS (bsource->bus), FALSE); + g_return_val_if_fail (bsource != NULL, FALSE); - message = gst_bus_peek (bsource->bus); + bus = bsource->bus; + + g_return_val_if_fail (GST_IS_BUS (bus), FALSE); + + message = gst_bus_peek (bus); GST_DEBUG ("have message %p", message); g_return_val_if_fail (message != NULL, TRUE); - if (!handler) { - g_warning ("GstBus watch dispatched without callback\n" - "You must call g_source_connect()."); - return FALSE; - } + if (!handler) + goto no_handler; GST_DEBUG ("calling dispatch with %p", message); - needs_pop = handler (bsource->bus, message, user_data); + needs_pop = handler (bus, message, user_data); GST_DEBUG ("handler returns %d", needs_pop); if (needs_pop) { - message = gst_bus_pop (bsource->bus); + message = gst_bus_pop (bus); if (message) { gst_message_unref (message); } else { @@ -452,8 +454,14 @@ gst_bus_source_dispatch (GSource * source, GSourceFunc callback, GST_DEBUG ("handler requested pop but no message on the bus"); } } - return TRUE; + +no_handler: + { + g_warning ("GstBus watch dispatched without callback\n" + "You must call g_source_connect()."); + return FALSE; + } } static void @@ -596,6 +604,8 @@ poll_timeout (GstBusPollData * poll_data) * specify a maximum time to poll with the @timeout parameter. If @timeout is * negative, this function will block indefinitely. * + * This function will enter the default mainloop while polling. + * * Returns: The type of the message that was received, or GST_MESSAGE_UNKNOWN if * the poll timed out. The message will remain in the bus queue; you will need * to gst_bus_pop() it off before entering gst_bus_poll() again. @@ -603,28 +613,29 @@ poll_timeout (GstBusPollData * poll_data) GstMessageType gst_bus_poll (GstBus * bus, GstMessageType events, GstClockTimeDiff timeout) { - GstBusPollData *poll_data; + GstBusPollData poll_data; GstMessageType ret; guint id; - poll_data = g_new0 (GstBusPollData, 1); if (timeout >= 0) - poll_data->timeout_id = g_timeout_add (timeout / GST_MSECOND, - (GSourceFunc) poll_timeout, poll_data); - poll_data->loop = g_main_loop_new (NULL, FALSE); - poll_data->events = events; - poll_data->revent = GST_MESSAGE_UNKNOWN; + poll_data.timeout_id = g_timeout_add (timeout / GST_MSECOND, + (GSourceFunc) poll_timeout, &poll_data); + else + poll_data.timeout_id = 0; - id = gst_bus_add_watch (bus, (GstBusHandler) poll_handler, poll_data); - g_main_loop_run (poll_data->loop); + poll_data.loop = g_main_loop_new (NULL, FALSE); + poll_data.events = events; + poll_data.revent = GST_MESSAGE_UNKNOWN; + + id = gst_bus_add_watch (bus, (GstBusHandler) poll_handler, &poll_data); + g_main_loop_run (poll_data.loop); g_source_remove (id); - ret = poll_data->revent; + ret = poll_data.revent; - if (poll_data->timeout_id) - g_source_remove (poll_data->timeout_id); - g_main_loop_unref (poll_data->loop); - g_free (poll_data); + if (poll_data.timeout_id) + g_source_remove (poll_data.timeout_id); + g_main_loop_unref (poll_data.loop); return ret; } diff --git a/gst/gstevent.c b/gst/gstevent.c index 2020593188..8aec18858c 100644 --- a/gst/gstevent.c +++ b/gst/gstevent.c @@ -99,6 +99,7 @@ gst_event_finalize (GstEvent * event) if (GST_EVENT_SRC (event)) { gst_object_unref (GST_EVENT_SRC (event)); + GST_EVENT_SRC (event) = NULL; } switch (GST_EVENT_TYPE (event)) { case GST_EVENT_TAG: diff --git a/gst/gstutils.c b/gst/gstutils.c index 15023f52f4..2ef1b2a755 100644 --- a/gst/gstutils.c +++ b/gst/gstutils.c @@ -1443,7 +1443,9 @@ gst_element_unlink (GstElement * src, GstElement * dest) /* see if the pad is connected and is really a pad * of dest */ if (peerpad) { - GstElement *peerelem = gst_pad_get_parent (peerpad); + GstElement *peerelem; + + peerelem = gst_pad_get_parent_element (peerpad); if (peerelem == dest) { gst_pad_unlink (pad, peerpad); @@ -1641,6 +1643,34 @@ done: return result; } +/** + * gst_pad_get_parent_element: + * @pad: a pad + * + * Gets the parent of @pad, cast to a #GstElement. If a @pad has no parent or + * its parent is not an element, return NULL. + * + * Returns: The parent of the pad. The caller has a reference on the parent, so + * unref when you're finished with it. + * + * MT safe. + */ +GstElement * +gst_pad_get_parent_element (GstPad * pad) +{ + GstObject *p; + + g_return_val_if_fail (GST_IS_PAD (pad), NULL); + + p = gst_object_get_parent (GST_OBJECT_CAST (pad)); + + if (p && !GST_IS_ELEMENT (p)) { + gst_object_unref (p); + p = NULL; + } + return GST_ELEMENT_CAST (p); +} + /** * gst_object_default_error: * @object: a #GObject that signalled the error. @@ -1955,7 +1985,7 @@ gst_pad_proxy_getcaps (GstPad * pad) GST_DEBUG ("proxying getcaps for %s:%s", GST_DEBUG_PAD_NAME (pad)); - element = gst_pad_get_parent (pad); + element = gst_pad_get_parent_element (pad); if (element == NULL) return NULL; @@ -2027,7 +2057,7 @@ gst_pad_proxy_setcaps (GstPad * pad, GstCaps * caps) GST_DEBUG ("proxying pad link for %s:%s", GST_DEBUG_PAD_NAME (pad)); - element = gst_pad_get_parent (pad); + element = gst_pad_get_parent_element (pad); iter = gst_element_iterate_pads (element); diff --git a/gst/gstutils.h b/gst/gstutils.h index a5f78a37f9..048753c75c 100644 --- a/gst/gstutils.h +++ b/gst/gstutils.h @@ -278,6 +278,8 @@ GstCaps* gst_pad_get_fixed_caps_func (GstPad *pad); GstCaps* gst_pad_proxy_getcaps (GstPad * pad); gboolean gst_pad_proxy_setcaps (GstPad * pad, GstCaps * caps); +GstElement* gst_pad_get_parent_element (GstPad *pad); + /* util query functions */ gboolean gst_pad_query_position (GstPad *pad, GstFormat *format, gint64 *cur, gint64 *end); diff --git a/libs/gst/base/gstbasetransform.c b/libs/gst/base/gstbasetransform.c index 375e316d4b..4384ab49f6 100644 --- a/libs/gst/base/gstbasetransform.c +++ b/libs/gst/base/gstbasetransform.c @@ -231,7 +231,7 @@ gst_base_transform_getcaps (GstPad * pad) GstPad *otherpad; GstCaps *caps; - trans = GST_BASE_TRANSFORM (GST_PAD_PARENT (pad)); + trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad; @@ -272,6 +272,8 @@ gst_base_transform_getcaps (GstPad * pad) done: GST_DEBUG ("returning %" GST_PTR_FORMAT, caps); + gst_object_unref (trans); + return caps; } @@ -306,7 +308,7 @@ gst_base_transform_setcaps (GstPad * pad, GstCaps * caps) GstCaps *othercaps = NULL; gboolean ret = TRUE; - trans = GST_BASE_TRANSFORM (GST_PAD_PARENT (pad)); + trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); klass = GST_BASE_TRANSFORM_GET_CLASS (trans); otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad; @@ -408,6 +410,8 @@ done: if (othercaps) gst_caps_unref (othercaps); + gst_object_unref (trans); + return ret; /* ERRORS */ @@ -479,7 +483,7 @@ gst_base_transform_event (GstPad * pad, GstEvent * event) gboolean ret = FALSE; gboolean unlock; - trans = GST_BASE_TRANSFORM (GST_PAD_PARENT (pad)); + trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); if (bclass->event) @@ -505,6 +509,8 @@ gst_base_transform_event (GstPad * pad, GstEvent * event) if (unlock) GST_STREAM_UNLOCK (pad); + gst_object_unref (trans); + return ret; } @@ -670,13 +676,14 @@ gst_base_transform_sink_activate_push (GstPad * pad, gboolean active) GstBaseTransform *trans; GstBaseTransformClass *bclass; - trans = GST_BASE_TRANSFORM (GST_OBJECT_PARENT (pad)); + trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); if (active) { if (bclass->start) result = bclass->start (trans); } + gst_object_unref (trans); return result; } @@ -688,7 +695,7 @@ gst_base_transform_src_activate_pull (GstPad * pad, gboolean active) GstBaseTransform *trans; GstBaseTransformClass *bclass; - trans = GST_BASE_TRANSFORM (GST_OBJECT_PARENT (pad)); + trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); result = gst_pad_activate_pull (trans->sinkpad, active); @@ -697,6 +704,7 @@ gst_base_transform_src_activate_pull (GstPad * pad, gboolean active) if (result && bclass->start) result &= bclass->start (trans); } + gst_object_unref (trans); return result; }