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

Ash Hughes ashes-iontach at hotmail.com
Sun Oct 7 01:20:03 CEST 2012


Hi,

Here is an update to the SMV patch. For using one packet for multiple frames, I don't know how to get it to send the same packet multiple times to the decode_frame function. Also, the documentation at http://ffmpeg.org/doxygen/trunk/group__lavf__decoding.html#g4fdb3084415a82e3810de6ee60e46a61 implies that this can't be done anyway, as there is only one video Frame per packet. Perhaps there's a property on AVPacket that I'm missing?

With the threading issue, I wonder if avcodec_open could be split into a mutexed public part and an unmutexed but private part? This seems messy and potentially dangerous to me though, maybe it would be better to accept the inefficiency for this (rarely used) codec?

Thanks,

---
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-06 13:59:15.540183753 +0100
@@ -25,6 +25,7 @@
 
 #include "libavutil/avassert.h"
 #include "libavutil/dict.h"
+#include "libavutil/intreadwrite.h"
 #include "libavutil/log.h"
 #include "libavutil/mathematics.h"
 #include "libavutil/opt.h"
@@ -53,6 +54,7 @@
     int smv_eof;
     int audio_eof;
     int ignore_length;
+    int smv_cur_pt;
 } WAVContext;
 
 #if CONFIG_WAV_MUXER
@@ -506,9 +508,16 @@
             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 = 4;
+            vst->codec->extradata = av_malloc(vst->codec->extradata_size +
+                                              FF_INPUT_BUFFER_PADDING_SIZE);
+            if (!vst->codec->extradata) {
+                av_log(s, AV_LOG_ERROR, "Could not allocate extradata.\n");
+                return AVERROR(ENOMEM);
+            }
             size = avio_rl24(pb);
             wav->smv_data_ofs = avio_tell(pb) + (size - 5) * 3;
             avio_rl24(pb);
@@ -518,6 +527,9 @@
             avio_rl24(pb);
             avio_rl24(pb);
             wav->smv_frames_per_jpeg = avio_rl24(pb);
+            AV_WL32(vst->codec->extradata, 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,8 +629,12 @@
             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;
 smv_out:
             avio_seek(s->pb, old_pos, SEEK_SET);
@@ -678,6 +694,7 @@
         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;
     }
 
     st = s->streams[0];
