[FFmpeg-devel] [PATCH] Support playing SMV files.

Ash Hughes ashes-iontach at hotmail.com
Tue Oct 2 12:50:08 CEST 2012


This patch updates SMV support to display video properly, addressing the issue in the archive (http://thread.gmane.org/gmane.comp.video.ffmpeg.devel/135864/focus=135893) of more than one frame in a single jpeg. This also helps towards ticket 412 (http://ffmpeg.org/trac/ffmpeg/ticket/412). The patch modifies two functions in libavutil/imgutils.c to reuse code and ensure maintainability. The changed functions now call the modified ones with a default extra argument of 0.

Not sure if this is the best way to do this, but it works!

Signed-off-by: Ash Hughes <ashley.hughes <at> blueyonder.co.uk>

---
diff -uNr ffmpeg-vanilla//libavcodec/allcodecs.c ffmpeg//libavcodec/allcodecs.c
--- ffmpeg-vanilla//libavcodec/allcodecs.c	2012-10-01 19:34:06.944974579 +0100
+++ ffmpeg//libavcodec/allcodecs.c	2012-10-01 19:36:15.533611881 +0100
@@ -215,6 +215,7 @@
     REGISTER_ENCDEC  (SGI, sgi);
     REGISTER_DECODER (SMACKER, smacker);
     REGISTER_DECODER (SMC, smc);
+    REGISTER_DECODER (SMVJPEG, smvjpeg);
     REGISTER_ENCDEC  (SNOW, snow);
     REGISTER_DECODER (SP5X, sp5x);
     REGISTER_ENCDEC  (SUNRAST, sunrast);
diff -uNr ffmpeg-vanilla//libavcodec/avcodec.h ffmpeg//libavcodec/avcodec.h
--- ffmpeg-vanilla//libavcodec/avcodec.h	2012-10-01 19:34:06.956974647 +0100
+++ ffmpeg//libavcodec/avcodec.h	2012-10-01 19:36:15.537611963 +0100
@@ -282,6 +282,7 @@
     AV_CODEC_ID_PAF_VIDEO  = MKBETAG('P','A','F','V'),
     AV_CODEC_ID_AVRN       = MKBETAG('A','V','R','n'),
     AV_CODEC_ID_CPIA       = MKBETAG('C','P','I','A'),
+    AV_CODEC_ID_SMVJPEG,
 
     /* various PCM "codecs" */
     AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
diff -uNr ffmpeg-vanilla//libavcodec/Makefile ffmpeg//libavcodec/Makefile
--- ffmpeg-vanilla//libavcodec/Makefile	2012-10-01 19:34:06.928974504 +0100
+++ ffmpeg//libavcodec/Makefile	2012-10-01 19:36:15.537611963 +0100
@@ -388,6 +388,7 @@
 OBJS-$(CONFIG_SMACKAUD_DECODER)        += smacker.o
 OBJS-$(CONFIG_SMACKER_DECODER)         += smacker.o
 OBJS-$(CONFIG_SMC_DECODER)             += smc.o
+OBJS-$(CONFIG_SMVJPEG_DECODER)         += smvjpegdec.o
 OBJS-$(CONFIG_SNOW_DECODER)            += snowdec.o snow.o
 OBJS-$(CONFIG_SNOW_ENCODER)            += snowenc.o snow.o              \
                                           h263.o ituh263enc.o
diff -uNr ffmpeg-vanilla//libavformat/wav.c ffmpeg//libavformat/wav.c
--- ffmpeg-vanilla//libavformat/wav.c	2012-10-01 19:34:06.872974243 +0100
+++ ffmpeg//libavformat/wav.c	2012-10-01 19:41:31.131177230 +0100
@@ -23,6 +23,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavcodec/smvjpegdec.h"
 #include "libavutil/avassert.h"
 #include "libavutil/dict.h"
 #include "libavutil/log.h"
@@ -53,6 +54,8 @@
     int smv_eof;
     int audio_eof;
     int ignore_length;
+    int smv_cur_pt;
+    int force_decode;
 } WAVContext;
 
 #if CONFIG_WAV_MUXER
@@ -506,9 +509,11 @@
             avio_r8(pb);
             vst->id = 1;
             vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
-            vst->codec->codec_id = AV_CODEC_ID_MJPEG;
+            vst->codec->codec_id = AV_CODEC_ID_SMVJPEG;
             vst->codec->width  = avio_rl24(pb);
             vst->codec->height = avio_rl24(pb);
