[Bug 754483] New: gst-plugins-good: multipartdemux: Better parsing of Content-Type multipart header

GStreamer (GNOME Bugzilla) bugzilla at gnome.org
Wed Sep 2 10:19:30 PDT 2015


https://bugzilla.gnome.org/show_bug.cgi?id=754483

            Bug ID: 754483
           Summary: gst-plugins-good: multipartdemux: Better parsing of
                    Content-Type multipart header
    Classification: Platform
           Product: GStreamer
           Version: 1.2.4
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: Normal
         Component: gst-plugins-good
          Assignee: gstreamer-bugs at lists.freedesktop.org
          Reporter: aladro.jose at gmail.com
        QA Contact: gstreamer-bugs at lists.freedesktop.org
     GNOME version: ---

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

-- 
You are receiving this mail because:
You are the QA Contact for the bug.
You are the assignee for the bug.


More information about the gstreamer-bugs mailing list