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_%d",
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, GstBuffer * buf);
118 static GstStateChangeReturn gst_multipart_demux_change_state (GstElement *
119 element, GstStateChange transition);
121 static void gst_multipart_set_property (GObject * object, guint prop_id,
122 const GValue * value, GParamSpec * pspec);
124 static void gst_multipart_get_property (GObject * object, guint prop_id,
125 GValue * value, GParamSpec * pspec);
127 static void gst_multipart_demux_finalize (GObject * object);
129 GST_BOILERPLATE (GstMultipartDemux, gst_multipart_demux, GstElement,
133 gst_multipart_demux_base_init (gpointer g_class)
135 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
137 gst_element_class_add_pad_template (element_class,
138 gst_static_pad_template_get (&multipart_demux_sink_template_factory));
139 gst_element_class_add_pad_template (element_class,
140 gst_static_pad_template_get (&multipart_demux_src_template_factory));
141 gst_element_class_set_details_simple (element_class, "Multipart demuxer",
143 "demux multipart streams",
144 "Wim Taymans <wim.taymans@gmail.com>, Sjoerd Simons <sjoerd@luon.net>");
148 gst_multipart_demux_class_init (GstMultipartDemuxClass * klass)
152 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
153 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
155 gobject_class->finalize = gst_multipart_demux_finalize;
156 gobject_class->set_property = gst_multipart_set_property;
157 gobject_class->get_property = gst_multipart_get_property;
159 g_object_class_install_property (gobject_class, PROP_BOUNDARY,
160 g_param_spec_string ("boundary", "Boundary",
161 "The boundary string separating data, automatic if NULL",
163 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
165 g_object_class_install_property (gobject_class, PROP_AUTOSCAN,
166 g_param_spec_boolean ("autoscan", "autoscan",
167 "Try to autofind the prefix (deprecated unused, see boundary)",
168 DEFAULT_AUTOSCAN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
171 * GstMultipartDemux::single-stream:
173 * Assume that there is only one stream whose content-type will
174 * not change and emit no-more-pads as soon as the first boundary
175 * content is parsed, decoded, and pads are linked.
179 g_object_class_install_property (gobject_class, PROP_SINGLE_STREAM,
180 g_param_spec_boolean ("single-stream", "Single Stream",
181 "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",
182 DEFAULT_SINGLE_STREAM, G_PARAM_READWRITE));
184 /* populate gst names and mime types pairs */
185 klass->gstnames = g_hash_table_new (g_str_hash, g_str_equal);
186 for (i = 0; gstnames[i].key; i++) {
187 g_hash_table_insert (klass->gstnames, (gpointer) gstnames[i].key,
188 (gpointer) gstnames[i].val);
191 gstelement_class->change_state = gst_multipart_demux_change_state;
195 gst_multipart_demux_init (GstMultipartDemux * multipart,
196 GstMultipartDemuxClass * g_class)
198 /* create the sink pad */
200 gst_pad_new_from_static_template (&multipart_demux_sink_template_factory,
202 gst_element_add_pad (GST_ELEMENT_CAST (multipart), multipart->sinkpad);
203 gst_pad_set_chain_function (multipart->sinkpad,
204 GST_DEBUG_FUNCPTR (gst_multipart_demux_chain));
206 multipart->adapter = gst_adapter_new ();
207 multipart->boundary = DEFAULT_BOUNDARY;
208 multipart->mime_type = NULL;
209 multipart->content_length = -1;
210 multipart->header_completed = FALSE;
211 multipart->scanpos = 0;
212 multipart->autoscan = DEFAULT_AUTOSCAN;
213 multipart->singleStream = DEFAULT_SINGLE_STREAM;
217 gst_multipart_pad_free (GstMultipartPad * mppad)
219 g_free (mppad->mime);
224 gst_multipart_demux_finalize (GObject * object)
226 GstMultipartDemux *demux = GST_MULTIPART_DEMUX (object);
228 g_object_unref (demux->adapter);
229 g_free (demux->boundary);
230 g_free (demux->mime_type);
231 g_slist_foreach (demux->srcpads, (GFunc) gst_multipart_pad_free, NULL);
232 g_slist_free (demux->srcpads);
234 G_OBJECT_CLASS (parent_class)->finalize (object);
238 gst_multipart_demux_get_gstname (GstMultipartDemux * demux, gchar * mimetype)
240 GstMultipartDemuxClass *klass;
241 const gchar *gstname;
243 klass = GST_MULTIPART_DEMUX_GET_CLASS (demux);
245 /* use hashtable to convert to gst name */
246 gstname = g_hash_table_lookup (klass->gstnames, mimetype);
247 if (gstname == NULL) {
248 /* no gst name mapping, use mime type */
251 GST_DEBUG_OBJECT (demux, "gst name for %s is %s", mimetype, gstname);
256 gst_multipart_combine_flows (GstMultipartDemux * demux, GstMultipartPad * pad,
261 /* store the value */
264 /* any other error that is not-linked can be returned right
266 if (ret != GST_FLOW_NOT_LINKED)
269 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
270 for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
271 GstMultipartPad *opad = (GstMultipartPad *) walk->data;
273 ret = opad->last_ret;
274 /* some other return value (must be SUCCESS but we can return
275 * other values as well) */
276 if (ret != GST_FLOW_NOT_LINKED)
279 /* if we get here, all other pads were unlinked and we return
285 static GstMultipartPad *
286 gst_multipart_find_pad_by_mime (GstMultipartDemux * demux, gchar * mime,
291 walk = demux->srcpads;
293 GstMultipartPad *pad = (GstMultipartPad *) walk->data;
295 if (!strcmp (pad->mime, mime)) {
304 /* pad not found, create it */
307 GstMultipartPad *mppad;
309 const gchar *capsname;
312 mppad = g_new0 (GstMultipartPad, 1);
314 GST_DEBUG_OBJECT (demux, "creating pad with mime: %s", mime);
316 name = g_strdup_printf ("src_%d", demux->numpads);
318 gst_pad_new_from_static_template (&multipart_demux_src_template_factory,
322 /* take the mime type, convert it to the caps name */
323 capsname = gst_multipart_demux_get_gstname (demux, mime);
324 caps = gst_caps_from_string (capsname);
325 GST_DEBUG_OBJECT (demux, "caps for pad: %s", capsname);
326 gst_pad_use_fixed_caps (pad);
327 gst_pad_set_caps (pad, caps);
328 gst_caps_unref (caps);
331 mppad->mime = g_strdup (mime);
332 mppad->last_ret = GST_FLOW_OK;
334 demux->srcpads = g_slist_prepend (demux->srcpads, mppad);
337 gst_pad_set_active (pad, TRUE);
338 gst_element_add_pad (GST_ELEMENT_CAST (demux), pad);
344 if (demux->singleStream) {
345 gst_element_no_more_pads (GST_ELEMENT_CAST (demux));
353 get_line_end (const guint8 * data, const guint8 * dataend, guint8 ** end,
357 gboolean foundr = FALSE;
359 for (x = (guint8 *) data; x < dataend; x++) {
362 } else if (*x == '\n') {
363 *end = x - (foundr ? 1 : 0);
372 get_mime_len (const guint8 * data, guint maxlen)
377 while (*x != '\0' && *x != '\r' && *x != '\n' && *x != ';') {
384 multipart_parse_header (GstMultipartDemux * multipart)
387 const guint8 *dataend;
394 datalen = gst_adapter_available (multipart->adapter);
395 data = gst_adapter_peek (multipart->adapter, datalen);
396 dataend = data + datalen;
398 /* Skip leading whitespace, pos endposition should at least leave space for
399 * the boundary and a \n */
400 for (pos = (guint8 *) data; pos < dataend - 4 && g_ascii_isspace (*pos);
403 if (pos >= dataend - 4) {
404 return MULTIPART_NEED_MORE_DATA;
407 if (G_UNLIKELY (pos[0] != '-' || pos[1] != '-')) {
408 GST_DEBUG_OBJECT (multipart, "No boundary available");
412 /* First the boundary */
413 if (!get_line_end (pos, dataend, &end, &next))
414 return MULTIPART_NEED_MORE_DATA;
416 /* Ignore the leading -- */
417 boundary_len = end - pos - 2;
418 boundary = (gchar *) pos + 2;
419 if (boundary_len < 1) {
420 GST_DEBUG_OBJECT (multipart, "No boundary available");
424 if (G_UNLIKELY (multipart->boundary == NULL)) {
425 /* First time we see the boundary, copy it */
426 multipart->boundary = g_strndup (boundary, boundary_len);
427 multipart->boundary_len = boundary_len;
428 } else if (G_UNLIKELY (boundary_len != multipart->boundary_len)) {
429 /* Something odd is going on, either the boundary indicated EOS or it's
431 if (G_UNLIKELY (boundary_len == multipart->boundary_len + 2 &&
432 !strncmp (boundary, multipart->boundary, multipart->boundary_len) &&
433 !strncmp (boundary + multipart->boundary_len, "--", 2))) {
434 return MULTIPART_DATA_EOS;
436 GST_DEBUG_OBJECT (multipart,
437 "Boundary length doesn't match detected boundary (%d <> %d",
438 boundary_len, multipart->boundary_len);
440 } else if (G_UNLIKELY (strncmp (boundary, multipart->boundary, boundary_len))) {
441 GST_DEBUG_OBJECT (multipart, "Boundary doesn't match previous boundary");
447 while (get_line_end (pos, dataend, &end, &next)) {
448 guint len = end - pos;
451 /* empty line, data starts behind us */
452 GST_DEBUG_OBJECT (multipart,
453 "Parsed the header - boundary: %s, mime-type: %s, content-length: %d",
454 multipart->boundary, multipart->mime_type, multipart->content_length);
458 if (len >= 14 && !g_ascii_strncasecmp ("content-type:", (gchar *) pos, 13)) {
461 /* only take the mime type up to the first ; if any. After ; there can be
462 * properties that we don't handle yet. */
463 mime_len = get_mime_len (pos + 14, len - 14);
465 g_free (multipart->mime_type);
466 multipart->mime_type = g_ascii_strdown ((gchar *) pos + 14, mime_len);
467 } else if (len >= 15 &&
468 !g_ascii_strncasecmp ("content-length:", (gchar *) pos, 15)) {
469 multipart->content_length =
470 g_ascii_strtoull ((gchar *) pos + 15, NULL, 10);
474 GST_DEBUG_OBJECT (multipart, "Need more data for the header");
475 return MULTIPART_NEED_MORE_DATA;
479 GST_ELEMENT_ERROR (multipart, STREAM, DEMUX, (NULL),
480 ("Boundary not found in the multipart header"));
481 return MULTIPART_DATA_ERROR;
486 multipart_find_boundary (GstMultipartDemux * multipart, gint * datalen)
488 /* Adaptor is positioned at the start of the data */
489 const guint8 *data, *pos;
490 const guint8 *dataend;
493 if (multipart->content_length >= 0) {
494 /* fast path, known content length :) */
495 len = multipart->content_length;
496 if (gst_adapter_available (multipart->adapter) >= len + 2) {
498 data = gst_adapter_peek (multipart->adapter, len + 1);
500 /* If data[len] contains \r then assume a newline is \r\n */
501 if (data[len] == '\r')
503 else if (data[len] == '\n')
505 /* Don't check if boundary is actually there, but let the header parsing
506 * bail out if it isn't */
510 return MULTIPART_NEED_MORE_DATA;
514 len = gst_adapter_available (multipart->adapter);
516 return MULTIPART_NEED_MORE_DATA;
517 data = gst_adapter_peek (multipart->adapter, len);
518 dataend = data + len;
520 for (pos = data + multipart->scanpos;
521 pos <= dataend - multipart->boundary_len - 2; pos++) {
522 if (*pos == '-' && pos[1] == '-' &&
523 !strncmp ((gchar *) pos + 2,
524 multipart->boundary, multipart->boundary_len)) {
525 /* Found the boundary! Check if there was a newline before the boundary */
527 if (pos - 2 > data && pos[-2] == '\r')
529 else if (pos - 1 > data && pos[-1] == '\n')
533 multipart->scanpos = 0;
537 multipart->scanpos = pos - data;
538 return MULTIPART_NEED_MORE_DATA;
542 gst_multipart_demux_chain (GstPad * pad, GstBuffer * buf)
544 GstMultipartDemux *multipart;
546 GstClockTime timestamp;
550 multipart = GST_MULTIPART_DEMUX (gst_pad_get_parent (pad));
551 adapter = multipart->adapter;
555 timestamp = GST_BUFFER_TIMESTAMP (buf);
557 if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) {
558 gst_adapter_clear (adapter);
560 gst_adapter_push (adapter, buf);
562 while (gst_adapter_available (adapter) > 0) {
563 GstMultipartPad *srcpad;
568 if (G_UNLIKELY (!multipart->header_completed)) {
569 if ((size = multipart_parse_header (multipart)) < 0) {
572 gst_adapter_flush (adapter, size);
573 multipart->header_completed = TRUE;
576 if ((size = multipart_find_boundary (multipart, &datalen)) < 0) {
580 /* Invalidate header info */
581 multipart->header_completed = FALSE;
582 multipart->content_length = -1;
584 if (G_UNLIKELY (datalen <= 0)) {
585 GST_DEBUG_OBJECT (multipart, "skipping empty content.");
586 gst_adapter_flush (adapter, size - datalen);
589 gst_multipart_find_pad_by_mime (multipart,
590 multipart->mime_type, &created);
591 outbuf = gst_adapter_take_buffer (adapter, datalen);
592 gst_adapter_flush (adapter, size - datalen);
594 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (srcpad->pad));
598 /* Push new segment, first buffer has 0 timestamp */
599 gst_pad_push_event (srcpad->pad,
600 gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0));
603 gst_tag_list_new_full (GST_TAG_CONTAINER_FORMAT, "Multipart", NULL);
604 gst_pad_push_event (srcpad->pad, gst_event_new_tag (tags));
606 GST_BUFFER_TIMESTAMP (outbuf) = 0;
608 GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
610 GST_DEBUG_OBJECT (multipart,
611 "pushing buffer with timestamp %" GST_TIME_FORMAT,
612 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
613 GST_DEBUG_OBJECT (multipart, "buffer has caps %" GST_PTR_FORMAT,
614 GST_BUFFER_CAPS (outbuf));
615 res = gst_pad_push (srcpad->pad, outbuf);
616 res = gst_multipart_combine_flows (multipart, srcpad, res);
617 if (res != GST_FLOW_OK)
623 gst_object_unref (multipart);
625 if (G_UNLIKELY (size == MULTIPART_DATA_ERROR))
626 return GST_FLOW_ERROR;
627 if (G_UNLIKELY (size == MULTIPART_DATA_EOS))
628 return GST_FLOW_UNEXPECTED;
633 static GstStateChangeReturn
634 gst_multipart_demux_change_state (GstElement * element,
635 GstStateChange transition)
637 GstMultipartDemux *multipart;
638 GstStateChangeReturn ret;
640 multipart = GST_MULTIPART_DEMUX (element);
642 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
643 if (ret == GST_STATE_CHANGE_FAILURE)
646 switch (transition) {
647 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
649 case GST_STATE_CHANGE_PAUSED_TO_READY:
650 multipart->header_completed = FALSE;
651 g_free (multipart->boundary);
652 multipart->boundary = NULL;
653 g_free (multipart->mime_type);
654 multipart->mime_type = NULL;
655 gst_adapter_clear (multipart->adapter);
657 case GST_STATE_CHANGE_READY_TO_NULL:
668 gst_multipart_set_property (GObject * object, guint prop_id,
669 const GValue * value, GParamSpec * pspec)
671 GstMultipartDemux *filter;
673 g_return_if_fail (GST_IS_MULTIPART_DEMUX (object));
674 filter = GST_MULTIPART_DEMUX (object);
678 /* Not really that usefull anymore as we can reliably autoscan */
679 g_free (filter->boundary);
680 filter->boundary = g_value_dup_string (value);
681 if (filter->boundary != NULL) {
682 filter->boundary_len = strlen (filter->boundary);
686 filter->autoscan = g_value_get_boolean (value);
688 case PROP_SINGLE_STREAM:
689 filter->singleStream = g_value_get_boolean (value);
692 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
698 gst_multipart_get_property (GObject * object, guint prop_id,
699 GValue * value, GParamSpec * pspec)
701 GstMultipartDemux *filter;
703 g_return_if_fail (GST_IS_MULTIPART_DEMUX (object));
704 filter = GST_MULTIPART_DEMUX (object);
708 g_value_set_string (value, filter->boundary);
711 g_value_set_boolean (value, filter->autoscan);
713 case PROP_SINGLE_STREAM:
714 g_value_set_boolean (value, filter->singleStream);
717 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
725 gst_multipart_demux_plugin_init (GstPlugin * plugin)
727 GST_DEBUG_CATEGORY_INIT (gst_multipart_demux_debug,
728 "multipartdemux", 0, "multipart demuxer");
730 return gst_element_register (plugin, "multipartdemux", GST_RANK_PRIMARY,
731 GST_TYPE_MULTIPART_DEMUX);