[FFmpeg-devel] [PATCH] Mimic decoder

Måns Rullgård mans
Wed Mar 12 00:04:29 CET 2008


Ramiro Polla <ramiro at lisha.ufsc.br> writes:

> Hello,
>
> I have taken libmimic [0] and used it to implement a native Mimic [1]
> decoder for FFmpeg.
>
> The Mimic codec is used by MSN Messenger to transmit Webcam
> conversations (not Video Conversations). It is also supported by aMSN
> and Mercury Messenger. IIRC, those two programs allow to record the
> streamed data. There's also MSN Webcam Recorder [2] that can record
> those streams.
>
> I have the whole history in an SVN server [3] that goes all the way
> back to the first integration that had only minor changes from the
> original code (like glib's ints to C99).
>
> There's one big issue that we discussed some on IRC and I decided to
> take to the list. The original code does lots of deblocking as
> post-processing. Some people on IRC said libavcodec should not do pure
> post-processing (it's not in-loop), and that I should drop that
> code. But pretty much every user that will use this expects the output
> to be the same as in MSN Messenger (which does the deblocking). So, is
> there any chance the deblocking can be accepted in the codec? If so,
> I'd need to clean it up some more.
>
> Ramiro Polla
> [0] http://sourceforge.net/projects/farsight/
> [1] http://wiki.multimedia.cx/index.php?title=Mimic
> [2] http://ml20rc.msnfanatic.com
> [3] http://msnwcrec.googlecode.com
> /*
>  * Copyright (C) 2005  Ole Andr? Vadla Ravn?s <oleavr at gmail.com>
>  * Copyright (C) 2008  Ramiro Polla <ramiro at lisha.ufsc.br>
>  *
>  * 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
>  */
>
> #include <stdlib.h>
> #include <string.h>
> #include <stdint.h>
>
> #include "avcodec.h"
> #include "bitstream.h"
> #include "dsputil.h"
>
> typedef struct {
>     int width;
>     int height;
>     enum PixelFormat pix_fmt;

Why do you duplicate these here?  They are already in AVCodecContext.

>     int f[2];
>
>     int num_vblocks_y;
>     int num_hblocks_y;
>
>     int num_vblocks_cbcr;
>     int num_hblocks_cbcr;
>
>     AVFrame prev_frame;
>
>     uint8_t *swap_buf;
>     int      swap_buf_size;
>
>     int8_t vlcdec_lookup[8][128];
>
>     int ptr_index;
>     AVFrame buf_ptrs[16];
>
>     GetBitContext gb;
>     ScanTable scantable;
>     DSPContext dsp;
>     VLC     vlc1;
>
>     AVFrame picture;
> } MimicContext;
>
> //#define MIMIC_POSTPROC
>
> #ifdef MIMIC_POSTPROC
> static void deblock_horizontal(uint8_t *blocks, unsigned int stride, unsigned int row_count);
> static void deblock_vertical(uint8_t *blocks, unsigned int stride, unsigned int row_count);
>
> static int deblock_h_consider_entire(uint8_t *blocks, unsigned int stride);
> static void deblock_h_do_entire(uint8_t *blocks, unsigned int stride);
> static void deblock_h_do_boundaries(uint8_t *blocks, unsigned int stride);
>
> static int deblock_v_consider_entire(uint8_t *blocks, unsigned int stride);
> static void deblock_v_do_entire(uint8_t *blocks, unsigned int stride);
> static void deblock_v_do_boundaries(uint8_t *blocks, unsigned int stride);
>
> static void deblock(uint8_t *blocks, unsigned int stride, unsigned int row_count);
> #endif
>
> static void bswap_buf(uint8_t *newbuf, const uint8_t *oldbuf, int bufsize)
> {
>     int i;
>     for(i = 0 ; i < (bufsize >> 2) ; i++)
>         ((uint32_t*) newbuf)[i] = bswap_32(((const uint32_t*) oldbuf)[i]);
> }

Use DSPContext.bswap_buf().

> static const uint8_t magic_values[][2] = {
>     {  0, 1 }, {  0, 2 }, {  0, 3 }, {  0, 0 }, {  1, 1 }, {  0, 4 },
>     {  0, 5 }, {  2, 1 }, {  3, 1 }, {  1, 2 }, {  1, 3 }, {  0, 6 },
>     {  4, 1 }, {  5, 1 }, {  6, 1 }, {  2, 2 }, {  1, 4 }, {  7, 1 },
>     {  8, 1 }, {  3, 2 }, {  4, 2 }, {  5, 2 }, {  2, 3 }, {  2, 4 },
>     {  1, 5 }, {  1, 6 }, {  0, 7 }, {  9, 1 }, { 10, 1 }, { 11, 1 },
>     { 12, 1 }, { 13, 1 }, { 14, 1 }, { 15, 1 }, {  6, 2 }, {  7, 2 },
>     {  8, 2 }, {  9, 2 }, { 10, 2 }, { 11, 2 }, { 12, 2 }, { 13, 2 },
>     { 14, 2 }, {  3, 3 }, {  4, 3 }, {  5, 3 }, {  6, 3 }, {  7, 3 },
>     {  8, 3 }, {  9, 3 }, { 10, 3 }, { 11, 3 }, { 12, 3 }, { 13, 3 },
>     { 14, 3 }, {  3, 4 }, {  4, 4 }, {  5, 4 }, {  6, 4 }, {  7, 4 },
>     {  8, 4 }, {  9, 4 }, { 10, 4 }, { 11, 4 }, { 12, 4 }, { 13, 4 },
>     { 14, 4 }, {  2, 5 }, {  3, 5 }, {  4, 5 }, {  5, 5 }, {  6, 5 },
>     {  7, 5 }, {  8, 5 }, {  9, 5 }, { 10, 5 }, { 11, 5 }, { 12, 5 },
>     { 13, 5 }, { 14, 5 }, {  2, 6 }, {  3, 6 }, {  4, 6 }, {  5, 6 },
>     {  6, 6 }, {  7, 6 }, {  8, 6 }, {  9, 6 }, { 10, 6 }, { 11, 6 },
>     { 12, 6 }, { 13, 6 }, { 14, 6 }, {  1, 7 }, {  2, 7 }, {  3, 7 },
>     {  4, 7 }, {  5, 7 }, {  6, 7 }, {  7, 7 }, {  8, 7 }, {  9, 7 },
>     { 10, 7 }, { 11, 7 }, { 12, 7 }, { 13, 7 }, { 14, 7 },
> };
>
> /*
>  * vlc_decode_block
>  *
>  * De-serialize (reconstruct) a variable length coded 8x8 block.
>  */
> static int vlc_decode_block(MimicContext *ctx, DCTELEM *block, int num_coeffs)
> {
>     unsigned int pos;
>
>     memset(block, 0, 64 * sizeof(DCTELEM));
>
>     /* The DC-value is read in as is. */
>     block[0] = get_bits(&ctx->gb, 8);
>
>     for(pos = 1; pos < num_coeffs; pos++) {
>         uint32_t vlc, num_bits;
>         int value;
>
>         vlc = get_vlc2(&ctx->gb, ctx->vlc1.table, ctx->vlc1.bits, 4);
>         if(vlc == -1)
>             return 0;
>         if(vlc == 3) /* end-of-block code */
>             return 1;
>
>         pos +=     magic_values[vlc][0];
>         num_bits = magic_values[vlc][1];
>
>         value = get_bits(&ctx->gb, num_bits);
>
>         block[ctx->scantable.permutated[pos]] = ctx->vlcdec_lookup[num_bits][value];
>     }

It would be nice if you could somehow avoid that memset().  Do you
have any idea how many coefficients are typically coded?  Maybe it's
few enough that it doesn't matter.

>     return 1;
> }
>
> static void dequant_block(MimicContext *ctx, DCTELEM *block, int is_chrom)
> {
>     int i, f = ctx->f[is_chrom];

Why don't you make 'f' an argument to this function instead?

>     /* FFmpeg's IDCT behaves somewhat different from the original code, so
>      * a factor of 4 was added to the input */
>
>     /* De-quantize. */
>     block[0] <<= 3;
>     block[1] <<= 4;
>     block[8] <<= 4;
>
>     for(i = 2; i < 64; i++) {
>         if(i == 8)
>             continue;
>
>         /* TODO Use >> 10 instead of / 1001 */
>         block[i] = (block[i] * f) / 1001;
>     }
> }

I doubt this works properly with permuted blocks.

> /*
>  * decode_main
>  *
>  * Main decoding loop.
>  */
> static int decode(MimicContext *ctx, int quality, int num_coeffs, int is_pframe)
> {
>     int y, x, i, chrom_ch, offset;
>     DECLARE_ALIGNED_16(DCTELEM, dct_block[64]);
>     uint8_t *src, *dst, *p;
>     uint8_t *base_src, *base_dst;
>     uint32_t bit;
>     int y_stride = ctx->picture.linesize[0];
>     int crcb_stride = ctx->picture.linesize[1];
>     int crcb_size = ctx->picture.linesize[1] * (ctx->height>>1);
>
>     /* Clear Cr and Cb planes. */
>     memset(ctx->picture.data[1], 128, crcb_size);
>     memset(ctx->picture.data[2], 128, crcb_size);

Why?

>     ctx->f[0] = av_clip(10000-quality, 1000, 10000)<<2;
>     ctx->f[1] = av_clip(10000-quality, 2000, 10000)<<2;
>
>     /* Decode Y plane. */
>     for(y = ctx->num_vblocks_y - 1; y >= 0 ; y--) {

What is it with Microsoft and inverted images?

>         offset = (y_stride << 3) * y;
>         src = ctx->prev_frame.data[0] + offset;
>         dst = ctx->picture.data[0]  + offset;
>         for(x = 0; x < ctx->num_hblocks_y; x++) {
>             /* Check for a change condition in the current block. */
>             bit = is_pframe ? get_bits1(&ctx->gb) : 0;
>             if(!bit) {
>                 /* Yes: Is the new content the same as it was in one of
>                  * the 15 last frames preceding the previous? */
>                 if(is_pframe)
>                     bit = get_bits1(&ctx->gb);

This looks like it could be a case for decode012(), not sure.

>                 if(!bit) {
>                     /* No: decode it. */
>                     if(!vlc_decode_block(ctx, dct_block, num_coeffs)) {
>                         /* Corruped frame, return. */
>                         return 0;
>                     }
>                     dequant_block(ctx, dct_block, 0);
>                     ctx->dsp.idct_put(dst+7*y_stride, -y_stride, dct_block);
>                 } else {
>                     uint32_t backref;

Make that a plain 'unsigned'.  There is no need to force exactly 32 bits.

>                     /* Yes: read the backreference (4 bits) and copy. */
>                     backref = get_bits(&ctx->gb, 4);
>                     p = ctx->buf_ptrs[(ctx->ptr_index + backref) % 16].data[0];

Do you trust the compiler with the % operator?  Better use & 15 instead.

>                     p += offset + (x << 3);
>                     for(i = 7; i >= 0; i--) {
>                         int new_offset = y_stride * i;
>                         memcpy(dst + new_offset, p + new_offset, 8);
>                     }

for (i = 0; i < 8; i++) {
    memcpy(dst + offset, p + offset, 8);
    offset += y_stride;
}

>                 }
>             } else {
>                 /* No change no worries: just copy from the previous frame. */
>                 for(i = 7; i >= 0; i--) {
>                     int new_offset = y_stride * i;
>                     memcpy(dst + new_offset, src + new_offset, 8);
>                 }

Can you think of a nice way to combine those two identical for()
loops?

>             }
>             src += 8;
>             dst += 8;
>         }
>     }
>
>     /* Decode Cr and Cb planes. */
>     for(chrom_ch = 0; chrom_ch < 2; chrom_ch++) {
>         base_src = (chrom_ch ? ctx->prev_frame.data[1] : ctx->prev_frame.data[2]);
>         base_dst = (chrom_ch ? ctx->picture.data[1] : ctx->picture.data[2]);

for (chrom_ch = 1; chrom_ch <= 2; chrom_ch++) {
    base_dst = ctx->prev_frame.data[3-chrom_ch];
    ...

>         for(y = ctx->num_vblocks_cbcr - 1; y >= 0 ; y--) {
>             unsigned int num_rows = 8;
>             /* The last row of blocks in chrominance for 160x120 resolution
>              * is half the normal height and must be accounted for. */
>             if(y + 1 == ctx->num_vblocks_cbcr && ctx->height % 16)
>                 num_rows = 4;

How does this interact with 8x8 IDCT blocks?
And never use the % operator.

>             offset = (crcb_stride << 3) * y;
>             src = base_src + offset;
>             dst = base_dst + offset;
>             for(x = 0; x < ctx->num_hblocks_cbcr; x++) {
>                 /* Check for a change condition in the current block. */
>                 bit = is_pframe ? get_bits1(&ctx->gb) : 1;
>                 if(bit == 1) {
>                     /* Yes: decode it. */
>                     if(!vlc_decode_block(ctx, dct_block, num_coeffs)) {
>                         /* Corrupted frame: clear Cr and Cb planes and return. */
>                         memset(ctx->picture.data[1], 128, crcb_size);
>                         memset(ctx->picture.data[2], 128, crcb_size);

Why?

>                         return 0;
>                     }
>                     dequant_block(ctx, dct_block, 1);
>                     ctx->dsp.idct_put(dst+7*crcb_stride, -crcb_stride, dct_block);
>                 } else {
>                     /* No change no worries: just copy from the previous frame. */
>                     for(i = num_rows-1; i >= 0; i--) {
>                         int new_offset = crcb_stride * i;
>                         memcpy(dst + new_offset, src + new_offset, 8);
>                     }
>                 }
>                 src += 8;
>                 dst += 8;
>             }
>         }
>     }

The chroma code is very similar to the luma code.  No chance they
could be merged?

>     /*
>      * Make a copy of the current frame and store in
>      * the circular pointer list of 16 entries.
>      */
>     ctx->prev_frame = ctx->buf_ptrs[ctx->ptr_index];
>
>     av_picture_copy((AVPicture*)&ctx->prev_frame, (AVPicture*)&ctx->picture,
>                     ctx->pix_fmt, ctx->width, ctx->height);
>
>     if(--ctx->ptr_index < 0)
>         ctx->ptr_index = 15;

This isn't speed critical, but ctx->ptr_index--; ctx->ptr_index &= 15
is faster.

> #ifdef MIMIC_POSTPROC
>     /* Perform deblocking on all planes. */
>     deblock(ctx->picture.data[0], y_stride, ctx->height);
>     deblock(ctx->picture.data[1], crcb_stride, ctx->height>>1);
>     deblock(ctx->picture.data[2], crcb_stride, ctx->height>>1);
> #endif
>
>     return 1;
> }
>
> #ifdef MIMIC_POSTPROC

[...]

Not commenting on this for now.

> #endif
>
> static const uint8_t col_zag[64] = {
>      0,  8,  1,  2,  9, 16, 24, 17,
>     10,  3,  4, 11, 18, 25, 32, 40,
>     33, 26, 19, 12,  5,  6, 13, 20,
>     27, 34, 41, 48, 56, 49, 42, 35,
>     28, 21, 14,  7, 15, 22, 29, 36,
>     43, 50, 57, 58, 51, 44, 37, 30,
>     23, 31, 38, 45, 52, 59, 39, 46,
>     53, 60, 61, 54, 47, 55, 62, 63
> };
>
> static const uint32_t huffcodes[] = {
>     0x00000000, 0x00000001, 0x00000004, 0x0000000a, 0x0000000b, 0x0000000c,
>     0x0000001a, 0x0000001b, 0x00000038, 0x00000039, 0x0000003a, 0x0000003b,
>     0x00000078, 0x00000079, 0x0000007a, 0x0000007b, 0x000000f8, 0x000000f9,
>     0x000000fa, 0x000000fb, 0x000001f8, 0x000001f9, 0x000001fa, 0x000001fb,
>     0x000003f8, 0x000003f9, 0x000003fa, 0x000003fb, 0x000007f8, 0x000007f9,
>     0x000007fa, 0x000007fb, 0x00000ff8, 0x00000ff9, 0x00000ffa, 0x00000ffb,
>     0x00001ff8, 0x00001ff9, 0x00001ffa, 0x00001ffb, 0x00003ff8, 0x00003ff9,
>     0x00003ffa, 0x00003ffb, 0x00007ff8, 0x00007ff9, 0x00007ffa, 0x00007ffb,
>     0x0000fff8, 0x0000fff9, 0x0000fffa, 0x0000fffb, 0x0001fff8, 0x0001fff9,
>     0x0001fffa, 0x0001fffb, 0x0003fff8, 0x0003fff9, 0x0003fffa, 0x0003fffb,
>     0x0007fff8, 0x0007fff9, 0x0007fffa, 0x0007fffb, 0x000ffff8, 0x000ffff9,
>     0x000ffffa, 0x000ffffb, 0x001ffff8, 0x001ffff9, 0x001ffffa, 0x001ffffb,
>     0x003ffff8, 0x003ffff9, 0x003ffffa, 0x003ffffb, 0x007ffff8, 0x007ffff9,
>     0x007ffffa, 0x007ffffb, 0x00fffff8, 0x00fffff9, 0x00fffffa, 0x00fffffb,
>     0x01fffff8, 0x01fffff9, 0x01fffffa, 0x01fffffb, 0x03fffff8, 0x03fffff9,
>     0x03fffffa, 0x03fffffb, 0x07fffff8, 0x07fffff9, 0x07fffffa, 0x07fffffb,
>     0x0ffffff8, 0x0ffffff9, 0x0ffffffa, 0x0ffffffb, 0x1ffffff8, 0x1ffffff9,
>     0x1ffffffa, 0x1ffffffb, 0x3ffffff8, 0x3ffffff9, 0x3ffffffa,
> };
>
> static const uint8_t huffbits[] = {
>      2,  2,  3,  4,  4,  4,  5,  5,  6,  6,  6,  6,
>      7,  7,  7,  7,  8,  8,  8,  8,  9,  9,  9,  9,
>     10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12,
>     13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15,
>     16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
>     19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21,
>     22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24,
>     25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27,
>     28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30,
> };

Those last two tables look like they could be computed by some simple
expressions (which I am too lazy to work out right now).

> /*
>  * initialize_vlcdec_lookup
>  *
>  * Internal helper-function used to initialize
>  * the lookup-table used by the VLC-decoder.
>  */
> static void initialize_vlcdec_lookup(int8_t lookup_tbl[8][128])
> {
>     int i, j;
>
>     for(i = 1 ; i < 8 ; i++) {
>         int first = (1<<i)-1;
>         int last = 1<<(i-1);
>         int cur = first;
>
>         for(j = 0 ; cur >= last ; cur--) {
>             lookup_tbl[i][j++] = -cur;
>             lookup_tbl[i][j++] =  cur;
>         }
>     }
> }
>
> static int mimic_decode_init(AVCodecContext *avctx)
> {
>     MimicContext *ctx = avctx->priv_data;
>     int width, height;
>     int i;
>
>     if(!avctx->extradata) {
>         av_log(avctx, AV_LOG_ERROR, "extradata missing!\n");
>         return -1;
>     }

You should check the size of extradata too.

>     width  = AV_RL16(avctx->extradata + 4);
>     height = AV_RL16(avctx->extradata + 6);
>
>     if(!(width == 160 && height == 120) && !(width == 320 && height == 240)) {
>         av_log(avctx, AV_LOG_ERROR, "invalid width/height!\n");
>         return -1;
>     }
>
>     ctx->width  = avctx->width  = width ;
>     ctx->height = avctx->height = height;
>     ctx->pix_fmt = avctx->pix_fmt = PIX_FMT_YUV420P;

Why?

>     ctx->num_vblocks_y = ctx->height >> 3;
>     ctx->num_hblocks_y = ctx->width  >> 3;
>     ctx->num_vblocks_cbcr = ctx->height >> 4;
>     ctx->num_hblocks_cbcr = ctx->width  >> 4;
>     if(ctx->height % 16)
>         ctx->num_vblocks_cbcr++;

ctx->num_vblocks_cbcr += !!(ctx->height & 15), if you like a little
obfuscation.  In a speed-critical place, it would probably be faster.

>     ctx->ptr_index = 15;
>
>     for(i = 0; i < 16; i++) {
>         if(avctx->get_buffer(avctx, &ctx->buf_ptrs[i])) {
>             av_log(avctx, AV_LOG_ERROR, "could not get buffer\n");
>             return -1;
>         }
>     }
>
>     initialize_vlcdec_lookup(ctx->vlcdec_lookup);
>     if(init_vlc(&ctx->vlc1, 8, sizeof(huffbits)/sizeof(huffbits[0]),
>                  huffbits, 1, 1, huffcodes, 4, 4, 0)) {
>         av_log(avctx, AV_LOG_ERROR, "error initializing vlc table\n");
>         return -1;
>     }
>     dsputil_init(&ctx->dsp, avctx);
>     ff_init_scantable(ctx->dsp.idct_permutation, &ctx->scantable, col_zag);
>     ctx->picture.data[0] = NULL;
>
>     return 0;
> }
>
> static int mimic_decode_frame(AVCodecContext *avctx, void *data,
>                               int *data_size, const uint8_t *buf, int buf_size)
> {
>     MimicContext *ctx = avctx->priv_data;
>     int is_pframe;
>     int width, height;
>     int quality, num_coeffs;
>
>     if(ctx->picture.data[0]) {
>         avctx->release_buffer(avctx, &ctx->picture);
>     }
>
>     if(avctx->get_buffer(avctx, &ctx->picture)) {
>         av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
>         return -1;
>     }
>
>     width  = AV_RL16(buf + 4);
>     height = AV_RL16(buf + 6);
>
>     if(width != ctx->width || height != ctx->height) {
>         av_log(avctx, AV_LOG_ERROR, "resolution changing is not supported\n");
>         return -1;
>     }
>
>     quality = AV_RL16(buf + 2);
>     is_pframe = AV_RL32(buf + 12);
>     num_coeffs = buf[16];

You should check the buffer size before reading.

>     if(!(is_pframe && !ctx->prev_frame.data[0])) {
>         int swap_buf_size = buf_size - 20;
>         ctx->swap_buf = av_fast_realloc(ctx->swap_buf, &ctx->swap_buf_size,
>                                  swap_buf_size + FF_INPUT_BUFFER_PADDING_SIZE);
>         if(!ctx->swap_buf)
>             return AVERROR_NOMEM;
>
>         /* TODO find out why dsp.bswap_buf crashes */

Did you ask Valgrind?

>         bswap_buf(ctx->swap_buf, buf + 20, swap_buf_size);
>         init_get_bits(&ctx->gb, ctx->swap_buf, swap_buf_size << 3);
>
>         if(!decode(ctx, quality, num_coeffs, is_pframe))
>             return -1;
>     } else {
>         av_log(avctx, AV_LOG_ERROR, "decoding must start with keyframe\n");
>         return -1;
>     }
>
>     ctx->picture.pict_type = is_pframe ? FF_P_TYPE : FF_I_TYPE;
>     *(AVFrame*)data = ctx->picture;
>     *data_size = sizeof(AVFrame);
>
>     return buf_size;
> }
>
> static int mimic_decode_end(AVCodecContext *avctx)
> {
>     MimicContext *ctx = avctx->priv_data;
>     int i;
>
>     if(ctx->swap_buf)
>         av_free(ctx->swap_buf);

Useless if().

>     for(i = 0; i < 16; i++)
>         avctx->release_buffer(avctx, &ctx->buf_ptrs[i]);
>     free_vlc(&ctx->vlc1);
>     if(ctx->picture.data[0])
>         avctx->release_buffer(avctx, &ctx->picture);
>
>     return 0;
> }
>
> AVCodec mimic_decoder = {
>     "mimic",
>     CODEC_TYPE_VIDEO,
>     CODEC_ID_MIMIC,
>     sizeof(MimicContext),
>     mimic_decode_init,
>     NULL,
>     mimic_decode_end,
>     mimic_decode_frame
> };
> Index: libavcodec/Makefile
> ===================================================================
> --- libavcodec/Makefile	(revision 12363)
> +++ libavcodec/Makefile	(working copy)
> @@ -100,6 +100,7 @@
>  OBJS-$(CONFIG_LOCO_DECODER)            += loco.o golomb.o
>  OBJS-$(CONFIG_MACE3_DECODER)           += mace.o
>  OBJS-$(CONFIG_MACE6_DECODER)           += mace.o
> +OBJS-$(CONFIG_MIMIC_DECODER)           += mimic.o
>  OBJS-$(CONFIG_MJPEG_DECODER)           += mjpegdec.o mjpeg.o
>  OBJS-$(CONFIG_MJPEG_ENCODER)           += mjpegenc.o mjpeg.o mpegvideo_enc.o motion_est.o ratecontrol.o mpeg12data.o mpegvideo.o
>  OBJS-$(CONFIG_MJPEGB_DECODER)          += mjpegbdec.o mjpegdec.o mjpeg.o
> Index: libavcodec/allcodecs.c
> ===================================================================
> --- libavcodec/allcodecs.c	(revision 12363)
> +++ libavcodec/allcodecs.c	(working copy)
> @@ -101,6 +101,7 @@
>      REGISTER_ENCODER (LJPEG, ljpeg);
>      REGISTER_DECODER (LOCO, loco);
>      REGISTER_DECODER (MDEC, mdec);
> +    REGISTER_DECODER (MIMIC, mimic);
>      REGISTER_ENCDEC  (MJPEG, mjpeg);
>      REGISTER_DECODER (MJPEGB, mjpegb);
>      REGISTER_DECODER (MMVIDEO, mmvideo);
> Index: libavcodec/avcodec.h
> ===================================================================
> --- libavcodec/avcodec.h	(revision 12363)
> +++ libavcodec/avcodec.h	(working copy)
> @@ -178,6 +178,7 @@
>      CODEC_ID_SUNRAST,
>      CODEC_ID_INDEO4,
>      CODEC_ID_INDEO5,
> +    CODEC_ID_MIMIC,
>
>      /* various PCM "codecs" */
>      CODEC_ID_PCM_S16LE= 0x10000,

Last 3 files OK.

-- 
M?ns Rullg?rd
mans at mansr.com




More information about the ffmpeg-devel mailing list