On Wed, 14 Jan 2015, Luca Barbato wrote:

Support only streams with Content-Length.
---

That's probably ok as a first version of it. The ironic thing is that the mpjpeg streams that our muxer produces don't include any Content-Length.


It is a quite simple demuxer written while writing a small blogpost about
demuxers. It is apparently useful for at least few people.

libavformat/Makefile     |   1 +
libavformat/allformats.c |   2 +-
libavformat/mpjpegdec.c  | 210 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 212 insertions(+), 1 deletion(-)
create mode 100644 libavformat/mpjpegdec.c

diff --git a/libavformat/Makefile b/libavformat/Makefile
index bdd0e9e..3496c4c 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -201,6 +201,7 @@ OBJS-$(CONFIG_MPEGPS_DEMUXER)            += mpeg.o
OBJS-$(CONFIG_MPEGTS_DEMUXER)            += mpegts.o isom.o
OBJS-$(CONFIG_MPEGTS_MUXER)              += mpegtsenc.o
OBJS-$(CONFIG_MPEGVIDEO_DEMUXER)         += mpegvideodec.o rawdec.o
+OBJS-$(CONFIG_MPJPEG_DEMUXER)            += mpjpegdec.o
OBJS-$(CONFIG_MPJPEG_MUXER)              += mpjpeg.o
OBJS-$(CONFIG_MSNWC_TCP_DEMUXER)         += msnwc_tcp.o
OBJS-$(CONFIG_MTV_DEMUXER)               += mtv.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index cb22ae3..1e52143 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -162,7 +162,7 @@ void av_register_all(void)
    REGISTER_MUXDEMUX(MPEGTS,           mpegts);
    REGISTER_DEMUXER (MPEGTSRAW,        mpegtsraw);
    REGISTER_DEMUXER (MPEGVIDEO,        mpegvideo);
-    REGISTER_MUXER   (MPJPEG,           mpjpeg);
+    REGISTER_MUXDEMUX(MPJPEG,           mpjpeg);
    REGISTER_DEMUXER (MSNWC_TCP,        msnwc_tcp);
    REGISTER_DEMUXER (MTV,              mtv);
    REGISTER_DEMUXER (MV,               mv);
diff --git a/libavformat/mpjpegdec.c b/libavformat/mpjpegdec.c
new file mode 100644
index 0000000..edcc061
--- /dev/null
+++ b/libavformat/mpjpegdec.c
@@ -0,0 +1,210 @@
+/*
+ * Multipart JPEG format
+ * Copyright (c) 2014 Luca Barbato
+ *
+ * This file is part of Libav.
+ *
+ * Libav is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * Libav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/avstring.h"
+
+#include "avformat.h"
+#include "internal.h"
+
+static int get_line(AVIOContext *pb, char *line, int line_size)
+{
+    int i, ch;
+    char *q = line;
+
+    for (i = 0; !pb->eof_reached; i++) {
+        ch = avio_r8(pb);
+        if (ch == '\n') {
+            if (q > line && q[-1] == '\r')
+                q--;
+            *q = '\0';
+
+            return 0;
+        } else {
+            if ((q - line) < line_size - 1)
+                *q++ = ch;
+        }
+    }
+
+    if (pb->error)
+        return pb->error;
+    return AVERROR_EOF;
+}

We already have ff_get_line - could this be adapted to use that instead?

+static int split_tag_value(char **tag, char **value, char *line)
+{
+    char *p = line;
+
+    while (*p != '\0' && *p != ':')
+        p++;

Isn't this pretty much what strchr does, or it could at least be changed to use strchr?

+    if (*p != ':')
+        return AVERROR_INVALIDDATA;
+
+    *p   = '\0';
+    *tag = line;
+
+    p++;
+
+    while (av_isspace(*p))
+        p++;
+
+    *value = p;
+
+    return 0;
+}
+
+static int check_content_type(char *line)
+{
+    char *tag, *value;
+    int ret = split_tag_value(&tag, &value, line);
+
+    if (ret < 0)
+        return ret;
+
+    if (av_strcasecmp(tag, "Content-type") ||
+        av_strcasecmp(value, "image/jpeg"))
+        return AVERROR_INVALIDDATA;
+
+    return 0;
+}
+
+static int mpjpeg_read_probe(AVProbeData *p)
+{
+    AVIOContext *pb;
+    char line[128] = { 0 };
+    int ret;
+
+    pb = avio_alloc_context(p->buf, p->buf_size, 0, NULL, NULL, NULL, NULL);
+    if (!pb)
+        return AVERROR(ENOMEM);
+
+    while (!pb->eof_reached) {
+        ret = get_line(pb, line, sizeof(line));
+        if (ret < 0)
+            break;
+
+        ret = check_content_type(line);
+        if (!ret)
+            return AVPROBE_SCORE_MAX;
+    }

This leaks the allocated AVIOContext.

Also, even though this in itself isn't a very heavy operation, it would still be executed on every single file open. Is there some simple rough test that could be executed at the start, that could return sooner (before even trying to allocate anything) for clearly non-matching data?

+
+    return 0;
+}
+
+static int mpjpeg_read_header(AVFormatContext *s)
+{
+    AVStream *st;
+    char boundary[70 + 2 + 1];

That seems like an awfully specific size - where does 70 come from?

+    int ret;
+
+    ret = get_line(s->pb, boundary, sizeof(boundary));
+    if (ret < 0)
+        return ret;
+
+    if (strncmp(boundary, "--", 2))
+        return AVERROR_INVALIDDATA;

Here we seem to require that the first two bytes of the whole stream should be "--" - this would be an excellent first check in the probe function, to avoid the more expensive parsing until that matches.

+
+    st = avformat_new_stream(s, NULL);
+
+    st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+    st->codec->codec_id   = AV_CODEC_ID_MJPEG;
+
+    avpriv_set_pts_info(st, 60, 1, 25);
+
+    return 0;
+}
+
+static int parse_content_length(char *line)
+{
+    char *tag, *value;
+    int ret = split_tag_value(&tag, &value, line);
+    long int val;
+
+    if (ret < 0)
+        return ret;
+
+    if (av_strcasecmp(tag, "Content-Length"))
+        return AVERROR_INVALIDDATA;
+
+    val = strtol(value, NULL, 10);
+    if (val == LONG_MIN || val == LONG_MAX)
+        return AVERROR(errno);
+    if (val > INT_MAX)
+        return AVERROR(ERANGE);
+    return val;
+}
+
+static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    char line[128];
+    int ret, size;
+
+    ret = get_line(s->pb, line, sizeof(line));
+    if (ret < 0)
+        return ret;
+
+    ret = check_content_type(line);
+    if (ret < 0)
+        return ret;
+
+    ret = get_line(s->pb, line, sizeof(line));
+    if (ret < 0)
+        return ret;
+
+    size = parse_content_length(line);
+    if (size < 0)
+        return size;

This seems to assume that content type and content length always come in this particular order

// Martin
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to