Writing a 1-to-N Element, Demuxer or Parser 1-to-N elements don't have much special needs or requirements that haven't been discussed already. The most important thing to take care of in 1-to-N elements (things like tee-elements or so) is to use proper buffer refcounting and caps negotiation. If those two are taken care of (see the tee element if you need example code), there's little that can go wrong. Demuxers are the 1-to-N elements that need very special care, though. They are responsible for timestamping raw, unparsed data into elementary video or audio streams, and there are many things that you can optimize or do wrong. Here, several culprits will be mentioned and common solutions will be offered. Parsers are demuxers with only one source pad. Also, they only cut the stream into buffers, they don't touch the data otherwise. Demuxer Caps Negotiation Demuxers will usually contain several elementary streams, and each of those streams' properties will be defined in a stream header at the start of the file (or, rather, stream) that you're parsing. Since those are fixed and there is no possibility to negotiate stream properties with elements earlier in the pipeline, you should always use explicit caps on demuxer source pads. This prevents a whole lot of caps negotiation or re-negotiation errors. Data processing and downstream events Data parsing, pulling this into subbuffers and sending that to the source pads of the elementary streams is the one single most important task of demuxers and parsers. Usually, an element will have a _loop () function using the bytestream object to read data. Try to have a single point of data reading from the bytestream object. In this single point, do proper event handling (in case there is any) and proper error handling in case that's needed. Make your element as fault-tolerant as possible, but do not go further than possible. Parsing versus interpreting One particular convention that &GStreamer; demuxers follow is that of separation of parsing and interpreting. The reason for this is maintainability, clarity and code reuse. An easy example of this is something like RIFF, which has a chunk header of 4 bytes, then a length indicator of 4 bytes and then the actual data. We write special functions to read one chunk, to peek a chunk ID and all those; that's the parsing part of the demuxer. Then, somewhere else, we like to write the main data processing function, which calls this parse function, reads one chunk and then does with the data whatever it needs to do. Some example code for RIFF-reading to illustrate the above two points: static gboolean gst_my_demuxer_peek (GstMyDemuxer *demux, guint32 *id, guint32 *size) { guint8 *data; while (gst_bytestream_peek_bytes (demux->bs, &data, 4) != 4) { guint32 remaining; GstEvent *event; gst_bytestream_get_status (demux->bs, &remaining, &event); if (event) { GstEventType type = GST_EVENT_TYPE (event); /* or maybe custom event handling, up to you - we lose reference! */ gst_pad_event_default (demux->sinkpad, event); if (type == GST_EVENT_EOS) return FALSE; } else { GST_ELEMENT_ERROR (demux, STREAM, READ, (NULL), (NULL)); return FALSE; } } *id = GUINT32_FROM_LE (((guint32 *) data)[0]); *size = GUINT32_FROM_LE (((guint32 *) data)[0]); return TRUE; } static void gst_my_demuxer_loop (GstElement *element) { GstMyDemuxer *demux = GST_MY_DEMUXER (element); guint32 id, size; if (!gst_my_demuxer_peek (demux, &id, &size)) return; switch (id) { [.. normal chunk handling ..] } } Reason for this is that event handling is now centralized in one place and the _loop () function is a lot cleaner and more readable. Those are common code practices, but since the mistake of not using such common code practices has been made too often, we explicitely mention this here. Simple seeking and indexes Sources will generally receive a seek event in the exact supported format by the element. Demuxers, however, can not seek in themselves directly, but need to convert from one unit (e.g. time) to the other (e.g. bytes) and send a new event to its sink pad. Given this, the _convert ()-function (or, more general: unit conversion) is the most important function in a demuxer. Some demuxers (AVI, Matroska) and parsers will keep an index of all chunks in a stream, firstly to improve seeking precision and secondly so they won't lose sync. Some other demuxers will seek the stream directly without index (e.g. MPEG, Ogg) - usually based on something like a cumulative bitrate - and then find the closest next chunk from their new position. The best choice depends on the format. Note that it is recommended for demuxers to implement event, conversion and query handling functions (using time units or so), in addition to the ones (usually in byte units) provided by the pipeline source element.