diff -uNr ffmpeg-vanilla/libavcodec/smvjpegdec.c ffmpeg/libavcodec/smvjpegdec.c
--- ffmpeg-vanilla/libavcodec/smvjpegdec.c    1970-01-01 01:00:00.000000000 +0100
+++ ffmpeg/libavcodec/smvjpegdec.c    2012-10-06 13:59:33.880274691 +0100
@@ -0,0 +1,220 @@
+/*
+ * 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 "mjpegdec.h"
+#include "internal.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);
+void smv_image_pnt(uint8_t *dst_data[4], int dst_linesizes[4],
+                   uint8_t *src_data[4], const int src_linesizes[4],
+                   enum PixelFormat pix_fmt, int width, int height, int nlines);
+void smv_image_pnt_plane(uint8_t       **dst, int dst_linesize,
+                         uint8_t *src, int src_linesize,
+                         int bytewidth, int height, int nlines);
+
+typedef struct SMVJpegDecodeContext {
+    MJpegDecodeContext jpg;
+    AVFrame picture[3]; /* pictures array */
+    AVCodecContext* avctx;
+    AVCodec *codec;
+    int frames_per_jpeg;
+    enum PixelFormat pix_fmt;
+} SMVJpegDecodeContext;
+
+void smv_image_pnt_plane(uint8_t      **dst, int dst_linesize,
+                         uint8_t *src, int src_linesize,
+                         int bytewidth, int height, int nlines)
+{
+    if (!dst || !src)
+        return;
+    src += (nlines) * src_linesize * height;
+    *dst = src;
+}
+
+void smv_image_pnt(uint8_t *dst_data[4], int dst_linesizes[4],
+                   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)
+        return;
+
+    if (desc->flags & PIX_FMT_PAL ||
+        desc->flags & PIX_FMT_PSEUDOPAL) {
+        smv_image_pnt_plane(&dst_data[0], dst_linesizes[0],
+                            src_data[0], src_linesizes[0],
+                            width, height, nlines);
+        /* copy the palette */
+        dst_data[1] = src_data[1];
+    } else {
+        int i, planes_nb = 0;
+
+        for (i = 0; i < desc->nb_components; i++)
+            planes_nb = FFMAX(planes_nb, desc->comp[i].plane + 1);
+
+        for (i = 0; i < planes_nb; i++) {
+            int h = height;
+            int bwidth = av_image_get_linesize(pix_fmt, width, i);
+            if (i == 1 || i == 2) {
+                h= -((-height)>>desc->log2_chroma_h);
+            }
+            smv_image_pnt_plane(&dst_data[i], dst_linesizes[i],
+                                src_data[i], src_linesizes[i],
+                                bwidth, h, nlines);
+        }
+    }
+}
+
+av_cold int ff_smvjpeg_decode_init(AVCodecContext *avctx)
+{
+    SMVJpegDecodeContext *s = avctx->priv_data;
+
+    s->frames_per_jpeg = AV_RL32(avctx->extradata);
+    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 = 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, i;
+    int cur_frame = avpkt->pts % s->frames_per_jpeg;
+
+    /* Are we at the start of a block?
+       Also call mjpeg if we start on an odd pts and buf_data isn't
+       allocated yet. */
+    if (cur_frame == 0 || !buf_data->data[0]) {
+        /* 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, 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, mjpeg_data, data_size, avpkt);
+            s->pix_fmt = s->avctx->pix_fmt;
+            buf_data->width = s->avctx->width;
+            buf_data->height = s->avctx->height;
+            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);
+        }
+    } 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);
+
+    s->picture[1].type = FF_BUFFER_TYPE_INTERNAL;
+    s->picture[1].extended_data = s->picture[1].data;
+    s->picture[1].width         = buf_data->width;
+    s->picture[1].height        = avctx->height;
+    s->picture[1].format        = avctx->pix_fmt;
+    /* ff_init_buffer_info(avctx, &s->picture[1]); */
+    smv_image_pnt(s->picture[1].data, s->picture[1].linesize,
+        buf_data->data, buf_data->linesize, avctx->pix_fmt, avctx->width,
+        avctx->height, cur_frame);
+    for (i = 0; i < AV_NUM_DATA_POINTERS; i++) {
+        s->picture[1].base[i]     =  s->picture[1].data[i];
+        s->picture[1].linesize[i] = buf_data->linesize[i];
+    }
+
+    *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]);
+    av_freep(&s->picture[2].data[0]);
+    av_free(s->avctx);
+    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,
+    .max_lowres     = 3,
+    .long_name      = NULL_IF_CONFIG_SMALL("SMV JPEG"),
+    .priv_class     = &smvjpegdec_class,
+};
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-06 13:59:15.540183753 +0100
@@ -25,6 +25,7 @@
 
 #include "libavutil/avassert.h"
 #include "libavutil/dict.h"
+#include "libavutil/intreadwrite.h"
 #include "libavutil/log.h"
 #include "libavutil/mathematics.h"
 #include "libavutil/opt.h"
@@ -53,6 +54,7 @@
     int smv_eof;
     int audio_eof;
     int ignore_length;
+    int smv_cur_pt;
 } WAVContext;
 
 #if CONFIG_WAV_MUXER
@@ -506,9 +508,16 @@
             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 = 4;
+            vst->codec->extradata = av_malloc(vst->codec->extradata_size +
+                                              FF_INPUT_BUFFER_PADDING_SIZE);
+            if (!vst->codec->extradata) {
+                av_log(s, AV_LOG_ERROR, "Could not allocate extradata.\n");
+                return AVERROR(ENOMEM);
+            }
             size = avio_rl24(pb);
             wav->smv_data_ofs = avio_tell(pb) + (size - 5) * 3;
             avio_rl24(pb);
@@ -518,6 +527,9 @@
             avio_rl24(pb);
             avio_rl24(pb);
             wav->smv_frames_per_jpeg = avio_rl24(pb);
+            AV_WL32(vst->codec->extradata, 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,8 +629,12 @@
             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;
 smv_out:
             avio_seek(s->pb, old_pos, SEEK_SET);
@@ -678,6 +694,7 @@
         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;
     }
 
     st = s->streams[0];
