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., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, 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 filesrc location=/tmp/test.multipart ! multipartdemux ! jpegdec ! ffmpegcolorspace ! ximagesink
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 /* signals and args */
65 #define DEFAULT_AUTOSCAN FALSE
66 #define DEFAULT_BOUNDARY NULL
67 #define DEFAULT_SINGLE_STREAM FALSE
77 static GstStaticPadTemplate multipart_demux_src_template_factory =
78 GST_STATIC_PAD_TEMPLATE ("src_%u",
83 static GstStaticPadTemplate multipart_demux_sink_template_factory =
84 GST_STATIC_PAD_TEMPLATE ("sink",
87 GST_STATIC_CAPS ("multipart/x-mixed-replace")
96 /* convert from mime types to gst structure names. Add more when needed. The
97 * mime-type is stored as lowercase */
98 static const GstNamesMap gstnames[] = {
99 /* RFC 2046 says audio/basic is mulaw, mono, 8000Hz */
100 {"audio/basic", "audio/x-mulaw, channels=1, rate=8000"},
102 "audio/x-adpcm, bitrate=16000, layout=g726, channels=1, rate=8000"},
104 "audio/x-adpcm, bitrate=24000, layout=g726, channels=1, rate=8000"},
106 "audio/x-adpcm, bitrate=32000, layout=g726, channels=1, rate=8000"},
108 "audio/x-adpcm, bitrate=40000, layout=g726, channels=1, rate=8000"},
109 /* Panasonic Network Cameras non-standard types */
111 "audio/x-adpcm, bitrate=32000, layout=g726, channels=1, rate=8000"},
116 static GstFlowReturn gst_multipart_demux_chain (GstPad * pad,
117 GstObject * parent, GstBuffer * buf);
119 static GstStateChangeReturn gst_multipart_demux_change_state (GstElement *
120 element, GstStateChange transition);
122 static void gst_multipart_set_property (GObject * object, guint prop_id,
123 const GValue * value, GParamSpec * pspec);
125 static void gst_multipart_get_property (GObject * object, guint prop_id,
126 GValue * value, GParamSpec * pspec);
128 static void gst_multipart_demux_finalize (GObject * object);
130 #define gst_multipart_demux_parent_class parent_class
131 G_DEFINE_TYPE (GstMultipartDemux, gst_multipart_demux, GST_TYPE_ELEMENT);
134 gst_multipart_demux_class_init (GstMultipartDemuxClass * klass)
138 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
139 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
141 gobject_class->finalize = gst_multipart_demux_finalize;
142 gobject_class->set_property = gst_multipart_set_property;
143 gobject_class->get_property = gst_multipart_get_property;
145 g_object_class_install_property (gobject_class, PROP_BOUNDARY,
146 g_param_spec_string ("boundary", "Boundary",
147 "The boundary string separating data, automatic if NULL",
149 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
151 g_object_class_install_property (gobject_class, PROP_AUTOSCAN,
152 g_param_spec_boolean ("autoscan", "autoscan",
153 "Try to autofind the prefix (deprecated unused, see boundary)",
154 DEFAULT_AUTOSCAN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
157 * GstMultipartDemux::single-stream:
159 * Assume that there is only one stream whose content-type will
160 * not change and emit no-more-pads as soon as the first boundary
161 * content is parsed, decoded, and pads are linked.
165 g_object_class_install_property (gobject_class, PROP_SINGLE_STREAM,
166 g_param_spec_boolean ("single-stream", "Single Stream",
167 "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",
168 DEFAULT_SINGLE_STREAM, G_PARAM_READWRITE));
170 /* populate gst names and mime types pairs */
171 klass->gstnames = g_hash_table_new (g_str_hash, g_str_equal);
172 for (i = 0; gstnames[i].key; i++) {
173 g_hash_table_insert (klass->gstnames, (gpointer) gstnames[i].key,
174 (gpointer) gstnames[i].val);
177 gstelement_class->change_state = gst_multipart_demux_change_state;
179 gst_element_class_add_pad_template (gstelement_class,
180 gst_static_pad_template_get (&multipart_demux_sink_template_factory));
181 gst_element_class_add_pad_template (gstelement_class,
182 gst_static_pad_template_get (&multipart_demux_src_template_factory));
183 gst_element_class_set_static_metadata (gstelement_class, "Multipart demuxer",
185 "demux multipart streams",
186 "Wim Taymans <wim.taymans@gmail.com>, Sjoerd Simons <sjoerd@luon.net>");
190 gst_multipart_demux_init (GstMultipartDemux * multipart)
192 /* create the sink pad */
194 gst_pad_new_from_static_template (&multipart_demux_sink_template_factory,
196 gst_element_add_pad (GST_ELEMENT_CAST (multipart), multipart->sinkpad);
197 gst_pad_set_chain_function (multipart->sinkpad,
198 GST_DEBUG_FUNCPTR (gst_multipart_demux_chain));
200 multipart->adapter = gst_adapter_new ();
201 multipart->boundary = DEFAULT_BOUNDARY;
202 multipart->mime_type = NULL;
203 multipart->content_length = -1;
204 multipart->header_completed = FALSE;
205 multipart->scanpos = 0;
206 multipart->autoscan = DEFAULT_AUTOSCAN;
207 multipart->singleStream = DEFAULT_SINGLE_STREAM;
211 gst_multipart_pad_free (GstMultipartPad * mppad)
213 g_free (mppad->mime);
218 gst_multipart_demux_finalize (GObject * object)
220 GstMultipartDemux *demux = GST_MULTIPART_DEMUX (object);
222 g_object_unref (demux->adapter);
223 g_free (demux->boundary);
224 g_free (demux->mime_type);
225 g_slist_foreach (demux->srcpads, (GFunc) gst_multipart_pad_free, NULL);
226 g_slist_free (demux->srcpads);
228 G_OBJECT_CLASS (parent_class)->finalize (object);
232 gst_multipart_demux_get_gstname (GstMultipartDemux * demux, gchar * mimetype)
234 GstMultipartDemuxClass *klass;
235 const gchar *gstname;
237 klass = GST_MULTIPART_DEMUX_GET_CLASS (demux);
239 /* use hashtable to convert to gst name */
240 gstname = g_hash_table_lookup (klass->gstnames, mimetype);
241 if (gstname == NULL) {
242 /* no gst name mapping, use mime type */
245 GST_DEBUG_OBJECT (demux, "gst name for %s is %s", mimetype, gstname);
250 gst_multipart_combine_flows (GstMultipartDemux * demux, GstMultipartPad * pad,
255 /* store the value */
258 /* any other error that is not-linked can be returned right
260 if (ret != GST_FLOW_NOT_LINKED)
263 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
264 for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
265 GstMultipartPad *opad = (GstMultipartPad *) walk->data;
267 ret = opad->last_ret;
268 /* some other return value (must be SUCCESS but we can return
269 * other values as well) */
270 if (ret != GST_FLOW_NOT_LINKED)
273 /* if we get here, all other pads were unlinked and we return
279 static GstMultipartPad *
280 gst_multipart_find_pad_by_mime (GstMultipartDemux * demux, gchar * mime,
285 walk = demux->srcpads;
287 GstMultipartPad *pad = (GstMultipartPad *) walk->data;
289 if (!strcmp (pad->mime, mime)) {
298 /* pad not found, create it */
301 GstMultipartPad *mppad;
303 const gchar *capsname;
306 mppad = g_new0 (GstMultipartPad, 1);
308 GST_DEBUG_OBJECT (demux, "creating pad with mime: %s", mime);
310 name = g_strdup_printf ("src_%u", demux->numpads);
312 gst_pad_new_from_static_template (&multipart_demux_src_template_factory,
316 /* take the mime type, convert it to the caps name */
317 capsname = gst_multipart_demux_get_gstname (demux, mime);
318 caps = gst_caps_from_string (capsname);
319 GST_DEBUG_OBJECT (demux, "caps for pad: %s", capsname);
320 gst_pad_use_fixed_caps (pad);
321 gst_pad_set_caps (pad, caps);
322 gst_caps_unref (caps);
325 mppad->mime = g_strdup (mime);
326 mppad->last_ret = GST_FLOW_OK;
328 demux->srcpads = g_slist_prepend (demux->srcpads, mppad);
331 gst_pad_set_active (pad, TRUE);
332 gst_element_add_pad (GST_ELEMENT_CAST (demux), pad);
338 if (demux->singleStream) {
339 gst_element_no_more_pads (GST_ELEMENT_CAST (demux));
347 get_line_end (const guint8 * data, const guint8 * dataend, guint8 ** end,
351 gboolean foundr = FALSE;
353 for (x = (guint8 *) data; x < dataend; x++) {
356 } else if (*x == '\n') {
357 *end = x - (foundr ? 1 : 0);
366 get_mime_len (const guint8 * data, guint maxlen)
371 while (*x != '\0' && *x != '\r' && *x != '\n' && *x != ';') {
378 multipart_parse_header (GstMultipartDemux * multipart)
381 const guint8 *dataend;
388 datalen = gst_adapter_available (multipart->adapter);
389 data = gst_adapter_map (multipart->adapter, datalen);
390 dataend = data + datalen;
392 /* Skip leading whitespace, pos endposition should at least leave space for
393 * the boundary and a \n */
394 for (pos = (guint8 *) data; pos < dataend - 4 && g_ascii_isspace (*pos);
397 if (pos >= dataend - 4)
400 if (G_UNLIKELY (pos[0] != '-' || pos[1] != '-')) {
401 GST_DEBUG_OBJECT (multipart, "No boundary available");
405 /* First the boundary */
406 if (!get_line_end (pos, dataend, &end, &next))
409 /* Ignore the leading -- */
410 boundary_len = end - pos - 2;
411 boundary = (gchar *) pos + 2;
412 if (boundary_len < 1) {
413 GST_DEBUG_OBJECT (multipart, "No boundary available");
417 if (G_UNLIKELY (multipart->boundary == NULL)) {
418 /* First time we see the boundary, copy it */
419 multipart->boundary = g_strndup (boundary, boundary_len);
420 multipart->boundary_len = boundary_len;
421 } else if (G_UNLIKELY (boundary_len != multipart->boundary_len)) {
422 /* Something odd is going on, either the boundary indicated EOS or it's
424 if (G_UNLIKELY (boundary_len == multipart->boundary_len + 2 &&
425 !strncmp (boundary, multipart->boundary, multipart->boundary_len) &&
426 !strncmp (boundary + multipart->boundary_len, "--", 2)))
429 GST_DEBUG_OBJECT (multipart,
430 "Boundary length doesn't match detected boundary (%d <> %d",
431 boundary_len, multipart->boundary_len);
433 } else if (G_UNLIKELY (strncmp (boundary, multipart->boundary, boundary_len))) {
434 GST_DEBUG_OBJECT (multipart, "Boundary doesn't match previous boundary");
439 while (get_line_end (pos, dataend, &end, &next)) {
440 guint len = end - pos;
443 /* empty line, data starts behind us */
444 GST_DEBUG_OBJECT (multipart,
445 "Parsed the header - boundary: %s, mime-type: %s, content-length: %d",
446 multipart->boundary, multipart->mime_type, multipart->content_length);
447 gst_adapter_unmap (multipart->adapter);
451 if (len >= 14 && !g_ascii_strncasecmp ("content-type:", (gchar *) pos, 13)) {
454 /* only take the mime type up to the first ; if any. After ; there can be
455 * properties that we don't handle yet. */
456 mime_len = get_mime_len (pos + 14, len - 14);
458 g_free (multipart->mime_type);
459 multipart->mime_type = g_ascii_strdown ((gchar *) pos + 14, mime_len);
460 } else if (len >= 15 &&
461 !g_ascii_strncasecmp ("content-length:", (gchar *) pos, 15)) {
462 multipart->content_length =
463 g_ascii_strtoull ((gchar *) pos + 15, NULL, 10);
469 GST_DEBUG_OBJECT (multipart, "Need more data for the header");
470 gst_adapter_unmap (multipart->adapter);
472 return MULTIPART_NEED_MORE_DATA;
476 GST_ELEMENT_ERROR (multipart, STREAM, DEMUX, (NULL),
477 ("Boundary not found in the multipart header"));
478 gst_adapter_unmap (multipart->adapter);
479 return MULTIPART_DATA_ERROR;
483 GST_DEBUG_OBJECT (multipart, "we are EOS");
484 gst_adapter_unmap (multipart->adapter);
485 return MULTIPART_DATA_EOS;
490 multipart_find_boundary (GstMultipartDemux * multipart, gint * datalen)
492 /* Adaptor is positioned at the start of the data */
493 const guint8 *data, *pos;
494 const guint8 *dataend;
497 if (multipart->content_length >= 0) {
498 /* fast path, known content length :) */
499 len = multipart->content_length;
500 if (gst_adapter_available (multipart->adapter) >= len + 2) {
502 data = gst_adapter_map (multipart->adapter, len + 1);
504 /* If data[len] contains \r then assume a newline is \r\n */
505 if (data[len] == '\r')
507 else if (data[len] == '\n')
510 gst_adapter_unmap (multipart->adapter);
511 /* Don't check if boundary is actually there, but let the header parsing
512 * bail out if it isn't */
516 return MULTIPART_NEED_MORE_DATA;
520 len = gst_adapter_available (multipart->adapter);
522 return MULTIPART_NEED_MORE_DATA;
523 data = gst_adapter_map (multipart->adapter, len);
524 dataend = data + len;
526 for (pos = data + multipart->scanpos;
527 pos <= dataend - multipart->boundary_len - 2; pos++) {
528 if (*pos == '-' && pos[1] == '-' &&
529 !strncmp ((gchar *) pos + 2,
530 multipart->boundary, multipart->boundary_len)) {
531 /* Found the boundary! Check if there was a newline before the boundary */
533 if (pos - 2 > data && pos[-2] == '\r')
535 else if (pos - 1 > data && pos[-1] == '\n')
539 gst_adapter_unmap (multipart->adapter);
540 multipart->scanpos = 0;
544 gst_adapter_unmap (multipart->adapter);
545 multipart->scanpos = pos - data;
546 return MULTIPART_NEED_MORE_DATA;
550 gst_multipart_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
552 GstMultipartDemux *multipart;
554 GstClockTime timestamp;
558 multipart = GST_MULTIPART_DEMUX (parent);
559 adapter = multipart->adapter;
563 timestamp = GST_BUFFER_TIMESTAMP (buf);
565 if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) {
566 gst_adapter_clear (adapter);
568 gst_adapter_push (adapter, buf);
570 while (gst_adapter_available (adapter) > 0) {
571 GstMultipartPad *srcpad;
576 if (G_UNLIKELY (!multipart->header_completed)) {
577 if ((size = multipart_parse_header (multipart)) < 0) {
580 gst_adapter_flush (adapter, size);
581 multipart->header_completed = TRUE;
584 if ((size = multipart_find_boundary (multipart, &datalen)) < 0) {
588 /* Invalidate header info */
589 multipart->header_completed = FALSE;
590 multipart->content_length = -1;
592 if (G_UNLIKELY (datalen <= 0)) {
593 GST_DEBUG_OBJECT (multipart, "skipping empty content.");
594 gst_adapter_flush (adapter, size - datalen);
597 gst_multipart_find_pad_by_mime (multipart,
598 multipart->mime_type, &created);
599 outbuf = gst_adapter_take_buffer (adapter, datalen);
600 gst_adapter_flush (adapter, size - datalen);
606 gst_segment_init (&segment, GST_FORMAT_TIME);
608 /* Push new segment, first buffer has 0 timestamp */
609 gst_pad_push_event (srcpad->pad, gst_event_new_segment (&segment));
611 tags = gst_tag_list_new (GST_TAG_CONTAINER_FORMAT, "Multipart", NULL);
612 gst_pad_push_event (srcpad->pad, gst_event_new_tag (tags));
614 GST_BUFFER_TIMESTAMP (outbuf) = 0;
616 GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
618 GST_DEBUG_OBJECT (multipart,
619 "pushing buffer with timestamp %" GST_TIME_FORMAT,
620 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
621 res = gst_pad_push (srcpad->pad, outbuf);
622 res = gst_multipart_combine_flows (multipart, srcpad, res);
623 if (res != GST_FLOW_OK)
629 if (G_UNLIKELY (size == MULTIPART_DATA_ERROR))
630 return GST_FLOW_ERROR;
631 if (G_UNLIKELY (size == MULTIPART_DATA_EOS))
637 static GstStateChangeReturn
638 gst_multipart_demux_change_state (GstElement * element,
639 GstStateChange transition)
641 GstMultipartDemux *multipart;
642 GstStateChangeReturn ret;
644 multipart = GST_MULTIPART_DEMUX (element);
646 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
647 if (ret == GST_STATE_CHANGE_FAILURE)
650 switch (transition) {
651 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
653 case GST_STATE_CHANGE_PAUSED_TO_READY:
654 multipart->header_completed = FALSE;
655 g_free (multipart->boundary);
656 multipart->boundary = NULL;
657 g_free (multipart->mime_type);
658 multipart->mime_type = NULL;
659 gst_adapter_clear (multipart->adapter);
661 case GST_STATE_CHANGE_READY_TO_NULL:
672 gst_multipart_set_property (GObject * object, guint prop_id,
673 const GValue * value, GParamSpec * pspec)
675 GstMultipartDemux *filter;
677 g_return_if_fail (GST_IS_MULTIPART_DEMUX (object));
678 filter = GST_MULTIPART_DEMUX (object);
682 /* Not really that usefull anymore as we can reliably autoscan */
683 g_free (filter->boundary);
684 filter->boundary = g_value_dup_string (value);
685 if (filter->boundary != NULL) {
686 filter->boundary_len = strlen (filter->boundary);
690 filter->autoscan = g_value_get_boolean (value);
692 case PROP_SINGLE_STREAM:
693 filter->singleStream = g_value_get_boolean (value);
696 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
702 gst_multipart_get_property (GObject * object, guint prop_id,
703 GValue * value, GParamSpec * pspec)
705 GstMultipartDemux *filter;
707 g_return_if_fail (GST_IS_MULTIPART_DEMUX (object));
708 filter = GST_MULTIPART_DEMUX (object);
712 g_value_set_string (value, filter->boundary);
715 g_value_set_boolean (value, filter->autoscan);
717 case PROP_SINGLE_STREAM:
718 g_value_set_boolean (value, filter->singleStream);
721 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
729 gst_multipart_demux_plugin_init (GstPlugin * plugin)
731 GST_DEBUG_CATEGORY_INIT (gst_multipart_demux_debug,
732 "multipartdemux", 0, "multipart demuxer");
734 return gst_element_register (plugin, "multipartdemux", GST_RANK_PRIMARY,
735 GST_TYPE_MULTIPART_DEMUX);