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  <wingo@pobox.com>

* 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.
This commit is contained in:
Andy Wingo 2007-01-10 21:15:08 +00:00
parent a18f048fb2
commit 7ef6acd8cd
4 changed files with 118 additions and 22 deletions

View file

@ -1,3 +1,13 @@
2007-01-10 Andy Wingo <wingo@pobox.com>
* 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 <tim at centricular dot net> 2007-01-10 Tim-Philipp Müller <tim at centricular dot net>
* plugins/elements/gstfilesink.c: * plugins/elements/gstfilesink.c:

View file

@ -1,10 +1,24 @@
Negotiation Negotiation
----------- -----------
Negotiation happens when elements want to push buffers and need to decide Capabilities negotiation is the process of deciding on an adequate
on the format. This is called downstream negotiation because the upstream format for dataflow within a GStreamer pipeline. Ideally, negotiation
element decides the format for the downstream element. This is the most (also known as "capsnego") transfers information from those parts of the
common case. 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 Negotiation can also happen when a downstream element wants to receive
another data format from an upstream element. This is called upstream another data format from an upstream element. This is called upstream
@ -152,4 +166,75 @@ videotestsrc ! queue ! xvimagesink
- queue contains buffers with different types. - 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.

View file

@ -2475,40 +2475,41 @@ gst_pad_get_peer (GstPad * pad)
/** /**
* gst_pad_get_allowed_caps: * 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 * Gets the capabilities of the allowed media types that can flow through
* @srcpad and its peer. The pad must be a source pad. * @pad and its peer.
* The caller must free the resulting caps.
* *
* Returns: the allowed #GstCaps of the pad link. Free the caps when * The allowed capabilities is calculated as the intersection of the results of
* you no longer need it. This function returns NULL when the @srcpad has no * calling gst_pad_get_caps() on @pad and its peer. The caller owns a reference
* peer. * 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. * MT safe.
*/ */
GstCaps * GstCaps *
gst_pad_get_allowed_caps (GstPad * srcpad) gst_pad_get_allowed_caps (GstPad * pad)
{ {
GstCaps *mycaps; GstCaps *mycaps;
GstCaps *caps; GstCaps *caps;
GstCaps *peercaps; GstCaps *peercaps;
GstPad *peer; GstPad *peer;
g_return_val_if_fail (GST_IS_PAD (srcpad), NULL); g_return_val_if_fail (GST_IS_PAD (pad), NULL);
g_return_val_if_fail (GST_PAD_IS_SRC (srcpad), NULL);
GST_OBJECT_LOCK (srcpad); GST_OBJECT_LOCK (pad);
peer = GST_PAD_PEER (srcpad); peer = GST_PAD_PEER (pad);
if (G_UNLIKELY (peer == NULL)) if (G_UNLIKELY (peer == NULL))
goto no_peer; 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_ref (peer);
GST_OBJECT_UNLOCK (srcpad); GST_OBJECT_UNLOCK (pad);
mycaps = gst_pad_get_caps (srcpad); mycaps = gst_pad_get_caps (pad);
peercaps = gst_pad_get_caps (peer); peercaps = gst_pad_get_caps (peer);
gst_object_unref (peer); gst_object_unref (peer);
@ -2517,15 +2518,15 @@ gst_pad_get_allowed_caps (GstPad * srcpad)
gst_caps_unref (peercaps); gst_caps_unref (peercaps);
gst_caps_unref (mycaps); 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); caps);
return caps; return caps;
no_peer: no_peer:
{ {
GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, srcpad, "no peer"); GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "no peer");
GST_OBJECT_UNLOCK (srcpad); GST_OBJECT_UNLOCK (pad);
return NULL; return NULL;
} }

View file

@ -825,7 +825,7 @@ GstCaps * gst_pad_peer_get_caps (GstPad * pad);
gboolean gst_pad_peer_accept_caps (GstPad * pad, GstCaps *caps); gboolean gst_pad_peer_accept_caps (GstPad * pad, GstCaps *caps);
/* capsnego for connected pads */ /* 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); GstCaps * gst_pad_get_negotiated_caps (GstPad * pad);
/* data passing functions to peer */ /* data passing functions to peer */