diff -uNr ffmpeg-vanilla/libavcodec/smvjpegdec.c ffmpeg/libavcodec/smvjpegdec.c
--- ffmpeg-vanilla/libavcodec/smvjpegdec.c    1970-01-01 01:00:00.000000000 +0100
+++ ffmpeg/libavcodec/smvjpegdec.c    2012-10-07 00:07:37.005184736 +0100
@@ -0,0 +1,220 @@
+/*
+ * 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 "mjpegdec.h"
+#include "internal.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);
+void smv_image_pnt(uint8_t *dst_data[4], int dst_linesizes[4],
+                   uint8_t *src_data[4], const int src_linesizes[4],
+                   enum PixelFormat pix_fmt, int width, int height, int nlines);
+void smv_image_pnt_plane(uint8_t       **dst, int dst_linesize,
+                         uint8_t *src, int src_linesize,
+                         int bytewidth, int height, int nlines);
+
+typedef struct SMVJpegDecodeContext {
+    MJpegDecodeContext jpg;
+    AVFrame picture[3]; /* pictures array */
+    AVCodecContext* avctx;
+    AVCodec *codec;
+    int frames_per_jpeg;
+    enum PixelFormat pix_fmt;
+} SMVJpegDecodeContext;
+
+void smv_image_pnt_plane(uint8_t      **dst, int dst_linesize,
+                         uint8_t *src, int src_linesize,
+                         int bytewidth, int height, int nlines)
+{
+    if (!dst || !src)
+        return;
+    src += (nlines) * src_linesize * height;
+    *dst = src;
+}
+
+void smv_image_pnt(uint8_t *dst_data[4], int dst_linesizes[4],
+                   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)
+        return;
+
+    if (desc->flags & PIX_FMT_PAL ||
+        desc->flags & PIX_FMT_PSEUDOPAL) {
+        smv_image_pnt_plane(&dst_data[0], dst_linesizes[0],
+                            src_data[0], src_linesizes[0],
+                            width, height, nlines);
+        /* copy the palette */
+        dst_data[1] = src_data[1];
+    } else {
+        int i, planes_nb = 0;
+
+        for (i = 0; i < desc->nb_components; i++)
+            planes_nb = FFMAX(planes_nb, desc->comp[i].plane + 1);
+
+        for (i = 0; i < planes_nb; i++) {
+            int h = height;
+            int bwidth = av_image_get_linesize(pix_fmt, width, i);
+            if (i == 1 || i == 2) {
+                h= -((-height)>>desc->log2_chroma_h);
+            }
+            smv_image_pnt_plane(&dst_data[i], dst_linesizes[i],
+                                src_data[i], src_linesizes[i],
+                                bwidth, h, nlines);
+        }
+    }
+}
+
+av_cold int ff_smvjpeg_decode_init(AVCodecContext *avctx)
+{
+    SMVJpegDecodeContext *s = avctx->priv_data;
+
+    s->frames_per_jpeg = AV_RL32(avctx->extradata);
+    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 = 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, i;
+    int cur_frame = avpkt->pts % s->frames_per_jpeg;
+
+    /* Are we at the start of a block?
+       Also call mjpeg if we start on an odd pts and buf_data isn't
+       allocated yet. */
+    if (cur_frame == 0 || !buf_data->data[0]) {
+        /* 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, 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, mjpeg_data, data_size, avpkt);
+            s->pix_fmt = s->avctx->pix_fmt;
+            buf_data->width = s->avctx->width;
+            buf_data->height = s->avctx->height;
+            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);
+        }
+    } 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);
+
+    s->picture[1].type = FF_BUFFER_TYPE_INTERNAL;
+    s->picture[1].extended_data = s->picture[1].data;
+    s->picture[1].width         = buf_data->width;
+    s->picture[1].height        = avctx->height;
+    s->picture[1].format        = avctx->pix_fmt;
+    /* ff_init_buffer_info(avctx, &s->picture[1]); */
+    smv_image_pnt(s->picture[1].data, s->picture[1].linesize,
+        buf_data->data, buf_data->linesize, avctx->pix_fmt, avctx->width,
+        avctx->height, cur_frame);
+    for (i = 0; i < AV_NUM_DATA_POINTERS; i++) {
+        s->picture[1].base[i]     =  s->picture[1].data[i];
+        s->picture[1].linesize[i] = buf_data->linesize[i];
+    }
+
+    *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]);
+    av_freep(&s->picture[2].data[0]);
+    av_free(s->avctx);
+    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,
+    .max_lowres     = 3,
+    .long_name      = NULL_IF_CONFIG_SMALL("SMV JPEG"),
+    .priv_class     = &smvjpegdec_class,
+};

