[FFmpeg-devel] [PATCH] Support playing SMV files.
Ash Hughes
ashes-iontach at hotmail.com
Thu Oct 11 19:06:07 CEST 2012
Thanks for the feedback, here's an update with fixes from your comments.
Ash
---
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-11 15:23:28.037828767 +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-11 15:08:30.253376898 +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,10 @@
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;
+ if (wav->smv_frames_per_jpeg > 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 +630,13 @@
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++;
+ if (wav->smv_frames_per_jpeg > 0)
+ 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);
@@ -677,7 +695,10 @@
smv_timestamp = av_rescale_q(timestamp, s->streams[0]->time_base, s->streams[1]->time_base);
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;
+ if (wav->smv_frames_per_jpeg > 0) {
+ 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-11 15:40:10.854801495 +0100
@@ -0,0 +1,203 @@
+/*
+ * 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[2]; /* pictures array */
+ AVCodecContext* avctx;
+ int frames_per_jpeg;
+} 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];
+ int i, planes_nb = 0;
+
+ if (desc->flags & PIX_FMT_HWACCEL)
+ return;
+
+ 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;
+ AVCodec *codec;
+ AVDictionary *thread_opt = NULL;
+
+ 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];
+ codec = avcodec_find_decoder(AV_CODEC_ID_MJPEG);
+ if (!codec)
+ av_log(avctx, AV_LOG_ERROR, "MJPEG codec not found\n");
+
+ s->avctx = avcodec_alloc_context3(codec);
+
+ av_dict_set(&thread_opt, "threads", "1", 0);
+ if (avcodec_open2_recursive(s->avctx, codec, &thread_opt) < 0) {
+ av_log(avctx, AV_LOG_ERROR, "MJPEG codec failed to open\n");
+ return -1;
+ }
+ av_dict_free(&thread_opt);
+
+ 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* mjpeg_data = &s->picture[0];
+ AVFrame* output = data;
+ int i, new_height;
+ int cur_frame = 0;
+
+ if (s->frames_per_jpeg > 0)
+ cur_frame = avpkt->pts % s->frames_per_jpeg;
+
+ /* Are we at the start of a block? */
+ if (cur_frame == 0)
+ ret = avcodec_decode_video2(s->avctx, mjpeg_data, data_size, avpkt);
+ else /*use the last lot... */
+ *data_size = sizeof(AVPicture);
+
+ avctx->pix_fmt = s->avctx->pix_fmt;
+ new_height = mjpeg_data->height;
+ if (s->frames_per_jpeg > 0) {
+ if (new_height % s->frames_per_jpeg != 0) {
+ av_log(avctx, AV_LOG_WARNING,
+ "There should be an integer number of frames per jpeg.\n");
+ }
+ new_height /= s->frames_per_jpeg;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Invalid number of frames per jpeg.\n");
+ ret = -1;
+ }
+
+ avcodec_set_dimensions(avctx, mjpeg_data->width, new_height);
+
+ s->picture[1].type = FF_BUFFER_TYPE_INTERNAL;
+ s->picture[1].extended_data = s->picture[1].data;
+ s->picture[1].width = avctx->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,
+ mjpeg_data->data, mjpeg_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] = mjpeg_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]);
+ avcodec_close_recursive(s->avctx);
+ 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/utils.c ffmpeg/libavcodec/utils.c
--- ffmpeg-vanilla/libavcodec/utils.c 2012-10-01 19:34:06.932974542 +0100
+++ ffmpeg/libavcodec/utils.c 2012-10-11 14:28:35.269500814 +0100
@@ -796,6 +796,27 @@
}
#endif
+int attribute_align_arg avcodec_open2_recursive(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
+{
+ int ret = 0;
+
+ entangled_thread_counter--;
+ /* Release any user-supplied mutex. */
+ if (ff_lockmgr_cb) {
+ (*ff_lockmgr_cb)(&codec_mutex, AV_LOCK_RELEASE);
+ }
+
+ ret = avcodec_open2(avctx, codec, options);
+
+ /* If there is a user-supplied mutex locking routine, call it. */
+ if (ff_lockmgr_cb) {
+ if ((*ff_lockmgr_cb)(&codec_mutex, AV_LOCK_OBTAIN))
+ return -1;
+ }
+ entangled_thread_counter++;
+ return ret;
+}
+
int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
{
int ret = 0;
@@ -1796,6 +1817,27 @@
memset(sub, 0, sizeof(AVSubtitle));
}
+av_cold int avcodec_close_recursive(AVCodecContext *avctx)
+{
+ int ret = 0;
+
+ entangled_thread_counter--;
+ /* Release any user-supplied mutex. */
+ if (ff_lockmgr_cb) {
+ (*ff_lockmgr_cb)(&codec_mutex, AV_LOCK_RELEASE);
+ }
+
+ ret = avcodec_close(avctx);
+
+ /* If there is a user-supplied mutex locking routine, call it. */
+ if (ff_lockmgr_cb) {
+ if ((*ff_lockmgr_cb)(&codec_mutex, AV_LOCK_OBTAIN))
+ return -1;
+ }
+ entangled_thread_counter++;
+ return ret;
+}
+
av_cold int avcodec_close(AVCodecContext *avctx)
{
/* If there is a user-supplied mutex locking routine, call it. */
diff -uNr ffmpeg-vanilla/libavcodec/internal.h ffmpeg/libavcodec/internal.h
--- ffmpeg-vanilla/libavcodec/internal.h 2012-10-01 19:34:06.928974504 +0100
+++ ffmpeg/libavcodec/internal.h 2012-10-11 16:20:17.890737307 +0100
@@ -178,4 +178,16 @@
int avpriv_h264_has_num_reorder_frames(AVCodecContext *avctx);
+/**
+ * Call avcodec_open2 recursively by decrementing counter, unlocking mutex,
+ * calling the function and then restoring again. Assumes the mutex is
+ * already locked
+ */
+int avcodec_open2_recursive(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
+
+/**
+ * Call avcodec_close recursively, counterpart to avcodec_open2_recursive.
+ */
+int avcodec_close_recursive(AVCodecContext *avctx);
+
#endif /* AVCODEC_INTERNAL_H */
--
Date: Wed, 10 Oct 2012 03:07:19 +0200
From: michaelni at gmx.at
To: ffmpeg-devel at ffmpeg.org
Subject: Re: [FFmpeg-devel] [PATCH] Support playing SMV files.
On Sat, Oct 06, 2012 at 11:20:03PM +0000, Ash Hughes wrote:
> 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?
calling avcodec_open2() from decode_frame is not an entirely safe act
from a quick look unlocking the mutex and calling it from init seems
safer. Maybe you could add a avcodec_open2_recursiv() that does this
[...]
> @@ -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;
division by 0
[...]
> +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];
is there some case that uses a paletted format ?
> + } 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);
this is not correct
you cant mix highlevel (avcodec_open2) and lowlevel (codec->decode())
calls
also make sure that the internal decoder you create gets threads=1
set, running it with multithreading could cause problems
[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
I am the wisest man alive, for I know one thing, and that is that I know
nothing. -- Socrates
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel at ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
More information about the ffmpeg-devel
mailing list