From 7ef6acd8cde789697dbab097c48101528f182f31 Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Wed, 10 Jan 2007 21:15:08 +0000 Subject: [PATCH] docs/design/part-negotiation.txt: Update with, um, one way that pull-mode negotiation might work? Original commit message from CVS: 2007-01-10 Andy Wingo * docs/design/part-negotiation.txt: Update with, um, one way that pull-mode negotiation might work? * gst/gstpad.h: * gst/gstpad.c (gst_pad_get_allowed_caps): Remove the restriction that the pad must be a src pad; makes sense to call it the other way in pull mode, and the logic is symmetric anyway. --- ChangeLog | 10 ++++ docs/design/part-negotiation.txt | 93 ++++++++++++++++++++++++++++++-- gst/gstpad.c | 35 ++++++------ gst/gstpad.h | 2 +- 4 files changed, 118 insertions(+), 22 deletions(-) diff --git a/ChangeLog b/ChangeLog index a5e4aafedc..425a465a81 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2007-01-10 Andy Wingo + + * docs/design/part-negotiation.txt: Update with, um, one way that + pull-mode negotiation might work? + + * gst/gstpad.h: + * gst/gstpad.c (gst_pad_get_allowed_caps): Remove the restriction + that the pad must be a src pad; makes sense to call it the other + way in pull mode, and the logic is symmetric anyway. + 2007-01-10 Tim-Philipp Müller * plugins/elements/gstfilesink.c: diff --git a/docs/design/part-negotiation.txt b/docs/design/part-negotiation.txt index 048e5854bf..743892b334 100644 --- a/docs/design/part-negotiation.txt +++ b/docs/design/part-negotiation.txt @@ -1,10 +1,24 @@ Negotiation ----------- -Negotiation happens when elements want to push buffers and need to decide -on the format. This is called downstream negotiation because the upstream -element decides the format for the downstream element. This is the most -common case. +Capabilities negotiation is the process of deciding on an adequate +format for dataflow within a GStreamer pipeline. Ideally, negotiation +(also known as "capsnego") transfers information from those parts of the +pipeline that have information to those parts of the pipeline that are +flexible, constrained by those parts of the pipeline that are not +flexible. + +GStreamer's two scheduling modes, push mode and pull mode, lend +themselves to different mechanisms to acheive this goal. As it is more +common we describe push mode negotiation first. + +Push-mode negotiation +--------------------- + +Pussh-mode negotiation happens when elements want to push buffers and +need to decide on the format. This is called downstream negotiation +because the upstream element decides the format for the downstream +element. This is the most common case. Negotiation can also happen when a downstream element wants to receive another data format from an upstream element. This is called upstream @@ -152,4 +166,75 @@ videotestsrc ! queue ! xvimagesink - queue contains buffers with different types. +Pull-mode negotiation +--------------------- +A pipeline in pull mode has different negotiation needs than one +activated in push mode. Push mode is optimized for two use cases: + + * Playback of media files, in which the demuxers and the decoders are + the points from which format information should disseminate to the + rest of the pipeline; and + + * Recording from live sources, in which users are accustomed to putting + a capsfilter directly after the source element; thus the caps + information flow proceeds from the user, through the potential caps + of the source, to the sinks of the pipeline. + +In contrast, pull mode has other typical use cases: + + * Playback from a lossy source, such as RTP, in which more knowledge + about the latency of the pipeline can increase quality; or + + * Audio synthesis, in which audio APIs are tuned to producing only the + necessary number of samples, typically driven by a hardware interrupt + to fill a DMA buffer or a Jack[0] port buffer. + + * Low-latency effects processing, whereby filters should be applied as + data is transferred from a ring buffer to a sink instead of + beforehand. For example, instead of using the internal alsasink + ringbuffer thread in push-mode wavsrc ! volume ! alsasink, placing + the volume inside the sound card writer thread via wavsrc ! + audioringbuffer ! volume ! alsasink. + +[0] http://jackit.sf.net + +The problem with push mode is that the sink has to know the format in +order to know how many bytes to pull via gst_pad_pull_range(). This +means that before pulling, the sink must initiate negotation to decide +on a format. + +Recalling the principles of capsnego, whereby information must flow from +those that have it to those that do not, we see that the two named use +cases have different negotiation requirements: + + * RTP and low-latency playback are both like the normal playback case, + in which information flows downstream. + + * In audio synthesis, the part of the pipeline that has the most + information is the sink, constrained by the capabilities of the graph + that feeds it. However the caps are not completely specified; at some + point the user has to intervene to choose the sample rate, at least. + This can be done externally to gstreamer, as in the jack elements, or + internally via a capsnego, as is customary with live sources. + +Given that sinks potentially need the input of sources, as in the RTP +case and at least as a filter in the synthesis case, there must be a +negotiation phase before the pull thread is activated. + +[ok at this point i'm a bit at a loss about how it should go] + +This negotiation phase is initiated by the sink, after it succeeds in +calling gst_pad_activate_pull(), but before it spawns a thread to start +pulling. The sink will call gst_pad_get_allowed_caps() on the its sink +pad and fixate if necessary to determine the flow caps. It then calls +gst_pad_set_caps on its sink pad's peer, to configure the upstream +elements. + +A typical element will implement a setcaps() function on its src pads +that proxies the setcaps() to all peers of its sink pads. This way, when +getrange() is called on a pad, it knows what format it is being asked to +produce. + +If the sink element could not set caps on its peer(s), it should post an +error message on the bus indicating that negotiation was not possible. diff --git a/gst/gstpad.c b/gst/gstpad.c index 076321bd97..95c5caf216 100644 --- a/gst/gstpad.c +++ b/gst/gstpad.c @@ -2475,40 +2475,41 @@ gst_pad_get_peer (GstPad * pad) /** * gst_pad_get_allowed_caps: - * @srcpad: a #GstPad, it must a a source pad. + * @pad: a #GstPad. * * Gets the capabilities of the allowed media types that can flow through - * @srcpad and its peer. The pad must be a source pad. - * The caller must free the resulting caps. + * @pad and its peer. * - * Returns: the allowed #GstCaps of the pad link. Free the caps when - * you no longer need it. This function returns NULL when the @srcpad has no - * peer. + * The allowed capabilities is calculated as the intersection of the results of + * calling gst_pad_get_caps() on @pad and its peer. The caller owns a reference + * on the resulting caps. + * + * Returns: the allowed #GstCaps of the pad link. Unref the caps when you no + * longer need it. This function returns NULL when @pad has no peer. * * MT safe. */ GstCaps * -gst_pad_get_allowed_caps (GstPad * srcpad) +gst_pad_get_allowed_caps (GstPad * pad) { GstCaps *mycaps; GstCaps *caps; GstCaps *peercaps; GstPad *peer; - g_return_val_if_fail (GST_IS_PAD (srcpad), NULL); - g_return_val_if_fail (GST_PAD_IS_SRC (srcpad), NULL); + g_return_val_if_fail (GST_IS_PAD (pad), NULL); - GST_OBJECT_LOCK (srcpad); + GST_OBJECT_LOCK (pad); - peer = GST_PAD_PEER (srcpad); + peer = GST_PAD_PEER (pad); if (G_UNLIKELY (peer == NULL)) goto no_peer; - GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, srcpad, "getting allowed caps"); + GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "getting allowed caps"); gst_object_ref (peer); - GST_OBJECT_UNLOCK (srcpad); - mycaps = gst_pad_get_caps (srcpad); + GST_OBJECT_UNLOCK (pad); + mycaps = gst_pad_get_caps (pad); peercaps = gst_pad_get_caps (peer); gst_object_unref (peer); @@ -2517,15 +2518,15 @@ gst_pad_get_allowed_caps (GstPad * srcpad) gst_caps_unref (peercaps); gst_caps_unref (mycaps); - GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, srcpad, "allowed caps %" GST_PTR_FORMAT, + GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "allowed caps %" GST_PTR_FORMAT, caps); return caps; no_peer: { - GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, srcpad, "no peer"); - GST_OBJECT_UNLOCK (srcpad); + GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "no peer"); + GST_OBJECT_UNLOCK (pad); return NULL; } diff --git a/gst/gstpad.h b/gst/gstpad.h index 87c866d2f0..f9091a1d32 100644 --- a/gst/gstpad.h +++ b/gst/gstpad.h @@ -825,7 +825,7 @@ GstCaps * gst_pad_peer_get_caps (GstPad * pad); gboolean gst_pad_peer_accept_caps (GstPad * pad, GstCaps *caps); /* capsnego for connected pads */ -GstCaps * gst_pad_get_allowed_caps (GstPad * srcpad); +GstCaps * gst_pad_get_allowed_caps (GstPad * pad); GstCaps * gst_pad_get_negotiated_caps (GstPad * pad); /* data passing functions to peer */