[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