mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-30 12:10:37 +00:00
349 lines
11 KiB
Text
349 lines
11 KiB
Text
OUTDATED
|
|
--------
|
|
|
|
|
|
caps negotiation
|
|
================
|
|
|
|
1) purpose
|
|
----------
|
|
|
|
The pads expose the media types they can handle using a mime
|
|
type and a set of properties. Before the pad is created or
|
|
used to pass buffers, we only know the global 'range' of media
|
|
data this pad can accept. When the element has had a chance to
|
|
look at the media data, only then it knows the exact values of the
|
|
properties.
|
|
|
|
example1:
|
|
!
|
|
! The mp3 decoder exposes the capabilities of its src pad
|
|
! with the following caps:
|
|
!
|
|
! 'mpg123_src':
|
|
! MIME type: 'audio/raw':
|
|
! format: Integer: 16
|
|
! depth: Integer: 16
|
|
! rate: Integer range: 11025 - 48000
|
|
! channels: Integer range: 1 - 2
|
|
|
|
as you can see in example1, the padtemplate has both a range
|
|
(for the audio rate) and a list (for the number of channels)
|
|
for its properties.
|
|
|
|
only when the mpg123 element has decoded the first mpeg audio
|
|
header, it knows the exact values of the rate and channels
|
|
properties.
|
|
|
|
suppose that we want to connect this src pad to the sink pad
|
|
of an audiosink with the properties given in example2:
|
|
|
|
example2:
|
|
!
|
|
! 'audiosink_sink':
|
|
! MIME type: 'audio/raw':
|
|
! format: Integer: 16
|
|
! depth: List:
|
|
! Integer: 8
|
|
! Integer: 16
|
|
! rate: Integer range: 8000 - 44000
|
|
! channels: Integer range: 1 - 2
|
|
|
|
we can see that connecting the mpg123 src pad with the
|
|
audiosinks sink pad can cause a potential problem with the
|
|
rate property.
|
|
|
|
When the mpg123 decoder decides to output raw audio with a
|
|
48000Hz samplerate, the audiosink will not be able to handle
|
|
it. The conservative approach would be to disallow the connection
|
|
between the two incompatible pads. This rules out any potential
|
|
problems but severely limits the amount of possible connections
|
|
between the elements.
|
|
|
|
Another approach would be to allow the connection (and mark it
|
|
as dangerous) and let the two elements figure out a suitable
|
|
media type at runtime. This procedure is called caps negotiation.
|
|
|
|
|
|
2) a bit of history
|
|
-------------------
|
|
|
|
The typing of the data that was attached to a buffer used to be
|
|
done using GstMeta* (and it still is as of 11 feb 2001). With
|
|
the new GstCaps and GstProps system this typing is gradually moved
|
|
to the pads and to the padtemplates. This has several advantages:
|
|
|
|
- the typing of the data tends to be static. The type of media
|
|
doesn't change for every buffer.
|
|
|
|
- Moving the typing up to the pad(templates) allows us to save
|
|
them into the registry and allows us to figure out what pads
|
|
are compatible.
|
|
|
|
- the current metadata implementation needs header files. this may
|
|
change when we also use properties for metadata.
|
|
|
|
example3:
|
|
!
|
|
! This is the current GstMeta structure that travels with audio buffers
|
|
!
|
|
! struct _MetaAudioRaw {
|
|
! GstMeta meta;
|
|
!
|
|
! /* formatting information */
|
|
! gint format;
|
|
! gint channels;
|
|
! gint frequency;
|
|
! gint bps;
|
|
! };
|
|
|
|
|
|
The question still remains what purpose the metadata will serve
|
|
now that we expose the media type in the pads. Some possibilities:
|
|
|
|
- interesting information, not describing the data itself but the
|
|
context in which the data was generated (suggested buffer size,
|
|
timestamps, etc...)
|
|
|
|
- user app metadata.
|
|
|
|
In this proposal we also assume that the current use of metadata using
|
|
GstMeta is deprecated and that we move this information to the properties
|
|
of the pads.
|
|
|
|
|
|
3) the pad/padtemplates caps
|
|
----------------------------
|
|
|
|
All elements have to provide a padtemplate for their pads.
|
|
|
|
The padtemplates provide a range of possible media types this pad can
|
|
src/sink. the main purpose for the padtemplates is to allow a
|
|
rough guess at which pads are compatible before even a single buffer
|
|
has been processed by the element.
|
|
|
|
pads are usually created from the templates. When the pad is created
|
|
it has no GstCaps* attached to it yet. The possible caps this pad
|
|
can have is exposed in the padtemplate. The caps are filled in by
|
|
the element when it knows the values for the caps.
|
|
|
|
|
|
4) the connect function
|
|
-----------------------
|
|
|
|
when two pads are connected the following steps will take
|
|
place (not sure, FIXME):
|
|
|
|
- if both pads have caps, the caps are checked. If the caps
|
|
are incompatible, the padtemplates are checked, if they
|
|
are compatible, caps negotiation is performed.
|
|
|
|
- if one of the pads has caps, the caps is checked against
|
|
the padtemplate of the peer pad. If they are incompatible,
|
|
the padtemplates are compared, if they are incompatible,
|
|
caps negotiation is performed.
|
|
|
|
- if none of the pads have caps, the padtemplates are checked,
|
|
if they are incompatible, a warning is issued.
|
|
|
|
|
|
5) when the element knows the media type it is handling
|
|
-------------------------------------------------------
|
|
|
|
When the element has received its first buffer it will know
|
|
the media type it is handling by inspecting the buffer.
|
|
|
|
before pushing the data out to its peer element(s), the element
|
|
will set its src pad with the appropriate caps and properties.
|
|
These caps must follow the following rules:
|
|
|
|
- the caps must be compatible with the padtemplates of this
|
|
pad.
|
|
|
|
- the caps cannot contain ranges or lists.
|
|
|
|
when the element wants to change the caps of a pad, it has to
|
|
perform gst_pad_renegotiate (GstPad *pad). this will trigger
|
|
the caps negotiation procedure.
|
|
|
|
this will trigger the class method of the pad and calls the pads
|
|
gst_pad_negotiate function:
|
|
|
|
GstCaps *gst_pad_negotiate (GstPad *pad, GstCaps *caps, guint count);
|
|
|
|
This function takes a GstCaps *structure as an argument (typically the
|
|
current caps of the pad) and a negotiation counter. this counter can be
|
|
used to keep track of the negotiation process.
|
|
|
|
The pad then creates a new caps structure with the desired caps.
|
|
If the caps are accepted, it just returns the provided input caps. the
|
|
_renegotiate function will set the caps of both pads whenever the
|
|
input caps are the same (pointer wise) as the input caps.
|
|
|
|
the caps structure is checked against the padtemplate of the peer pad,
|
|
if it is incompatible the gst_pad_negotiate function is called again
|
|
and the element is supposed to create another caps structure.
|
|
|
|
the gst_pad_renegotiate function then calls the gst_pad_negotiate
|
|
function of the peer pad with the new caps as the argument. The peer
|
|
pad can adjust or create a new caps if it doesn't accept it.
|
|
|
|
the caps structure keeps on bouncing between the two pads until one
|
|
of the pads negotiation functions returns the caps unmodified.
|
|
|
|
The element can also return a NULL pointer if it has run out of
|
|
options for the caps structure. When this happens, both pads are set
|
|
the NULL caps again and the pad connnection is broken.
|
|
|
|
The negotiation process is stopped after a fixed number of tries,
|
|
when the counter has reached some limit. This limit is typically
|
|
checked by the pads negotiate function.
|
|
|
|
|
|
6) caps negotiation function
|
|
----------------------------
|
|
|
|
the negotiate function of a pad is called whenever the pad or
|
|
peer pad has performed _renegotiate.
|
|
|
|
example5:
|
|
!
|
|
! this is the caps negotiation function implemented by an element on
|
|
! one of its sink pads.
|
|
!
|
|
! static GstCaps*
|
|
! gst_pad_negotiate (GstPad *pad, GstCaps *caps, guint counter)
|
|
! {
|
|
! /* we don't accept anything else than audio/raw */
|
|
! if (strcmp (gst_caps_get_mime (caps), "audio/raw"))
|
|
! return NULL;
|
|
!
|
|
! if (gst_caps_get_int_prop (caps, "format") != AFMT_S16_LE)
|
|
! return NULL;
|
|
!
|
|
! /* we accept everything else */
|
|
! return caps;
|
|
! }
|
|
|
|
When the negotiate function returns NULL (it does not accept the
|
|
specified caps of the peer pad), the negotiation process is stopped.
|
|
|
|
|
|
|
|
APPENDIX A: use cases
|
|
=====================
|
|
|
|
1) mpg123 src!sink audiosink
|
|
----------------------------
|
|
|
|
When the pads are connected the padtemplates are checked and it
|
|
turns out that the pads might be incompatible (mpg123 can do
|
|
48000Hz while audiosink can do 44000Hz). Nothing happens at
|
|
connect time except for the user app that can mark this connection
|
|
as possibly dangerous and keep some spare elements ready for when
|
|
the pads turn out to be incompatible.
|
|
|
|
both elements start out with no caps at all (NULL). mpg123 wants
|
|
to output a buffer with specific properties. It calls
|
|
gst_pad_renegotiate (mpg123->srcpad).
|
|
|
|
The _renegotiate functions calls the negotiate function of the
|
|
mpg123->srcpad. the negotiate function would look like this:
|
|
|
|
|
|
/*
|
|
* The mpg123 element cannot convert the decoded type into something
|
|
* else so it has to force the caps of the src pad into the specific
|
|
* type as defined by the mp3.
|
|
*/
|
|
static GstCaps*
|
|
gst_mpeg123_src_negotiate (GstPad *pad, GstCaps *caps, guint counter)
|
|
{
|
|
GstMpg123 *mpg123;
|
|
|
|
mpg123 = GST_MPG123 (gst_pad_get_parent (pad));
|
|
|
|
/* we got caps in, check them */
|
|
if (caps != NULL) {
|
|
if (!strcmp (gst_caps_get_mime (caps), "audio/raw") &&
|
|
(gst_caps_get_int_prop (caps, "format") == AFMT_S16_LE) &&
|
|
(gst_caps_get_int_prop (caps, "depth") == 16) &&
|
|
(gst_caps_get_int_prop (caps, "rate") == mpg123->rate) &&
|
|
(gst_caps_get_int_prop (caps, "channels") == mpg123->channels)) {
|
|
return caps;
|
|
}
|
|
}
|
|
/* we didn't get caps, so we decide */
|
|
else if (counter != 2) {
|
|
GstCaps *new;
|
|
|
|
/* fill in our desired caps */
|
|
new = gst_caps_new_with_props (
|
|
"src_caps", /* name */
|
|
"audio/raw", /* mime */
|
|
gst_props_new (
|
|
"format", GST_PROPS_INT (AFMT_S16_LE),
|
|
"depth", GST_PROPS_INT (16),
|
|
"rate", GST_PROPS_INT (mpg123->rate),
|
|
"channels", GST_PROPS_INT (mpg123->channels),
|
|
NULL
|
|
)
|
|
);
|
|
return caps;
|
|
}
|
|
/* too many attempts at nogotiation, bail out */
|
|
return NULL;
|
|
}
|
|
|
|
|
|
The audiosink pad negotiate function would look like this:
|
|
|
|
/*
|
|
* The audiosink has a wide range of possible parameters for
|
|
* its sink pad, based on the audio card capabilities and
|
|
* possibly the element configuration.
|
|
* we assume the audiosink element can be both the initiator of
|
|
* the negotiations and the negotiated one.
|
|
*/
|
|
static GstCaps*
|
|
gst_audiosink_sink_negotiate (GstPad *pad, GstCaps *caps, guint counter)
|
|
{
|
|
GstAudiosink *audiosink;
|
|
gboolean accepted = TRUE;
|
|
|
|
audiosink = GST_AUDIOSINK (gst_pad_get_parent (pad));
|
|
|
|
/* we got caps in, we know they will match the padtemplate */
|
|
if (caps != NULL) {
|
|
return caps;
|
|
}
|
|
/* we didn't get caps, so we decide */
|
|
else if (counter != 2) {
|
|
GstCaps *new;
|
|
|
|
/* fill in our desired caps */
|
|
new = gst_caps_new_with_props (
|
|
"sink_caps", /* name */
|
|
"audio/raw", /* mime */
|
|
gst_props_new (
|
|
"format", GST_PROPS_INT (audiosink->format),
|
|
"depth", GST_PROPS_INT (audiosink->depth),
|
|
"rate", GST_PROPS_INT (audiosink->rate),
|
|
"channels", GST_PROPS_INT (audiosink->channels),
|
|
NULL
|
|
)
|
|
);
|
|
return caps;
|
|
}
|
|
/* too many attempts at nogotiation, bail out */
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|