+            vst->codec->extradata_size = sizeof(SMVCodecExtra);
+            vst->codec->extradata = av_malloc(sizeof(SMVCodecExtra) + FF_INPUT_BUFFER_PADDING_SIZE);
             size = avio_rl24(pb);
             wav->smv_data_ofs = avio_tell(pb) + (size - 5) * 3;
             avio_rl24(pb);
@@ -518,6 +523,9 @@
             avio_rl24(pb);
             avio_rl24(pb);
             wav->smv_frames_per_jpeg = avio_rl24(pb);
+            ((SMVCodecExtra*)vst->codec->extradata)->frames_per_block = wav->smv_frames_per_jpeg;
+            wav->smv_cur_pt = 0;
+            vst->codec->height /= wav->smv_frames_per_jpeg;
             goto break_loop;
         case MKTAG('L', 'I', 'S', 'T'):
             list_type = avio_rl32(pb);
@@ -617,9 +625,14 @@
             if (ret < 0)
                 goto smv_out;
             pkt->pos -= 3;
-            pkt->pts = wav->smv_block * wav->smv_frames_per_jpeg;
-            wav->smv_block++;
+            pkt->pts = wav->smv_block * wav->smv_frames_per_jpeg + wav->smv_cur_pt;
+            wav->smv_cur_pt++;
+            wav->smv_cur_pt %= wav->smv_frames_per_jpeg;
+            if (wav->smv_cur_pt == 0)
+                wav->smv_block++;
+
             pkt->stream_index = 1;
+            pkt->priv = &wav->force_decode;
 smv_out:
             avio_seek(s->pb, old_pos, SEEK_SET);
             if (ret == AVERROR_EOF) {
@@ -678,6 +691,8 @@
         else
             timestamp = av_rescale_q(smv_timestamp, s->streams[1]->time_base, s->streams[0]->time_base);
         wav->smv_block = smv_timestamp / wav->smv_frames_per_jpeg;
+        wav->smv_cur_pt = smv_timestamp % wav->smv_frames_per_jpeg;
+        wav->force_decode = 1;
     }
 
     st = s->streams[0];
diff -uNr ffmpeg-vanilla//libavutil/imgutils.c ffmpeg//libavutil/imgutils.c
--- ffmpeg-vanilla//libavutil/imgutils.c	2012-10-01 19:34:06.888974305 +0100
+++ ffmpeg//libavutil/imgutils.c	2012-10-01 19:36:15.541612053 +0100
@@ -235,8 +235,17 @@
                          const uint8_t *src, int src_linesize,
                          int bytewidth, int height)
 {
+    av_image_copy_plane_skip(dst, dst_linesize, src, src_linesize,
+        bytewidth, height, 0);
+}
+
+void av_image_copy_plane_skip(uint8_t       *dst, int dst_linesize,
+                         const uint8_t *src, int src_linesize,
+                         int bytewidth, int height, int nlines)
+{
     if (!dst || !src)
         return;
+    src += (nlines) * src_linesize * height;
     for (;height > 0; height--) {
         memcpy(dst, src, bytewidth);
         dst += dst_linesize;
@@ -248,6 +257,14 @@
                    const uint8_t *src_data[4], const int src_linesizes[4],
                    enum PixelFormat pix_fmt, int width, int height)
 {
+    av_image_copy_skip(dst_data, dst_linesizes, src_data, src_linesizes,
+        pix_fmt, width, height, 0);
+}
+
+void av_image_copy_skip(uint8_t *dst_data[4], int dst_linesizes[4],
+                   const uint8_t *src_data[4], const int src_linesizes[4],
+                   enum PixelFormat pix_fmt, int width, int height, int nlines)
+{
     const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[pix_fmt];
 
     if (desc->flags & PIX_FMT_HWACCEL)
@@ -255,9 +272,9 @@
 
     if (desc->flags & PIX_FMT_PAL ||
         desc->flags & PIX_FMT_PSEUDOPAL) {
-        av_image_copy_plane(dst_data[0], dst_linesizes[0],
+        av_image_copy_plane_skip(dst_data[0], dst_linesizes[0],
                             src_data[0], src_linesizes[0],
-                            width, height);
+                            width, height, nlines);
         /* copy the palette */
         memcpy(dst_data[1], src_data[1], 4*256);
     } else {
@@ -272,9 +289,9 @@
             if (i == 1 || i == 2) {
                 h= -((-height)>>desc->log2_chroma_h);
             }
-            av_image_copy_plane(dst_data[i], dst_linesizes[i],
+            av_image_copy_plane_skip(dst_data[i], dst_linesizes[i],
                                 src_data[i], src_linesizes[i],
-                                bwidth, h);
+                                bwidth, h, nlines);
         }
     }
 }
