mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-18 07:47:17 +00:00
155 lines
6.3 KiB
XML
155 lines
6.3 KiB
XML
<chapter id="chapter-other-ntoone"
|
|
xreflabel="Writing a N-to-1 Element or Muxer">
|
|
<title>Writing a N-to-1 Element or Muxer</title>
|
|
<para>
|
|
N-to-1 elements have been previously mentioned and discussed in both
|
|
<xref linkend="chapter-advanced-request"/> and in
|
|
<xref linkend="chapter-scheduling"/>. The main noteworthy thing
|
|
about N-to-1 elements is that each pad is push-based in its own thread,
|
|
and the N-to-1 element synchronizes those streams by
|
|
expected-timestamp-based logic. This means it lets all streams wait
|
|
except for the one that provides the earliest next-expected timestamp.
|
|
When that stream has passed one buffer, the next
|
|
earliest-expected-timestamp is calculated, and we start back where we
|
|
were, until all streams have reached EOS. There is a helper base class,
|
|
called <classname>GstCollectPads</classname>, that will help you to do
|
|
this.
|
|
</para>
|
|
<para>
|
|
Note, however, that this helper class will only help you with grabbing
|
|
a buffer from each input and giving you the one with earliest timestamp.
|
|
If you need anything more difficult, such as "don't-grab-a-new-buffer
|
|
until a given timestamp" or something like that, you'll need to do this
|
|
yourself.
|
|
</para>
|
|
|
|
<!--
|
|
Note: I'd like to have something like this in the final text, but since
|
|
the code below doesn't work and this is all 0.8'y, I commented it for now.
|
|
<sect1 id="section-muxer-negotiation" xreflabel="Negotiation">
|
|
<title>Negotiation</title>
|
|
<para>
|
|
Most container formats will have a fair amount of issues with
|
|
changing content on an elementary stream. Therefore, you should
|
|
not allow caps to be changed once you've started using data from
|
|
them. The easiest way to achieve this is by using explicit caps,
|
|
which have been explained before. However, we're going to use them
|
|
in a slightly different way then what you're used to, having the
|
|
core do all the work for us.
|
|
</para>
|
|
<para>
|
|
The idea is that, as long as the stream/file headers have not been
|
|
written yet and no data has been processed yet, a stream is allowed
|
|
to renegotiate. After that point, the caps should be fixed, because
|
|
we can only use a stream once. Caps may then only change within an
|
|
allowed range (think MPEG, where changes in FPS are allowed), or
|
|
sometimes not at all (such as AVI audio). In order to do that, we
|
|
will, after data retrieval and header writing, set an explicit caps
|
|
on each sink pad, that is the minimal caps describing the properties
|
|
of the format that may not change. As an example, for MPEG audio
|
|
inside an MPEG system stream, this would mean a wide caps of
|
|
audio/mpeg with mpegversion=1 and layer=[1,2]. For the same audio type
|
|
in MPEG, though, the samplerate, bitrate, layer and number of channels
|
|
would become static, too. Since the (request) pads will be removed
|
|
when the stream ends, the static caps will cease to exist too, then.
|
|
While the explicit caps exist, the <function>_link ()</function>-
|
|
function will not be called, since the core will do all necessary
|
|
checks for us. Note that the property of using explicit caps should
|
|
be added along with the actual explicit caps, not any earlier.
|
|
</para>
|
|
<para>
|
|
Below here follows the simple example of an AVI muxer's audio caps
|
|
negotiation. The <function>_link ()</function>-function is fairly
|
|
normal, but the <function>-Loop ()</function>-function does some of
|
|
the tricks mentioned above. There is no <function>_getcaps ()</function>-
|
|
function since the pad template contains all that information already
|
|
(not shown).
|
|
</para>
|
|
<programlisting>
|
|
static GstPadLinkReturn
|
|
gst_avi_mux_audio_link (GstPad *pad,
|
|
const GstCaps *caps)
|
|
{
|
|
GstAviMux *mux = GST_AVI_MUX (gst_pad_get_parent (pad));
|
|
GstStructure *str = gst_caps_get_structure (caps, 0);
|
|
const gchar *media = gst_structure_get_name (str);
|
|
|
|
if (!strcmp (str, "audio/mpeg")) {
|
|
/* get version, make sure it's 1, get layer, make sure it's 1-3,
|
|
* then create the 2-byte audio tag (0x0055) and fill an audio
|
|
* stream structure (strh/strf). */
|
|
[..]
|
|
return GST_PAD_LINK_OK;
|
|
} else if !strcmp (str, "audio/x-raw")) {
|
|
/* See above, but now with the raw audio tag (0x0001). */
|
|
[..]
|
|
return GST_PAD_LINK_OK;
|
|
} else [..]
|
|
[..]
|
|
}
|
|
|
|
static void
|
|
gst_avi_mux_loop (GstElement *element)
|
|
{
|
|
GstAviMux *mux = GST_AVI_MUX (element);
|
|
[..]
|
|
/* As we get here, we should have written the header if we hadn't done
|
|
* that before yet, and we're supposed to have an internal buffer from
|
|
* each pad, also from the audio one. So here, we check again whether
|
|
* this is the first run and if so, we set static caps. */
|
|
if (mux->first_cycle) {
|
|
const GList *padlist = gst_element_get_pad_list (element);
|
|
GList *item;
|
|
|
|
for (item = padlist; item != NULL; item = item->next) {
|
|
GstPad *pad = item->data;
|
|
GstCaps *caps;
|
|
|
|
if (!GST_PAD_IS_SINK (pad))
|
|
continue;
|
|
|
|
/* set static caps here */
|
|
if (!strncmp (gst_pad_get_name (pad), "audio_", 6)) {
|
|
/* the strf is the struct you filled in the _link () function. */
|
|
switch (strf->format) {
|
|
case 0x0055: /* mp3 */
|
|
caps = gst_caps_new_simple ("audio/mpeg",
|
|
"mpegversion", G_TYPE_INT, 1,
|
|
"layer", G_TYPE_INT, 3,
|
|
"bitrate", G_TYPE_INT, strf->av_bps,
|
|
"rate", G_TYPE_INT, strf->rate,
|
|
"channels", G_TYPE_INT, strf->channels,
|
|
NULL);
|
|
break;
|
|
case 0x0001: /* pcm */
|
|
caps = gst_caps_new_simple ("audio/x-raw",
|
|
[..]);
|
|
break;
|
|
[..]
|
|
}
|
|
} else if (!strncmp (gst_pad_get_name (pad), "video_", 6)) {
|
|
[..]
|
|
} else {
|
|
g_warning ("oi!");
|
|
continue;
|
|
}
|
|
|
|
/* set static caps */
|
|
gst_pad_use_explicit_caps (pad);
|
|
gst_pad_set_explicit_caps (pad, caps);
|
|
}
|
|
}
|
|
[..]
|
|
/* Next runs will never be the first again. */
|
|
mux->first_cycle = FALSE;
|
|
}
|
|
</programlisting>
|
|
<para>
|
|
Note that there are other ways to achieve that, which might be useful
|
|
for more complex cases. This will do for the simple cases, though.
|
|
This method is provided to simplify negotiation and renegotiation in
|
|
muxers, it is not a complete solution, nor is it a pretty one.
|
|
</para>
|
|
</sect1>
|
|
-->
|
|
</chapter>
|