FFmpeg codec HOWTO(http://wiki.multimedia.cx/index.php?title=FFmpeg_codec_HOWTO)

来源:互联网 发布:json对象合并 编辑:程序博客网 时间:2024/04/29 17:07

FFmpeg codec HOWTO

这篇文字意在对FFmpeg内置编解码库的API做一个入门级介绍。其中也将告诉大家编解码库是如何同demuxers(初步学习,认为是FFmpeg的接口之一)链接的。这无意于提供一个完整的指导,但是对于了解如何将一个编解码库加入到FFmpeg是足够的。

registering the codec

libavcodec/avcodec.h

首先看AVCodec结构体。

typedef struct AVCodec {    const char *name;    enum CodecType type;    enum CodecID id;    int priv_data_size;    int (*init)(AVCodecContext *);    int (*encode)(AVCodecContext *, uint8_t *buf, int buf_size, void *data);    int (*close)(AVCodecContext *);    int (*decode)(AVCodecContext *, void *outdata, int *outdata_size,                  uint8_t *buf, int buf_size);    int capabilities;    struct AVCodec *next;    void (*flush)(AVCodecContext *);    const AVRational *supported_framerates; ///array of supported framerates, or NULL if any, array is terminated by {0,0}    const enum PixelFormat *pix_fmts;       ///array of supported pixel formats, or NULL if unknown, array is terminanted by -1} AVCodec;

上边结构体里存在一些元素用于命名编解码器,比如它的类型(视频还是音频),所支持的像素格式以及一些init/encode/decode/close功能函数的指针。

libavcodec/cook.c

当我们查看文件的末端,我们将看到以下代码:

AVCodec cook_decoder ={    .name           = "cook",    .type           = CODEC_TYPE_AUDIO,    .id             = CODEC_ID_COOK,    .priv_data_size = sizeof(COOKContext),    .init           = cook_decode_init,    .close          = cook_decode_close,    .decode         = cook_decode_frame,};

首先我们得到了一个名为cook_decoder的AVCodec对象。然后我们设置cook_decoder的变量,但只是设置了必须设定的变量。目前没有一个编码器于是我们不需要设置。我们考虑其中的id变量,CODEC_ID_COOK不再libavcodec/cook.c中定义,而是在avcodec.h。

libavcodec/avcodec.h

我们会看到CodecID的枚举变量

enum CodecID {...CODEC_ID_GSM,CODEC_ID_QDM2,CODEC_ID_COOK,CODEC_ID_TRUESPEECH,CODEC_ID_TTA,...};

CODEC_ID_COOK位列其中。列表中是所有在FFmpeg中支持的编解码器,这份列表被修正过而且也内置使用在每一份编解码器的id。更改上述顺序会打破二进制兼容性。

目前对于申明一个编解码器已经足够。现在我们需要注册它们用于内置使用。

libavcodec/allcodecs.c

该文件中存在avcodec_register_all()函数,在所有的编解码器中,它都这样被调用。

...    REGISTER_DECODER(COOK, cook);...

这个宏延伸到一个avcodec_register_all()调用,该调用注册一个编解码器为内置使用。考虑到register_avcodec()仅在CONFIG_COOK_DECODER被定义时才被调用。这允许可以为一段特定的编码器而不必编译解码器。但是它在哪里定义的?这是由以下命令行抽取配置的:

sed -n 's/^[^#]*DEC.*, *\(.*\)).*/\1_decoder/p' libavcodec/allcodecs.c

所以添加REGISTER_DECODER(NEW, new)的入口到allcodecs.c和重复配置对于添加需要的定义是足够的。现在我们已经有了连接一个编解码器的所有准备。

libavcodec/Makefile

这个文件中我们定义了一个编解码器所基于的对象。比如,cook使用fft和mdct代码,于是它就基于mdct.o、fft.o、cook.o的对象文件。

...OBJS-$(CONFIG_COOK_DECODER)            += cook.o mdct.o fft.o...

Codec code

libavcodec/cook.c Init

在确立了编解码器的用途之后,ffmpeg调用已在AVCodec结构体中申明好的初始化函数指针。在cook.c中它被称作cook_decode_init。在我们开始解码之前,尽可能多的初始化,接下来的几项将在init, vlc表的初始化、表的更新、存储的分配以及extradata的parsing中得到处理。

libavcodec/cook.c Close

这个cook_decode_close函数是编解码器clean-up的调用。所有的存储空间、vlc表格等都会再此被释放。

libavcodec/cook.c Decode

在cook.c中解码调用函数名cook_decode_frame。

static int cook_decode_frame(AVCodecContext *avctx,            void *data, int *data_size,            uint8_t *buf, int buf_size) {...

这个函数有五个参数:

  • avctx是AVCodecContext的指针
  • data是output buffer的指针
  • data_size 是个需要为output buffer size 用bytes设置的变量(通常是解码样例的数量*包含的管道数*每个样例的字节大小)
  • buf是input buffer的指针
  • buf_size是input buffer的字节大小
这个解码功能函数将返回来自input buffer的消耗字节数或者在出现error情况下返回-1。如果在解码过程中没有出现error,返回值通常是buf_size作为buf且只包含一帧数据。比特流剖析器将比特流分离成“帧”,它曾作为编解码器的一部分,因而对解码函数的一次调用的消耗少于来自buf的buf_size字节数。现在鼓励分开比特流剖析器。

The Glue codec template

虚构的Glue视频编解码器将作为展示比特流读取、vlc解码等的基础。这段代码纯粹虚构的并纯粹有时为样例而写的。没有尝试着抑制无效数据的操作。

Glue编解码器如下:

/* The following includes have the bitstream reader, various dsp functions and the various defaults */#define ALT_BITSTREAM_READER#include "avcodec.h"#include "bitstream.h"#include "dsputil.h"/* This includes the tables needed for the Glue codec template */#include "gluedata.h"/* Here we declare the struct used for the codec private data */typedef struct {    GetBitContext       gb;    FFTContext          fft_ctx;    VLC                 vlc_table;    MDCTContext         mdct_ctx;    float*              sample_buffer;} GLUEContext;/* The init function */static int glue_decode_init(AVCodecContext *avctx){    GLUEContext *q = avctx->priv_data;    /* This imaginary codec uses one fft, one mdct and one vlc table. */    ff_mdct_init(&q->mdct_ctx, 10, 1);    // 2^10 == size of mdct, 1 == inverse mdct    ff_fft_init(&q->fft_ctx, 9, 1);       // 2^9 == size of fft, 0 == inverse fft    init_vlc (&q->vlc_table, 9, 24,           vlctable_huffbits, 1, 1,           vlctable_huffcodes, 2, 2, 0);  // look in bitstream.h for the meaning of the arguments    /* We also need to allocate a sample buffer */    q->sample_buffer = av_mallocz(sizeof(float)*1024);  // here we used av_mallocz instead of av_malloc                                                        // av_mallocz memsets the whole buffer to 0    /* Check if the allocation was successful */    if(q->sample_buffer == NULL)        return -1;    /* return 0 for a successful init, -1 for failure */    return 0;}/* This is the main decode function */static int glue_decode_frame(AVCodecContext *avctx,           void *data, int *data_size,           uint8_t *buf, int buf_size){    GLUEContext *q = avctx->priv_data;    int16_t *outbuffer = data;    /* We know what the arguments for this function are from above       now we just have to decode this imaginary codec, the made up       bitstream format is as follows:       12 bits representing the amount of samples       1 bit fft or mdct coded coeffs, 0 for fft/1 for mdct         read 13 bits representing the amount of vlc coded fft data coeffs         read 10 bits representing the amount of vlc coded mdct data coeffs       (...bits representing the coeffs...)       5 bits of dummy data that should be ignored       32 bits the hex value 0x12345678, used for integrity check    */    /* Declare the needed variables */    int samples, coeffs, i, fft;    float mdct_tmp[1024];    /* Now we init the bitstream reader, we start at the beginning of the inbuffer */    init_get_bits(&q->gb, buf, buf_size*8);  //the buf_size is in bytes but we need bits    /* Now we take 12 bits to get the amount of samples the current frame has */    samples = get_bits(&q->gb, 12);        /* Now we check if we have fft or mdct coeffs */    fft = get_bits1(&q->gb);    if (fft) {        //fft coeffs, get how many        coeffs = get_bits(&q->gb, 13);    } else {        //mdct coeffs, get how many        coeffs = get_bits(&q->gb, 10);    }    /* Now decode the vlc coded coeffs to the sample_buffer */    for (i=0 ; i<coeffs ; i++)        q->sample_buffer[i] = get_vlc2(&q->gb, q->vlc_table.table, vlc_table.bits, 3);  //read about the arguments in bitstream.h    /* Now we need to transform the coeffs to samples */    if (fft) {        //The fft is done inplace        ff_fft_permute(&q->fft_ctx, (FFTComplex *) q->sample_buffer);        ff_fft_calc(&q->fft_ctx, (FFTComplex *) q->sample_buffer);    } else {        //And we pretend that the mdct is also inplace        ff_imdct_calc(&q->mdct_ctx, q->sample_buffer, q->sample_buffer, mdct_tmp);    }    /* To make it easy the stream can only be 16 bits mono, so let's convert it to that */    for (i=0 ; i<samples ; i++)        outbuffer[i] = (int16_t)q->sample_buffer[i];    /* Report how many samples we got */    *data_size = samples;    /* Skip the dummy data bits */    skip_bits(&q->gb, 5);    /* Check if the buffer was consumed ok */    if (get_bits(&q->gb,32) != 0x12345678) {        av_log(avctx,AV_LOG_ERROR,"Stream error, integrity check failed!\n");        return -1;    }    /* The decision between erroring out or not in case of unexpected data       should be made so that the output quality is maximized.       This means that if undamaged data is assumed then unused/resereved values       should lead to warnings but not failure. (assumption of slightly non compliant       file)       OTOH if possibly damaged data is assumed and it is assumed that the original       did contain specific values in reserved/unused fields then finding unexpected       values should trigger error concealment code and the decoder/demuxer should       attempt to resync.       The decision between these 2 should be made by using        AVCodecContext.error_recognition unless its a clear case where only one of       the 2 makes sense.    */    /* Return the amount of bytes consumed if everything was ok */    return *data_size*sizeof(int16_t);}/* the uninit function, here we just do the inverse of the init */ static int glue_decode_close(AVCodecContext *avctx){    GLUEContext *q = avctx->priv_data;    /* Free allocated memory buffer */    av_free(q->sample_buffer);    /* Free the fft transform */    ff_fft_end(&q->fft_ctx);    /* Free the mdct transform */    ff_mdct_end(&q->mdct_ctx);    /* Free the vlc table */    free_vlc(&q->vlc_table);    /* Return 0 if everything is ok, -1 if not */    return 0;}AVCodec glue_decoder ={    .name           = "glue",    .type           = CODEC_TYPE_AUDIO,    .id             = CODEC_ID_GLUE,    .priv_data_size = sizeof(GLUEContext),    .init           = glue_decode_init,    .close          = glue_decode_close,    .decode         = glue_decode_frame,};


原创粉丝点击