GNOME Bugzilla – Bug 754483
multipartdemux: Better parsing of Content-Type multipart header
Last modified: 2018-11-03 15:03:48 UTC
Dear maintainer: The parsing of the "Content-Type:" multipart header in multipartdemux assumes ***1*** space must follow the colon in every chunk's header or it will parse the type incorrectly. I used an Approx APPIP01P2P IP camera (http://approx.es/APPIP01P2P). HTTP Request ------------ GET /videostream.cgi?user=XXX&pwd=XXX HTTP/1.1 HTTP Response ------------- HTTP/1.1 200 OK Date: Wed Sep 2 16:27:08 2015 Server: GoAhead-Webs Accept-Ranges: bytes Connection: close Content-Type: multipart/x-mixed-replace;boundary=object-ipcamera <-- this is parsed OK --object-ipcamera Content-Type:image/jpeg <-- this is not parsed properly Content-Length:54264 ......JFIF... ... --object-ipcamera Content-Type:image/jpeg Content-Length:54150 ......JFIF... ... and so on What happens ------------ The chunks are identified by multipartdemux as "mage/jpeg" (missing the "i"), assuming a space must follow the colon in the header. For that reason, any capsfilter for 'image/jpeg' or any jpegparse or jpegdec element further in the pipeline will not link. Note: Some buggy IP cameras may not format this header properly, as in this casse, but i.e. VLC 2.2.0 and Firefox 31.8.0 don't have any trouble playing that multipart content. How to reproduce (useless, see the caps = mage/jpeg) ---------------------------------------------------- $ gst-launch-1.0 -e -v --gst-debug=*:1 souphttpsrc location="http://192.168.4.37/videostream.cgi?user=asus&pwd=asus" do-timestamp=true ! multipartdemux ! identity silent=false ! fakesink Setting pipeline to PAUSED ... Pipeline is PREROLLING ... /GstPipeline:pipeline0/GstIdentity:identity0: last-message = event ******* (identity0:sink) E (type: caps (12814), GstEventCaps, caps=(GstCaps)mage/jpeg;) 0xe3a180 /GstPipeline:pipeline0/GstIdentity:identity0.GstPad:src: caps = mage/jpeg /GstPipeline:pipeline0/GstFakeSink:fakesink0.GstPad:sink: caps = mage/jpeg /GstPipeline:pipeline0/GstIdentity:identity0.GstPad:sink: caps = mage/jpeg /GstPipeline:pipeline0/GstIdentity:identity0: last-message = event ******* (identity0:sink) E (type: segment (17934), GstEventSegment, segment=(GstSegment)"GstSegment, flags=(GstSegmentFlags)GST_SEGMENT_FLAG_NONE, rate=(double)1, applied-rate=(double)1, format=(GstFormat)GST_FORMAT_TIME, base=(guint64)0, offset=(guint64)0, start=(guint64)0, stop=(guint64)18446744073709551615, time=(guint64)0, position=(guint64)0, duration=(guint64)18446744073709551615;";) 0xe3a240 /GstPipeline:pipeline0/GstIdentity:identity0: last-message = event ******* (identity0:sink) E (type: tag (20510), GstTagList-global, taglist=(taglist)"taglist\,\ container-format\=\(string\)Multipart\;";) 0xe3a300 /GstPipeline:pipeline0/GstIdentity:identity0: last-message = chain ******* (identity0:sink) (52276 bytes, dts: none, pts:none, duration: none, offset: -1, offset_end: -1, flags: 00000040 discont ) 0xf35b30 Pipeline is PREROLLED ... Setting pipeline to PLAYING ... New clock: GstSystemClock /GstPipeline:pipeline0/GstIdentity:identity0: last-message = chain ******* (identity0:sink) (52776 bytes, dts: none, pts:none, duration: none, offset: -1, offset_end: -1, flags: 00000000 ) 0xe41070 /GstPipeline:pipeline0/GstIdentity:identity0: last-message = chain ******* (identity0:sink) (53112 bytes, dts: none, pts:0:00:00.580902326, duration: none, offset: -1, offset_end: -1, flags: 00000000 ) 0xe414b0 /GstPipeline:pipeline0/GstIdentity:identity0: last-message = chain ******* (identity0:sink) (53160 bytes, dts: none, pts:0:00:01.642750430, duration: none, offset: -1, offset_end: -1, flags: 00000000 ) 0xf35c40 ^Chandling interrupt. How to reproduce (added jpegparse, not-linked error) ---------------------------------------------------- $ gst-launch-1.0 -e -v --gst-debug=*:1 souphttpsrc location="http://192.168.xxx.xxx/videostream.cgi?user=xxx&pwd=xxx" do-timestamp=true ! multipartdemux ! jpegparse ! identity silent=false ! fakesink Setting pipeline to PAUSED ... Pipeline is PREROLLING ... ERROR: from element /GstPipeline:pipeline0/GstSoupHTTPSrc:souphttpsrc0: Internal data flow error. Additional debug info: gstbasesrc.c(2865): gst_base_src_loop (): /GstPipeline:pipeline0/GstSoupHTTPSrc:souphttpsrc0: streaming task paused, reason not-linked (-1) ERROR: pipeline doesn't want to preroll. Setting pipeline to NULL ... /GstPipeline:pipeline0/GstMultipartDemux:multipartdemux0.GstPad:src_0: caps = NULL Freeing pipeline ... GStreamer version ----------------- I've tested it with gst-launch-1.0 (GStreamer 1.2.4) but the relevant code is similar/the same in the master trunk (1.4?) at: http://cgit.freedesktop.org/gstreamer/gst-plugins-good/tree/gst/multipart/multipartdemux.c Relevant code ------------- http://cgit.freedesktop.org/gstreamer/gst-plugins-good/tree/gst/multipart/multipartdemux.c:477 ---cut--- if (len >= 14 && !g_ascii_strncasecmp ("content-type:", (gchar *) pos, 13)) { guint mime_len; /* only take the mime type up to the first ; if any. After ; there can be * properties that we don't handle yet. */ mime_len = get_mime_len (pos + 14, len - 14); g_free (multipart->mime_type); multipart->mime_type = g_ascii_strdown ((gchar *) pos + 14, mime_len); } else if (len >= 15 && ---cut--- This piece checks for header length >= 14 chars, compares the first 13 and assumes the actual type starts at char 14, which is not always true. Possible solutions ------------------ Allow zero or more whitespace after the colon. Solution that works for me -------------------------- Consider both cases: 1 and zero spaces after the colon. For 14-char pattern, read after char #14. For 13-char pattern, read after char #13. (code recompiled with "apt-get build-dep ...", "apt-get source ..." and "fakeroot debian/rules binary" stuff) ---cut--- if (len >= 14 && !g_ascii_strncasecmp ("content-type: ", (gchar *) pos, 14)) { guint mime_len; /* only take the mime type up to the first ; if any. After ; there can be * properties that we don't handle yet. */ mime_len = get_mime_len (pos + 14, len - 14); g_free (multipart->mime_type); multipart->mime_type = g_ascii_strdown ((gchar *) pos + 14, mime_len); } else if (len >= 13 && !g_ascii_strncasecmp ("content-type:", (gchar *) pos, 13)) { guint mime_len; /* only take the mime type up to the first ; if any. After ; there can be * properties that we don't handle yet. */ mime_len = get_mime_len (pos + 13, len - 13); g_free (multipart->mime_type); multipart->mime_type = g_ascii_strdown ((gchar *) pos + 13, mime_len); } else if (len >= 15 && ---cut--- Result with patch applied ------------------------- $ gst-launch-1.0 -e -v --gst-debug=*:1 souphttpsrc location="http://192.168.xxx.xxx/videostream.cgi?user=xxx&pwd=xxx" do-timestamp=true ! multipartdemux ! jpegparse ! identity silent=false ! fakesink Setting pipeline to PAUSED ... Pipeline is PREROLLING ... /GstPipeline:pipeline0/GstJpegParse:jpegparse0.GstPad:sink: caps = image/jpeg /GstPipeline:pipeline0/GstJpegParse:jpegparse0.GstPad:src: caps = image/jpeg, parsed=(boolean)true, format=(string)UYVY, interlaced=(boolean)false, width=(int)640, height=(int)480, framerate=(fraction)1/1 /GstPipeline:pipeline0/GstIdentity:identity0: last-message = event ******* (identity0:sink) E (type: caps (12814), GstEventCaps, caps=(GstCaps)image/jpeg, parsed=(boolean)true, format=(string)UYVY, interlaced=(boolean)false, width=(int)640, height=(int)480, framerate=(fraction)1/1;) 0x21f5300 /GstPipeline:pipeline0/GstIdentity:identity0.GstPad:src: caps = image/jpeg, parsed=(boolean)true, format=(string)UYVY, interlaced=(boolean)false, width=(int)640, height=(int)480, framerate=(fraction)1/1 /GstPipeline:pipeline0/GstFakeSink:fakesink0.GstPad:sink: caps = image/jpeg, parsed=(boolean)true, format=(string)UYVY, interlaced=(boolean)false, width=(int)640, height=(int)480, framerate=(fraction)1/1 /GstPipeline:pipeline0/GstIdentity:identity0.GstPad:sink: caps = image/jpeg, parsed=(boolean)true, format=(string)UYVY, interlaced=(boolean)false, width=(int)640, height=(int)480, framerate=(fraction)1/1 /GstPipeline:pipeline0/GstIdentity:identity0: last-message = event ******* (identity0:sink) E (type: segment (17934), GstEventSegment, segment=(GstSegment)"GstSegment, flags=(GstSegmentFlags)GST_SEGMENT_FLAG_NONE, rate=(double)1, applied-rate=(double)1, format=(GstFormat)GST_FORMAT_TIME, base=(guint64)0, offset=(guint64)0, start=(guint64)0, stop=(guint64)18446744073709551615, time=(guint64)0, position=(guint64)0, duration=(guint64)18446744073709551615;";) 0x21f5400 /GstPipeline:pipeline0/GstIdentity:identity0: last-message = event ******* (identity0:sink) E (type: tag (20510), GstTagList-stream, taglist=(taglist)"taglist\,\ container-format\=\(string\)Multipart\;";) 0x21f5460 /GstPipeline:pipeline0/GstIdentity:identity0: last-message = chain ******* (identity0:sink) (51172 bytes, dts: none, pts:none, duration: none, offset: -1, offset_end: -1, flags: 00000040 discont ) 0x23b2ac0 Pipeline is PREROLLED ... Setting pipeline to PLAYING ... New clock: GstSystemClock /GstPipeline:pipeline0/GstIdentity:identity0: last-message = chain ******* (identity0:sink) (50988 bytes, dts: none, pts:none, duration: none, offset: -1, offset_end: -1, flags: 00000000 ) 0x21fa000 /GstPipeline:pipeline0/GstIdentity:identity0: last-message = chain ******* (identity0:sink) (50944 bytes, dts: none, pts:0:00:01.007713812, duration: none, offset: -1, offset_end: -1, flags: 00000000 ) 0x21fa330 ^Chandling interrupt. Could someone add this patch (or a more elegant one) to the development trunk? Thanks. Sorry for the long post. Jose
Created attachment 310533 [details] [review] Patch to allow zero and one space after the colon in Content-Type: header
Created attachment 310539 [details] Sample capture multipart data Play it with: $ gst-launch-1.0 -e -v --gst-debug=*:1 filesrc location=approx-cam-capture.bin ! multipartdemux ! 'image/jpeg, framerate=1/1' ! jpegparse ! jpegdec ! videoconvert ! aasink Replace aasink with autovideosink (I was testing it on a headless server) and you'll see my chikens.
Created attachment 311740 [details] [review] Patch to parse Content-Type: header allowing no or 1 space after header name Alternative patch, more elegant. Tested on gst-plugins-good1.0-1.4.4 (as it comes in Debian Jessie). Also tested with success on 1.2.4 (Debian Wheezy).
-- GitLab Migration Automatic Message -- This bug has been migrated to freedesktop.org's GitLab instance and has been closed from further activity. You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/issues/217.