[FFmpeg-devel] [PATCH] Video decoder and demuxer for AMV files
Vladimir Pantelic
pan
Wed Sep 26 18:30:50 CEST 2007
Vladimir Voroshilov wrote:
> Hi, All.
>
> amv_codec_ffmpeg.patch: Decoder for modified MJPEG, used it AMV files.
>
> All video frames in this file are JPEG's without header, with applied
> coded entropy data (i.e. added 0x00 bytes after each 0xff), enclosed
> in SOI and EOI markers.
> sp5x decoder code was reused.
do you know for sure that the same DQT tables are used for AMV and SP5? (e.g.
this perl script uses a different table:
http://svn.rot13.org/index.cgi/amv/view/amv.pl)
Regards,
Vladimir
>
> amv_demux_ffmpeg.patch: AMV file demuxer.
>
> AMV files contains ugly headers: strh sections are filled by zero,
> strf sections contains movie duration, image size and frame duration
> for video and waveformatex structure for audio respectively.
> Index is absent.
> Patch uses simplified version of avi_read_header routine for
> extracting needed info.
>
> I tested patches with several samples from mplayes samples collection
> (SP5x files was tested too to avoid regression in decoder) and are
> works enough good for me.
>
> P.S. I also use new fourcc (AMVV) to allow further interaction with mplayer.
>
> Opinions? Suggestions?
>
>
>
> ------------------------------------------------------------------------
>
> Index: ../mplayer/libavcodec/sp5xdec.c
> ===================================================================
> --- ../mplayer/libavcodec/sp5xdec.c (revision 10560)
> +++ ../mplayer/libavcodec/sp5xdec.c (working copy)
> @@ -72,6 +72,12 @@
> memcpy(recoded+j, &sp5x_data_sos[0], sizeof(sp5x_data_sos));
> j += sizeof(sp5x_data_sos);
>
> + if(avctx->codec_id==CODEC_ID_AMVVIDEO)
> + for (i = 2; i < buf_size-2 && j < buf_size+1024-2; i++)
> + {
> + recoded[j++] = buf[i];
> + }
> + else
> for (i = 14; i < buf_size && j < buf_size+1024-2; i++)
> {
> recoded[j++] = buf[i];
> @@ -194,3 +200,16 @@
> CODEC_CAP_DR1,
> NULL
> };
> +
> +AVCodec amvvideo_decoder = {
> + "amvv",
> + CODEC_TYPE_VIDEO,
> + CODEC_ID_AMVVIDEO,
> + sizeof(MJpegDecodeContext),
> + ff_mjpeg_decode_init,
> + NULL,
> + ff_mjpeg_decode_end,
> + sp5x_decode_frame,
> + CODEC_CAP_DR1,
> + NULL
> +};
> Index: ../mplayer/libavcodec/allcodecs.c
> ===================================================================
> --- ../mplayer/libavcodec/allcodecs.c (revision 10560)
> +++ ../mplayer/libavcodec/allcodecs.c (working copy)
> @@ -137,6 +137,7 @@
> REGISTER_DECODER (SMC, smc);
> REGISTER_ENCDEC (SNOW, snow);
> REGISTER_DECODER (SP5X, sp5x);
> + REGISTER_DECODER (AMVVIDEO, amvvideo);
> REGISTER_ENCDEC (SVQ1, svq1);
> REGISTER_DECODER (SVQ3, svq3);
> REGISTER_ENCDEC (TARGA, targa);
> Index: ../mplayer/libavcodec/avcodec.h
> ===================================================================
> --- ../mplayer/libavcodec/avcodec.h (revision 10560)
> +++ ../mplayer/libavcodec/avcodec.h (working copy)
> @@ -68,6 +68,7 @@
> CODEC_ID_MJPEGB,
> CODEC_ID_LJPEG,
> CODEC_ID_SP5X,
> + CODEC_ID_AMVVIDEO,
> CODEC_ID_JPEGLS,
> CODEC_ID_MPEG4,
> CODEC_ID_RAWVIDEO,
>
>
> ------------------------------------------------------------------------
>
> Index: ../mplayer/libavformat/avidec.c
> ===================================================================
> --- ../mplayer/libavformat/avidec.c (revision 10560)
> +++ ../mplayer/libavformat/avidec.c (working copy)
> @@ -26,8 +26,8 @@
> #undef NDEBUG
> #include <assert.h>
>
> -//#define DEBUG
> -//#define DEBUG_SEEK
> +#define DEBUG
> +#define DEBUG_SEEK
>
> typedef struct AVIStream {
> int64_t frame_offset; /* current frame (video) or byte (audio) counter
> @@ -210,6 +210,177 @@
> return 0;
> }
>
> +static int amv_read_header(AVFormatContext *s, AVFormatParameters *ap)
> +{
> + AVIContext *avi = s->priv_data;
> + ByteIOContext *pb = &s->pb;
> + uint32_t tag, tag1;
> + int width=0, height=0;
> + int stream_index, codec_type, frame_period;
> + unsigned int size;
> + int i;
> + AVStream *st = NULL;
> + AVIStream *ast = NULL;
> +
> + tag = get_le32(pb);
> + if (tag != MKTAG('R', 'I', 'F', 'F'))
> + return -1;
> +
> + get_le32(pb);
> + tag = get_le32(pb);
> + if (tag != MKTAG('A', 'M', 'V', ' '))
> + return -1;
> +
> + avi->fsize = url_fsize(pb);
> + avi->riff_end=avi->fsize;
> +
> + /* first list tag */
> + stream_index = -1;
> + codec_type = -1;
> + frame_period = 0;
> + for(;;) {
> + if (url_feof(pb))
> + goto fail;
> + tag = get_le32(pb);
> + size = get_le32(pb);
> +#ifdef DEBUG
> + print_tag("tag", tag, size);
> +#endif
> +
> + switch(tag) {
> + case MKTAG('L', 'I', 'S', 'T'):
> + /* ignored, except when start of video packets */
> + tag1 = get_le32(pb);
> +#ifdef DEBUG
> + print_tag("list", tag1, 0);
> +#endif
> + if (tag1 == MKTAG('m', 'o', 'v', 'i')) {
> + avi->movi_list = url_ftell(pb) - 4;
> + avi->movi_end = url_fsize(pb);
> +#ifdef DEBUG
> + printf("movi end=%"PRIx64"\n", avi->movi_end);
> +#endif
> + goto end_of_header;
> + }
> + break;
> + case MKTAG('a', 'm', 'v', 'h'):
> + frame_period=get_le32(pb);
> +
> + url_fskip(pb, 7 * 4);
> +
> + width=get_le32(pb);
> + height=get_le32(pb);
> +
> + url_fskip(pb, size - 10 * 4);
> + break;
> + case MKTAG('s', 't', 'r', 'h'):
> + /* stream header */
> +
> + switch(stream_index){
> + case 0:
> + tag1=MKTAG('v','i','d','s');
> + st->codec->stream_codec_tag=MKTAG('A','M','V','V');
> + st->codec->stream_codec_tag=MKTAG('M','J','P','G');
> + codec_type = CODEC_TYPE_VIDEO;
> + ast->sample_size = 0;
> + break;
> + case 1:
> + tag1=MKTAG('a','u','d','s');
> + st->codec->stream_codec_tag=MKTAG('A','I','W','S');
> + codec_type = CODEC_TYPE_AUDIO;
> + break;
> + }
> + stream_index++;
> + st = av_new_stream(s, stream_index);
> + if (!st)
> + goto fail;
> +
> + ast = av_mallocz(sizeof(AVIStream));
> + if (!ast)
> + goto fail;
> + st->priv_data = ast;
> +
> + assert(stream_index < s->nb_streams);
> +
> + if(frame_period){
> + ast->rate = 1000000;
> + ast->scale = frame_period;
> + }else{
> + ast->rate = 25;
> + ast->scale = 1;
> + }
> + av_set_pts_info(st, 64, ast->scale, ast->rate);
> +
> + ast->cum_len=0; /* start */
> + st->start_time = 0;
> + st->duration = 0;
> + ast->sample_size = 0;
> + ast->frame_offset=0;
> +// av_log(NULL, AV_LOG_DEBUG, "%d %d %d %d\n", ast->rate, ast->scale, ast->start, ast->sample_size);
> + url_fskip(pb, size);
> + break;
> + case MKTAG('s', 't', 'r', 'f'):
> + /* stream header */
> + if (stream_index >= (unsigned)s->nb_streams) {
> + url_fskip(pb, size);
> + break;
> + }
> +
> + st = s->streams[stream_index];
> + switch(stream_index) {
> + case 0: //video
> + st->codec->width=width;
> + st->codec->height=height;
> + st->codec->codec_type = CODEC_TYPE_VIDEO;
> + st->codec->codec_tag = MKTAG('A','M','V','V');
> + st->codec->codec_id = CODEC_ID_AMVVIDEO;
> + url_fskip(pb, size);
> + break;
> + case 1: //audio
> + get_wav_header(pb, st->codec, size);
> + if(ast->sample_size && st->codec->block_align && ast->sample_size % st->codec->block_align)
> + av_log(s, AV_LOG_DEBUG, "invalid sample size or block align detected\n");
> + if (size%2) /* 2-aligned (fix for Stargate SG-1 - 3x18 - Shades of Grey.avi) */
> + url_fskip(pb, 1);
> + /* Force parsing as several audio frames can be in
> + * one packet and timestamps refer to packet start*/
> + st->codec->codec_id = CODEC_ID_ADPCM_IMA_WS;
> + st->codec->codec_tag = 0;
> + st->codec->codec_type = CODEC_TYPE_AUDIO;
> + st->need_parsing = AVSTREAM_PARSE_TIMESTAMPS;
> + break;
> + }
> + break;
> + default:
> + if(size > 1000000){
> + av_log(s, AV_LOG_ERROR, "well something went wrong during header parsing, "
> + "ill ignore it and try to continue anyway\n");
> + avi->movi_list = url_ftell(pb) - 4;
> + avi->movi_end = url_fsize(pb);
> + goto end_of_header;
> + }
> + /* skip tag */
> + size += (size & 1);
> + url_fskip(pb, size);
> + break;
> + }
> + }
> + end_of_header:
> + /* check stream number */
> + if (stream_index != s->nb_streams - 1) {
> + fail:
> + for(i=0;i<s->nb_streams;i++) {
> + av_freep(&s->streams[i]->codec->extradata);
> + av_freep(&s->streams[i]);
> + }
> + return -1;
> + }
> +
> + avi->index_loaded = 0;
> + avi->non_interleaved = 0;
> +
> + return 0;
> +}
> static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
> {
> AVIContext *avi = s->priv_data;
> @@ -641,7 +812,7 @@
> //av_log(NULL, AV_LOG_DEBUG, "dts:%"PRId64" offset:%"PRId64" %d/%d smpl_siz:%d base:%d st:%d size:%d\n", pkt->dts, ast->frame_offset, ast->scale, ast->rate, ast->sample_size, AV_TIME_BASE, avi->stream_index, size);
> pkt->stream_index = avi->stream_index;
>
> - if (st->codec->codec_type == CODEC_TYPE_VIDEO) {
> + if (st->codec->codec_type == CODEC_TYPE_VIDEO && st->codec->codec_id!=CODEC_ID_AMVVIDEO) {
> AVIndexEntry *e;
> int index;
> assert(st->index_entries);
> @@ -997,6 +1168,18 @@
> return 0;
> }
>
> +static int amv_probe(AVProbeData *p)
> +{
> + /* check file header */
> + if (p->buf[0] == 'R' && p->buf[1] == 'I' &&
> + p->buf[2] == 'F' && p->buf[3] == 'F' &&
> + p->buf[8] == 'A' && p->buf[9] == 'M' &&
> + p->buf[10] == 'V' && p->buf[11] == ' ')
> + return AVPROBE_SCORE_MAX;
> + else
> + return 0;
> +}
> +
> static int avi_probe(AVProbeData *p)
> {
> /* check file header */
> @@ -1019,3 +1202,14 @@
> avi_read_close,
> avi_read_seek,
> };
> +
> +AVInputFormat amv_demuxer = {
> + "amv",
> + "amv format",
> + sizeof(AVIContext),
> + amv_probe,
> + amv_read_header,
> + avi_read_packet,
> + avi_read_close,
> + avi_read_seek,
> +};
> Index: ../mplayer/libavformat/allformats.c
> ===================================================================
> --- ../mplayer/libavformat/allformats.c (revision 10560)
> +++ ../mplayer/libavformat/allformats.c (working copy)
> @@ -64,6 +64,7 @@
> REGISTER_MUXDEMUX (AU, au);
> REGISTER_MUXDEMUX (AUDIO_BEOS, audio_beos);
> REGISTER_MUXDEMUX (AVI, avi);
> + REGISTER_DEMUXER (AMV, amv);
> #ifdef CONFIG_AVISYNTH
> av_register_input_format(&avisynth_demuxer);
> #endif
>
>
> ------------------------------------------------------------------------
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at mplayerhq.hu
> http://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-devel
More information about the ffmpeg-devel
mailing list