2 * Copyright (C) 2006 Sjoerd Simons <sjoerd@luon.net>
3 * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
5 * gstmultipartdemux.c: multipart stream demuxer
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
24 * SECTION:element-multipartdemux
25 * @see_also: #GstMultipartMux
27 * MultipartDemux uses the Content-type field of incoming buffers to demux and
28 * push data to dynamic source pads. Most of the time multipart streams are
29 * sequential JPEG frames generated from a live source such as a network source
32 * The output buffers of the multipartdemux typically have no timestamps and are
33 * usually played as fast as possible (at the rate that the source provides the
36 * the content in multipart files is separated with a boundary string that can
37 * be configured specifically with the #GstMultipartDemux:boundary property
38 * otherwise it will be autodetected.
41 * <title>Sample pipelines</title>
43 * gst-launch-1.0 filesrc location=/tmp/test.multipart ! multipartdemux ! image/jpeg,framerate=\(fraction\)5/1 ! jpegparse ! jpegdec ! videoconvert ! autovideosink
44 * ]| a simple pipeline to demux a multipart file muxed with #GstMultipartMux
45 * containing JPEG frames.
53 #include "multipartdemux.h"
55 GST_DEBUG_CATEGORY_STATIC (gst_multipart_demux_debug);
56 #define GST_CAT_DEFAULT gst_multipart_demux_debug
58 #define DEFAULT_BOUNDARY NULL
59 #define DEFAULT_SINGLE_STREAM FALSE
68 static GstStaticPadTemplate multipart_demux_src_template_factory =
69 GST_STATIC_PAD_TEMPLATE ("src_%u",
74 static GstStaticPadTemplate multipart_demux_sink_template_factory =
75 GST_STATIC_PAD_TEMPLATE ("sink",
78 GST_STATIC_CAPS ("multipart/x-mixed-replace")
87 /* convert from mime types to gst structure names. Add more when needed. The
88 * mime-type is stored as lowercase */
89 static const GstNamesMap gstnames[] = {
90 /* RFC 2046 says audio/basic is mulaw, mono, 8000Hz */
91 {"audio/basic", "audio/x-mulaw, channels=1, rate=8000"},
93 "audio/x-adpcm, bitrate=16000, layout=g726, channels=1, rate=8000"},
95 "audio/x-adpcm, bitrate=24000, layout=g726, channels=1, rate=8000"},
97 "audio/x-adpcm, bitrate=32000, layout=g726, channels=1, rate=8000"},
99 "audio/x-adpcm, bitrate=40000, layout=g726, channels=1, rate=8000"},
100 /* Panasonic Network Cameras non-standard types */
102 "audio/x-adpcm, bitrate=32000, layout=g726, channels=1, rate=8000"},
107 static GstFlowReturn gst_multipart_demux_chain (GstPad * pad,
108 GstObject * parent, GstBuffer * buf);
110 static GstStateChangeReturn gst_multipart_demux_change_state (GstElement *
111 element, GstStateChange transition);
113 static void gst_multipart_set_property (GObject * object, guint prop_id,
114 const GValue * value, GParamSpec * pspec);
116 static void gst_multipart_get_property (GObject * object, guint prop_id,
117 GValue * value, GParamSpec * pspec);
119 static void gst_multipart_demux_dispose (GObject * object);
121 #define gst_multipart_demux_parent_class parent_class
122 G_DEFINE_TYPE (GstMultipartDemux, gst_multipart_demux, GST_TYPE_ELEMENT);
125 gst_multipart_demux_class_init (GstMultipartDemuxClass * klass)
129 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
130 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
132 gobject_class->dispose = gst_multipart_demux_dispose;
133 gobject_class->set_property = gst_multipart_set_property;
134 gobject_class->get_property = gst_multipart_get_property;
136 g_object_class_install_property (gobject_class, PROP_BOUNDARY,
137 g_param_spec_string ("boundary", "Boundary",
138 "The boundary string separating data, automatic if NULL",
140 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
143 * GstMultipartDemux:single-stream:
145 * Assume that there is only one stream whose content-type will
146 * not change and emit no-more-pads as soon as the first boundary
147 * content is parsed, decoded, and pads are linked.
149 g_object_class_install_property (gobject_class, PROP_SINGLE_STREAM,
150 g_param_spec_boolean ("single-stream", "Single Stream",
151 "Assume that there is only one stream whose content-type will not change and emit no-more-pads as soon as the first boundary content is parsed, decoded, and pads are linked",
152 DEFAULT_SINGLE_STREAM, G_PARAM_READWRITE));
154 /* populate gst names and mime types pairs */
155 klass->gstnames = g_hash_table_new (g_str_hash, g_str_equal);
156 for (i = 0; gstnames[i].key; i++) {
157 g_hash_table_insert (klass->gstnames, (gpointer) gstnames[i].key,
158 (gpointer) gstnames[i].val);
161 gstelement_class->change_state = gst_multipart_demux_change_state;
163 gst_element_class_add_pad_template (gstelement_class,
164 gst_static_pad_template_get (&multipart_demux_sink_template_factory));
165 gst_element_class_add_pad_template (gstelement_class,
166 gst_static_pad_template_get (&multipart_demux_src_template_factory));
167 gst_element_class_set_static_metadata (gstelement_class, "Multipart demuxer",
169 "demux multipart streams",
170 "Wim Taymans <wim.taymans@gmail.com>, Sjoerd Simons <sjoerd@luon.net>");
174 gst_multipart_demux_init (GstMultipartDemux * multipart)
176 /* create the sink pad */
178 gst_pad_new_from_static_template (&multipart_demux_sink_template_factory,
180 gst_element_add_pad (GST_ELEMENT_CAST (multipart), multipart->sinkpad);
181 gst_pad_set_chain_function (multipart->sinkpad,
182 GST_DEBUG_FUNCPTR (gst_multipart_demux_chain));
184 multipart->adapter = gst_adapter_new ();
185 multipart->boundary = DEFAULT_BOUNDARY;
186 multipart->mime_type = NULL;
187 multipart->content_length = -1;
188 multipart->header_completed = FALSE;
189 multipart->scanpos = 0;
190 multipart->singleStream = DEFAULT_SINGLE_STREAM;
191 multipart->have_group_id = FALSE;
192 multipart->group_id = G_MAXUINT;
196 gst_multipart_demux_remove_src_pads (GstMultipartDemux * demux)
198 while (demux->srcpads != NULL) {
199 GstMultipartPad *mppad = demux->srcpads->data;
201 gst_element_remove_pad (GST_ELEMENT (demux), mppad->pad);
202 g_free (mppad->mime);
204 demux->srcpads = g_slist_delete_link (demux->srcpads, demux->srcpads);
206 demux->srcpads = NULL;
211 gst_multipart_demux_dispose (GObject * object)
213 GstMultipartDemux *demux = GST_MULTIPART_DEMUX (object);
215 if (demux->adapter != NULL)
216 g_object_unref (demux->adapter);
217 demux->adapter = NULL;
218 g_free (demux->boundary);
219 demux->boundary = NULL;
220 g_free (demux->mime_type);
221 demux->mime_type = NULL;
222 gst_multipart_demux_remove_src_pads (demux);
224 G_OBJECT_CLASS (parent_class)->dispose (object);
228 gst_multipart_demux_get_gstname (GstMultipartDemux * demux, gchar * mimetype)
230 GstMultipartDemuxClass *klass;
231 const gchar *gstname;
233 klass = GST_MULTIPART_DEMUX_GET_CLASS (demux);
235 /* use hashtable to convert to gst name */
236 gstname = g_hash_table_lookup (klass->gstnames, mimetype);
237 if (gstname == NULL) {
238 /* no gst name mapping, use mime type */
241 GST_DEBUG_OBJECT (demux, "gst name for %s is %s", mimetype, gstname);
246 gst_multipart_combine_flows (GstMultipartDemux * demux, GstMultipartPad * pad,
251 /* store the value */
254 /* any other error that is not-linked can be returned right
256 if (ret != GST_FLOW_NOT_LINKED)
259 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
260 for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
261 GstMultipartPad *opad = (GstMultipartPad *) walk->data;
263 ret = opad->last_ret;
264 /* some other return value (must be SUCCESS but we can return
265 * other values as well) */
266 if (ret != GST_FLOW_NOT_LINKED)
269 /* if we get here, all other pads were unlinked and we return
275 static GstMultipartPad *
276 gst_multipart_find_pad_by_mime (GstMultipartDemux * demux, gchar * mime,
281 walk = demux->srcpads;
283 GstMultipartPad *pad = (GstMultipartPad *) walk->data;
285 if (!strcmp (pad->mime, mime)) {
294 /* pad not found, create it */
297 GstMultipartPad *mppad;
299 const gchar *capsname;
304 mppad = g_new0 (GstMultipartPad, 1);
306 GST_DEBUG_OBJECT (demux, "creating pad with mime: %s", mime);
308 name = g_strdup_printf ("src_%u", demux->numpads);
310 gst_pad_new_from_static_template (&multipart_demux_src_template_factory,
315 mppad->mime = g_strdup (mime);
316 mppad->last_ret = GST_FLOW_OK;
317 mppad->last_ts = GST_CLOCK_TIME_NONE;
318 mppad->discont = TRUE;
320 demux->srcpads = g_slist_prepend (demux->srcpads, mppad);
323 gst_pad_use_fixed_caps (pad);
324 gst_pad_set_active (pad, TRUE);
325 gst_element_add_pad (GST_ELEMENT_CAST (demux), pad);
327 /* prepare and send stream-start */
328 if (!demux->have_group_id) {
329 event = gst_pad_get_sticky_event (demux->sinkpad,
330 GST_EVENT_STREAM_START, 0);
333 demux->have_group_id =
334 gst_event_parse_group_id (event, &demux->group_id);
335 gst_event_unref (event);
336 } else if (!demux->have_group_id) {
337 demux->have_group_id = TRUE;
338 demux->group_id = gst_util_group_id_next ();
342 stream_id = gst_pad_create_stream_id (pad,
343 GST_ELEMENT_CAST (demux), demux->mime_type);
345 event = gst_event_new_stream_start (stream_id);
346 if (demux->have_group_id)
347 gst_event_set_group_id (event, demux->group_id);
349 gst_pad_push_event (pad, event);
352 /* take the mime type, convert it to the caps name */
353 capsname = gst_multipart_demux_get_gstname (demux, mime);
354 caps = gst_caps_from_string (capsname);
355 GST_DEBUG_OBJECT (demux, "caps for pad: %s", capsname);
356 gst_pad_set_caps (pad, caps);
357 gst_caps_unref (caps);
363 if (demux->singleStream) {
364 gst_element_no_more_pads (GST_ELEMENT_CAST (demux));
372 get_line_end (const guint8 * data, const guint8 * dataend, guint8 ** end,
376 gboolean foundr = FALSE;
378 for (x = (guint8 *) data; x < dataend; x++) {
381 } else if (*x == '\n') {
382 *end = x - (foundr ? 1 : 0);
391 get_mime_len (const guint8 * data, guint maxlen)
396 while (*x != '\0' && *x != '\r' && *x != '\n' && *x != ';') {
403 multipart_parse_header (GstMultipartDemux * multipart)
406 const guint8 *dataend;
413 datalen = gst_adapter_available (multipart->adapter);
414 data = gst_adapter_map (multipart->adapter, datalen);
415 dataend = data + datalen;
417 /* Skip leading whitespace, pos endposition should at least leave space for
418 * the boundary and a \n */
419 for (pos = (guint8 *) data; pos < dataend - 4 && g_ascii_isspace (*pos);
422 if (pos >= dataend - 4)
425 if (G_UNLIKELY (pos[0] != '-' || pos[1] != '-')) {
426 GST_DEBUG_OBJECT (multipart, "No boundary available");
430 /* First the boundary */
431 if (!get_line_end (pos, dataend, &end, &next))
434 /* Ignore the leading -- */
435 boundary_len = end - pos - 2;
436 boundary = (gchar *) pos + 2;
437 if (boundary_len < 1) {
438 GST_DEBUG_OBJECT (multipart, "No boundary available");
442 if (G_UNLIKELY (multipart->boundary == NULL)) {
443 /* First time we see the boundary, copy it */
444 multipart->boundary = g_strndup (boundary, boundary_len);
445 multipart->boundary_len = boundary_len;
446 } else if (G_UNLIKELY (boundary_len != multipart->boundary_len)) {
447 /* Something odd is going on, either the boundary indicated EOS or it's
449 if (G_UNLIKELY (boundary_len == multipart->boundary_len + 2 &&
450 !strncmp (boundary, multipart->boundary, multipart->boundary_len) &&
451 !strncmp (boundary + multipart->boundary_len, "--", 2)))
454 GST_DEBUG_OBJECT (multipart,
455 "Boundary length doesn't match detected boundary (%d <> %d",
456 boundary_len, multipart->boundary_len);
458 } else if (G_UNLIKELY (strncmp (boundary, multipart->boundary, boundary_len))) {
459 GST_DEBUG_OBJECT (multipart, "Boundary doesn't match previous boundary");
464 while (get_line_end (pos, dataend, &end, &next)) {
465 guint len = end - pos;
468 /* empty line, data starts behind us */
469 GST_DEBUG_OBJECT (multipart,
470 "Parsed the header - boundary: %s, mime-type: %s, content-length: %d",
471 multipart->boundary, multipart->mime_type, multipart->content_length);
472 gst_adapter_unmap (multipart->adapter);
476 if (len >= 14 && !g_ascii_strncasecmp ("content-type:", (gchar *) pos, 13)) {
479 /* only take the mime type up to the first ; if any. After ; there can be
480 * properties that we don't handle yet. */
481 mime_len = get_mime_len (pos + 14, len - 14);
483 g_free (multipart->mime_type);
484 multipart->mime_type = g_ascii_strdown ((gchar *) pos + 14, mime_len);
485 } else if (len >= 15 &&
486 !g_ascii_strncasecmp ("content-length:", (gchar *) pos, 15)) {
487 multipart->content_length =
488 g_ascii_strtoull ((gchar *) pos + 15, NULL, 10);
494 GST_DEBUG_OBJECT (multipart, "Need more data for the header");
495 gst_adapter_unmap (multipart->adapter);
497 return MULTIPART_NEED_MORE_DATA;
501 GST_ELEMENT_ERROR (multipart, STREAM, DEMUX, (NULL),
502 ("Boundary not found in the multipart header"));
503 gst_adapter_unmap (multipart->adapter);
504 return MULTIPART_DATA_ERROR;
508 GST_DEBUG_OBJECT (multipart, "we are EOS");
509 gst_adapter_unmap (multipart->adapter);
510 return MULTIPART_DATA_EOS;
515 multipart_find_boundary (GstMultipartDemux * multipart, gint * datalen)
517 /* Adaptor is positioned at the start of the data */
518 const guint8 *data, *pos;
519 const guint8 *dataend;
522 if (multipart->content_length >= 0) {
523 /* fast path, known content length :) */
524 len = multipart->content_length;
525 if (gst_adapter_available (multipart->adapter) >= len + 2) {
527 data = gst_adapter_map (multipart->adapter, len + 1);
529 /* If data[len] contains \r then assume a newline is \r\n */
530 if (data[len] == '\r')
532 else if (data[len] == '\n')
535 gst_adapter_unmap (multipart->adapter);
536 /* Don't check if boundary is actually there, but let the header parsing
537 * bail out if it isn't */
541 return MULTIPART_NEED_MORE_DATA;
545 len = gst_adapter_available (multipart->adapter);
547 return MULTIPART_NEED_MORE_DATA;
548 data = gst_adapter_map (multipart->adapter, len);
549 dataend = data + len;
551 for (pos = data + multipart->scanpos;
552 pos <= dataend - multipart->boundary_len - 2; pos++) {
553 if (*pos == '-' && pos[1] == '-' &&
554 !strncmp ((gchar *) pos + 2,
555 multipart->boundary, multipart->boundary_len)) {
556 /* Found the boundary! Check if there was a newline before the boundary */
558 if (pos - 2 > data && pos[-2] == '\r')
560 else if (pos - 1 > data && pos[-1] == '\n')
564 gst_adapter_unmap (multipart->adapter);
565 multipart->scanpos = 0;
569 gst_adapter_unmap (multipart->adapter);
570 multipart->scanpos = pos - data;
571 return MULTIPART_NEED_MORE_DATA;
575 gst_multipart_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
577 GstMultipartDemux *multipart;
582 multipart = GST_MULTIPART_DEMUX (parent);
583 adapter = multipart->adapter;
587 if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) {
590 for (l = multipart->srcpads; l != NULL; l = l->next) {
591 GstMultipartPad *srcpad = l->data;
593 srcpad->discont = TRUE;
595 gst_adapter_clear (adapter);
597 gst_adapter_push (adapter, buf);
599 while (gst_adapter_available (adapter) > 0) {
600 GstMultipartPad *srcpad;
605 if (G_UNLIKELY (!multipart->header_completed)) {
606 if ((size = multipart_parse_header (multipart)) < 0) {
609 gst_adapter_flush (adapter, size);
610 multipart->header_completed = TRUE;
613 if ((size = multipart_find_boundary (multipart, &datalen)) < 0) {
617 /* Invalidate header info */
618 multipart->header_completed = FALSE;
619 multipart->content_length = -1;
621 if (G_UNLIKELY (datalen <= 0)) {
622 GST_DEBUG_OBJECT (multipart, "skipping empty content.");
623 gst_adapter_flush (adapter, size - datalen);
628 gst_multipart_find_pad_by_mime (multipart,
629 multipart->mime_type, &created);
631 ts = gst_adapter_prev_pts (adapter, NULL);
632 outbuf = gst_adapter_take_buffer (adapter, datalen);
633 gst_adapter_flush (adapter, size - datalen);
639 gst_segment_init (&segment, GST_FORMAT_TIME);
641 /* Push new segment, first buffer has 0 timestamp */
642 gst_pad_push_event (srcpad->pad, gst_event_new_segment (&segment));
644 tags = gst_tag_list_new (GST_TAG_CONTAINER_FORMAT, "Multipart", NULL);
645 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
646 gst_pad_push_event (srcpad->pad, gst_event_new_tag (tags));
649 outbuf = gst_buffer_make_writable (outbuf);
650 if (srcpad->last_ts == GST_CLOCK_TIME_NONE || srcpad->last_ts != ts) {
651 GST_BUFFER_TIMESTAMP (outbuf) = ts;
652 srcpad->last_ts = ts;
654 GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
657 if (srcpad->discont) {
658 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
659 srcpad->discont = FALSE;
661 GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DISCONT);
664 GST_DEBUG_OBJECT (multipart,
665 "pushing buffer with timestamp %" GST_TIME_FORMAT,
666 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
667 res = gst_pad_push (srcpad->pad, outbuf);
668 res = gst_multipart_combine_flows (multipart, srcpad, res);
669 if (res != GST_FLOW_OK)
675 if (G_UNLIKELY (size == MULTIPART_DATA_ERROR))
676 return GST_FLOW_ERROR;
677 if (G_UNLIKELY (size == MULTIPART_DATA_EOS))
683 static GstStateChangeReturn
684 gst_multipart_demux_change_state (GstElement * element,
685 GstStateChange transition)
687 GstMultipartDemux *multipart;
688 GstStateChangeReturn ret;
690 multipart = GST_MULTIPART_DEMUX (element);
692 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
693 if (ret == GST_STATE_CHANGE_FAILURE)
696 switch (transition) {
697 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
699 case GST_STATE_CHANGE_PAUSED_TO_READY:
700 multipart->header_completed = FALSE;
701 g_free (multipart->boundary);
702 multipart->boundary = NULL;
703 g_free (multipart->mime_type);
704 multipart->mime_type = NULL;
705 gst_adapter_clear (multipart->adapter);
706 multipart->content_length = -1;
707 multipart->scanpos = 0;
708 gst_multipart_demux_remove_src_pads (multipart);
709 multipart->have_group_id = FALSE;
710 multipart->group_id = G_MAXUINT;
712 case GST_STATE_CHANGE_READY_TO_NULL:
723 gst_multipart_set_property (GObject * object, guint prop_id,
724 const GValue * value, GParamSpec * pspec)
726 GstMultipartDemux *filter;
728 filter = GST_MULTIPART_DEMUX (object);
732 /* Not really that usefull anymore as we can reliably autoscan */
733 g_free (filter->boundary);
734 filter->boundary = g_value_dup_string (value);
735 if (filter->boundary != NULL) {
736 filter->boundary_len = strlen (filter->boundary);
739 case PROP_SINGLE_STREAM:
740 filter->singleStream = g_value_get_boolean (value);
743 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
749 gst_multipart_get_property (GObject * object, guint prop_id,
750 GValue * value, GParamSpec * pspec)
752 GstMultipartDemux *filter;
754 filter = GST_MULTIPART_DEMUX (object);
758 g_value_set_string (value, filter->boundary);
760 case PROP_SINGLE_STREAM:
761 g_value_set_boolean (value, filter->singleStream);
764 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
772 gst_multipart_demux_plugin_init (GstPlugin * plugin)
774 GST_DEBUG_CATEGORY_INIT (gst_multipart_demux_debug,
775 "multipartdemux", 0, "multipart demuxer");
777 return gst_element_register (plugin, "multipartdemux", GST_RANK_PRIMARY,
778 GST_TYPE_MULTIPART_DEMUX);