After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 754483 - multipartdemux: Better parsing of Content-Type multipart header
multipartdemux: Better parsing of Content-Type multipart header
Status: RESOLVED OBSOLETE
Product: GStreamer
Classification: Platform
Component: gst-plugins-good
1.2.4
Other Linux
: Normal normal
: git master
Assigned To: GStreamer Maintainers
GStreamer Maintainers
Depends on:
Blocks:
 
 
Reported: 2015-09-02 17:19 UTC by Jose Aladro
Modified: 2018-11-03 15:03 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Patch to allow zero and one space after the colon in Content-Type: header (1.27 KB, patch)
2015-09-02 17:35 UTC, Jose Aladro
none Details | Review
Sample capture multipart data (1.15 MB, application/octet-stream)
2015-09-02 18:28 UTC, Jose Aladro
  Details
Patch to parse Content-Type: header allowing no or 1 space after header name (1.19 KB, patch)
2015-09-21 09:59 UTC, Jose Aladro
none Details | Review

Description Jose Aladro 2015-09-02 17:19:30 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
Comment 1 Jose Aladro 2015-09-02 17:35:51 UTC
Created attachment 310533 [details] [review]
Patch to allow zero and one space after the colon in Content-Type: header
Comment 2 Jose Aladro 2015-09-02 18:28:25 UTC
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.
Comment 3 Jose Aladro 2015-09-21 09:59:25 UTC
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).
Comment 4 GStreamer system administrator 2018-11-03 15:03:48 UTC
-- 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.