avformat/mpjpeg: utilize MIME boundary value to detect start of new frame
authorAlex Agranovsky <alex@sighthound.com>
Sun, 29 Nov 2015 23:54:14 +0000 (18:54 -0500)
committerwm4 <nfxjfg@googlemail.com>
Wed, 2 Dec 2015 21:39:27 +0000 (22:39 +0100)
This code is disabled by default so not to regress endpoints sending invalid MIME, but can be enabled via AVOption 'strict_mime_boundary'

Signed-off-by: Alex Agranovsky <alex@sighthound.com>
doc/demuxers.texi
libavformat/mpjpegdec.c

index 349b531..fb1e4fb 100644 (file)
@@ -450,6 +450,21 @@ to 1 (-1 means automatic setting, 1 means enabled, 0 means
 disabled). Default value is -1.
 @end table
 
+@section mpjpeg
+
+MJPEG encapsulated in multi-part MIME demuxer.
+
+This demuxer allows reading of MJPEG, where each frame is represented as a part of
+multipart/x-mixed-replace stream.
+@table @option
+
+@item strict_mime_boundary
+Default implementation applies a relaxed standard to multi-part MIME boundary detection,
+to prevent regression with numerous existing endpoints not generating a proper MIME
+MJPEG stream. Turning this option on by setting it to 1 will result in a stricter check
+of the boundary value.
+@end table
+
 @section rawvideo
 
 Raw video demuxer.
index 9d5700a..b644ee4 100644 (file)
@@ -20,6 +20,7 @@
  */
 
 #include "libavutil/avstring.h"
+#include "libavutil/opt.h"
 
 #include "avformat.h"
 #include "internal.h"
 
 
 typedef struct MPJPEGDemuxContext {
+    const AVClass *class;
     char       *boundary;
     char       *searchstr;
     int         searchstr_len;
+    int         strict_mime_boundary;
 } MPJPEGDemuxContext;
 
 
@@ -242,6 +245,41 @@ static int parse_multipart_header(AVIOContext *pb,
 }
 
 
+static char* mpjpeg_get_boundary(AVIOContext* pb)
+{
+    uint8_t *mime_type = NULL;
+    uint8_t *start;
+    uint8_t *end;
+    uint8_t *res = NULL;
+    int     len;
+
+    /* get MIME type, and skip to the first parameter */
+    av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type);
+    start = mime_type;
+    while (start != NULL && *start != '\0') {
+        start = strchr(start, ';');
+        if (start)
+            start = start+1;
+
+        while (av_isspace(*start))
+            start++;
+
+        if (!av_stristart(start, "boundary=", &start)) {
+            end = strchr(start, ';');
+            if (end)
+                len = end - start - 1;
+            else
+                len = strlen(start);
+            res = av_strndup(start, len);
+            break;
+        }
+    }
+
+    av_freep(&mime_type);
+    return res;
+}
+
+
 static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt)
 {
     int size;
@@ -249,8 +287,17 @@ static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt)
 
     MPJPEGDemuxContext *mpjpeg = s->priv_data;
     if (mpjpeg->boundary == NULL) {
-        mpjpeg->boundary = av_strdup("--");
-        mpjpeg->searchstr = av_strdup("\r\n--");
+        uint8_t* boundary = NULL;
+        if (mpjpeg->strict_mime_boundary) {
+            boundary = mpjpeg_get_boundary(s->pb);
+        }
+        if (boundary != NULL) {
+            mpjpeg->boundary = boundary;
+            mpjpeg->searchstr = av_asprintf( "\r\n%s\r\n", boundary );
+        } else {
+            mpjpeg->boundary = av_strdup("--");
+            mpjpeg->searchstr = av_strdup("\r\n--");
+        }
         if (!mpjpeg->boundary || !mpjpeg->searchstr) {
             av_freep(&mpjpeg->boundary);
             av_freep(&mpjpeg->searchstr);
@@ -309,6 +356,22 @@ static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt)
     return ret;
 }
 
+#define OFFSET(x) offsetof(MPJPEGDemuxContext, x)
+
+#define DEC AV_OPT_FLAG_DECODING_PARAM
+const AVOption mpjpeg_options[] = {
+    { "strict_mime_boundary",  "require MIME boundaries match", OFFSET(strict_mime_boundary), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC },
+    { NULL }
+};
+
+
+static const AVClass mpjpeg_demuxer_class = {
+    .class_name     = "MPJPEG demuxer",
+    .item_name      = av_default_item_name,
+    .option         = mpjpeg_options,
+    .version        = LIBAVUTIL_VERSION_INT,
+};
+
 AVInputFormat ff_mpjpeg_demuxer = {
     .name              = "mpjpeg",
     .long_name         = NULL_IF_CONFIG_SMALL("MIME multipart JPEG"),
@@ -318,5 +381,8 @@ AVInputFormat ff_mpjpeg_demuxer = {
     .read_probe        = mpjpeg_read_probe,
     .read_header       = mpjpeg_read_header,
     .read_packet       = mpjpeg_read_packet,
-    .read_close        = mpjpeg_read_close
+    .read_close        = mpjpeg_read_close,
+    .priv_class        = &mpjpeg_demuxer_class
 };
+
+