diff -uNr ffmpeg-vanilla//libavutil/imgutils.h ffmpeg//libavutil/imgutils.h
--- ffmpeg-vanilla//libavutil/imgutils.h	2012-10-01 19:34:06.884974280 +0100
+++ ffmpeg//libavutil/imgutils.h	2012-10-01 19:36:15.541612053 +0100
@@ -107,6 +107,20 @@
                          int bytewidth, int height);
 
 /**
+ * Copy image plane from src to dst.
+ * That is, copy "height" number of lines of "bytewidth" bytes each.
+ * The first byte of each successive line is separated by *_linesize
+ * bytes. Skips copying the first n lines.
+ *
+ * @param dst_linesize linesize for the image plane in dst
+ * @param src_linesize linesize for the image plane in src
+ * @param nlines number of lines to skip before copying
+ */
+void av_image_copy_plane_skip(uint8_t       *dst, int dst_linesize,
+                         const uint8_t *src, int src_linesize,
+                         int bytewidth, int height, int nlines);
+
+/**
  * Copy image in src_data to dst_data.
  *
  * @param dst_linesizes linesizes for the image in dst_data
@@ -117,6 +131,17 @@
                    enum PixelFormat pix_fmt, int width, int height);
 
 /**
+ * Copy image in src_data to dst_data.
+ *
+ * @param dst_linesizes linesizes for the image in dst_data
+ * @param src_linesizes linesizes for the image in src_data
+ * @param nlines number of lines to skip before copying
+ */
+void av_image_copy_skip(uint8_t *dst_data[4], int dst_linesizes[4],
+                   const uint8_t *src_data[4], const int src_linesizes[4],
+                   enum PixelFormat pix_fmt, int width, int height, int nlines);
+
+/**
  * Setup the data pointers and linesizes based on the specified image
  * parameters and the provided array.
  *
diff -uNr ffmpeg-vanilla//smvjpegdec.c ffmpeg//smvjpegdec.c
--- ffmpeg-vanilla//smvjpegdec.c	1970-01-01 01:00:00.000000000 +0100
+++ ffmpeg//smvjpegdec.c	2012-10-01 19:42:32.295480490 +0100
@@ -0,0 +1,177 @@
+/*
+ * SMV JPEG decoder
+ * Copyright (c) 2012 Ash Hughes
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * SMV JPEG decoder.
+ */
+
+// #define DEBUG
+#include "avcodec.h"
+#include "libavutil/opt.h"
+#include "libavutil/imgutils.h"
+#include "smvjpegdec.h"
+#include "mjpegdec.h"
+
+int ff_smvjpeg_decode_init(AVCodecContext *avctx);
+int ff_smvjpeg_decode_end(AVCodecContext *avctx);
+int ff_smvjpeg_decode_frame(AVCodecContext *avctx,
+                          void *data, int *data_size,
+                          AVPacket *avpkt);
+
+typedef struct SMVJpegDecodeContext {
+    MJpegDecodeContext jpg;
+    AVFrame picture[3]; /* pictures array */
+    AVCodecContext* avctx[2];
+    AVCodec *codec;
+    int frames_per_jpeg;
+    enum PixelFormat pix_fmt;
+} SMVJpegDecodeContext;
+
+av_cold int ff_smvjpeg_decode_init(AVCodecContext *avctx)
+{
+    SMVJpegDecodeContext *s = avctx->priv_data;
+    SMVCodecExtra* xtra = (SMVCodecExtra*)(avctx->extradata);
+
+    s->frames_per_jpeg = xtra->frames_per_block;
+
+    s->picture[0].reference = s->picture[1].reference = 3;
+    s->picture[2].reference = 3;
+    s->jpg.picture_ptr      = &s->picture[0];
+
+    avcodec_get_frame_defaults(&s->picture[1]);
+    avctx->coded_frame = &s->picture[1];
+    s->codec = avcodec_find_decoder(AV_CODEC_ID_MJPEG);
+    if (!s->codec)
+        av_log(avctx, AV_LOG_ERROR, "MJPEG codec not found\n");
+
+    s->avctx[0] = avctx;
+    s->avctx[1] = avcodec_alloc_context3(s->codec);
+
+    return 0;
+}
+
+int ff_smvjpeg_decode_frame(AVCodecContext *avctx, void *data, int *data_size,
+                          AVPacket *avpkt)
+{    
+    SMVJpegDecodeContext *s = avctx->priv_data;
+    int ret = 0;
+    AVFrame* buf_data = &s->picture[2];
+    AVFrame* mjpeg_data = &s->picture[0];
+    AVFrame* output = data;
+    int u;
+    int cur_frame = avpkt->pts % s->frames_per_jpeg;
+    int* force_decode = (int*)avpkt->priv;
+
+    /* Are we at the start of a block or forced to decode again?
+       Also call mjpeg if we start on an odd pts and buf_data isn't
+       allocated yet. */
+    if (cur_frame == 0 || *force_decode == 1 || !buf_data->data[0]) {
+        *force_decode = 0; /* reset */
+        /* we can't open or close the codec in init or end so we have
+           to do it all here. */
+        if (avcodec_open2(s->avctx[1], s->codec, NULL) < 0) {
+            av_log(avctx, AV_LOG_ERROR, "MJPEG codec failed to open\n");
+            return -1;
+        } else {
+            /* Codec opened, create yet another buffer so the avcodec_close
+               will not wipe it. */
+            ret = s->codec->decode(s->avctx[1], mjpeg_data, data_size, avpkt);
+            s->pix_fmt = s->avctx[1]->pix_fmt;
+            buf_data->width = s->avctx[1]->width;
+            buf_data->height = s->avctx[1]->height;
+            if (buf_data->data[0]) {
+                av_freep(&buf_data->data[0]);
+            }
+            u = av_image_alloc(buf_data->data, buf_data->linesize,
+               buf_data->width, buf_data->height, s->pix_fmt, 16);
+            if (u < 0) {
+                av_log(avctx, AV_LOG_ERROR,
+                    "could not alloc raw picture buffer\n");
+                return -1;
+            }
+            av_picture_copy((AVPicture*)&s->picture[2], (AVPicture*)mjpeg_data,
+                s->pix_fmt, buf_data->width, buf_data->height);
+            avcodec_close(s->avctx[1]);
+        }
+    } else { /*use the last lot... */
+        *data_size = sizeof(AVPicture);
+    }
+
+    avctx->pix_fmt = s->pix_fmt;
+    avcodec_set_dimensions(avctx, buf_data->width,
+        buf_data->height / s->frames_per_jpeg);
+
+    if (s->picture[1].data[0])
+        avctx->release_buffer(avctx, &s->picture[1]);
+    if (u = avctx->get_buffer(avctx, &s->picture[1]) < 0) {
+        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
+        return -1;
+    }
+    av_image_copy_skip(&s->picture[1].data, &s->picture[1].linesize,
+        buf_data->data, buf_data->linesize, avctx->pix_fmt, avctx->width,
+        avctx->height, cur_frame);
+
+    *output = s->picture[1];
+
+    return ret;
+   
+}
+
+av_cold int ff_smvjpeg_decode_end(AVCodecContext *avctx)
+{
+    SMVJpegDecodeContext *s = avctx->priv_data;
+    MJpegDecodeContext *jpg = &s->jpg;
+
+    jpg->picture_ptr = NULL;
+    if (s->picture[1].data[0])
+        avctx->release_buffer(avctx, &s->picture[1]);
+    if (s->picture[2].data[0]) {
+        av_freep(&s->picture[2].data[0]);
+    }
+    av_free(s->avctx[1]);
+    return 0;
+}
+
+static const AVOption options[] = {
+    { NULL },
+};
+
+static const AVClass smvjpegdec_class = {
+    .class_name = "SMVJPEG decoder",
+    .item_name  = av_default_item_name,
+    .option = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+AVCodec ff_smvjpeg_decoder = {
+    .name           = "smvjpeg",
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_SMVJPEG,
+    .priv_data_size = sizeof(SMVJpegDecodeContext),
+    .init           = ff_smvjpeg_decode_init,
+    .close          = ff_smvjpeg_decode_end,
+    .decode         = ff_smvjpeg_decode_frame,
+    .capabilities   = CODEC_CAP_DR1,
+    .max_lowres     = 3,
+    .long_name      = NULL_IF_CONFIG_SMALL("SMV JPEG"),
+    .priv_class     = &smvjpegdec_class,
+};
diff -uNr ffmpeg-vanilla//smvjpegdec.h ffmpeg//smvjpegdec.h
--- ffmpeg-vanilla//smvjpegdec.h	1970-01-01 01:00:00.000000000 +0100
+++ ffmpeg//smvjpegdec.h	2012-10-01 19:42:32.295480490 +0100
@@ -0,0 +1,35 @@
+/*
+ * SMV JPEG decoder
+ * Copyright (c) 2012 Ash Hughes
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * SMV JPEG decoder.
+ */
+
+#ifndef AVCODEC_SMVJPEG_H
+#define AVCODEC_SMVJPEG_H
+
+typedef struct SMVCodecExtra {
+    int frames_per_block;
+} SMVCodecExtra;
+
+#endif
+


More information about the ffmpeg-devel mailing list