> From: ashes-iontach at hotmail.com
> To: ffmpeg-devel at ffmpeg.org
> Date: Tue, 2 Oct 2012 23:05:25 +0000
> Subject: Re: [FFmpeg-devel] [PATCH] Support playing SMV files.
> 
> 
> 
> > Date: Tue, 2 Oct 2012 21:17:35 +0200
> > From: Reimar.Doeffinger at gmx.de
> > To: ffmpeg-devel at ffmpeg.org
> > Subject: Re: [FFmpeg-devel] [PATCH] Support playing SMV files.
> > 
> > On Tue, Oct 02, 2012 at 11:50:08AM +0100, Ash Hughes wrote:
> > > +            vst->codec->extradata_size = sizeof(SMVCodecExtra);
> > > +            vst->codec->extradata = av_malloc(sizeof(SMVCodecExtra) + FF_INPUT_BUFFER_PADDING_SIZE);
> > 
> > You can't store extradata directly like that, if you were to mux
> > it like that in a container on a little-endian machine the resulting
> > file would only be readable on a little-endian machine again.
> > And that is not even going into issues like struct alignment which
> > could break things even when just changing compilers.
> 
> alright, I'll look more closely at how other codecs handle extradata
> 
> > 
> > > @@ -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++;
> > 
> > Are you trying to create one packet per final frame here?
> > I don't think that should be done, IMHO it complicates things
> > without much benefit.
> 
> My intention was to have it reuse packets, like you mention below, but I got it wrong :S
> > 
> > > +    s->picture[0].reference = s->picture[1].reference = 3;
> > > +    s->picture[2].reference = 3;
> > 
> > Why? SMV doesn't use reference frames to my knowledge.
> 
> ok!
> 
> > 
> > > +    s->avctx[0] = avctx;
> > > +    s->avctx[1] = avcodec_alloc_context3(s->codec);
> > 
> > You never actually use s->avctx[0]
> 
> other codecs (appear to me to) do it, e.g. kgv1dec.c so I assumed it was necessary.
> 
> > 
> > > +int ff_smvjpeg_decode_frame(AVCodecContext *avctx, void *data, int *data_size,
> > > +                          AVPacket *avpkt)
> > 
> > The indentation seems a bit strange.
> 
> missing two spaces? copied from mjpegdec and renamed...
> > 
> > > +{    
> > 
> > Trailing whitespace can't be committed into the repository.
> 
> oops!
> 
> > 
> > > +    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;
> > 
> > I don't think you're supposed to use "priv" that way, it won't
> > work in any external players like MPlayer or VLC.
> > You could use side data, but as said I think this approach is wrong.
> > There should be only one packet per frames_per_jpeg frames.
> > The decoder will then return 0 for all but the first frame to produce
> > multiple frames from one input packet.
> > I suspect this is likely to uncover some bugs though.
> 
> ok, will rework.
> 
> > 
> > > +        /* we can't open or close the codec in init or end so we have
> > > +           to do it all here. */
> > 
> > Why?
> > Also opening/closing the codec is potentially slow, so it really should
> > not be done each time.
> 
> I know :( The reason for this is that if you do it in init or end, you get "insufficient thread locking around avcodec_open/close()". In libavcodec/utils.c,  in avcodec_open2, the line:
> 
>  if (entangled_thread_counter != 1)
> 
> is what triggers this. We probably don't want to call somebodies lock function twice if they supply one and I'm not sure exactly what it is protecting so I don't know how to get round this neatly. Any advice would be much appreciated!
> 
> > 
> > > +            av_picture_copy((AVPicture*)&s->picture[2], (AVPicture*)mjpeg_data,
> > > +                s->pix_fmt, buf_data->width, buf_data->height);
> > > +    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);
> > 
> > This is 2 copies more than what should be necessary.
> > The first obviously can be avoided by fixing things so you can
> > open/close the JPEG decoder in the open/close decoder functions.
> > The second can be avoided by not setting CODEC_CAP_DR1 and just
> > exporting the appropriate part of the decoded frame.
> > The whole point of CODEC_CAP_DR1 is to avoid copying, if you
> > end up doing a copy in the codec because of it you do the opposite
> > of what its purpose it.
> > You are by far not the only one to misunderstand this (well, I assume
> > you misunderstood it), so suggestions for documenting this better
> > are welcome.
> 
> Yes, I've misunderstood this, I'll have another read of documentation.
> > 
> > > +    if (s->picture[2].data[0]) {
> > > +        av_freep(&s->picture[2].data[0]);
> > > +    }
> > 
> > The if() is unnecessary.
> 
> ok, and thanks for the feedback :)
> 
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel at ffmpeg.org
> > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>  		 	   		  
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 		 	   		  


More information about the ffmpeg-devel mailing list