FFmpeg编解码器如何
FFmpeg編解碼器如何
這頁是FFmpeg中內部編解碼器API的一個介紹。它還將展示編解碼器怎樣和demuxers連接。這不是一個完整的指南但足夠明白怎樣加一個編解碼器到FFmpeg。Cook被用作一個完全例子。
注冊編解碼器
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;
在這我們能看到我們有一些元素來命名編解碼器、它是什么類型(音頻/視頻)、被支持的像素格式和一些初始化/編碼/解碼和關閉的函數指針。現在讓我們看它怎樣被用。
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);
...
此宏擴展到為內部使用注冊一個編解碼器的register_avcodec()調用。注意register_avcodec()將僅當CONFIG_COOK_DECODER被定義時被調用。這允許為一個特殊編解碼器不編譯解碼器代碼。但它被定義在哪?這通過此命令行的配置被提取:
sed -n 's/^[^#]*DEC.*, *\(.*\)).*/\1_decoder/p' libavcodec/allcodecs.c
所以在allcodecs.c中增加一個REGISTER_DECODER(NEW, new)條和重新配置足夠增加需要的定義。現在我們有連接一個編解碼器的一切。
libavcodec/Makefile
在這個文件中我們定義一個編解碼器依賴的對象。例如,cook使用fft和mdct代碼所以它依賴mdct.o和fft.o對象文件以及cook.o對象文件。
...
OBJS-$(CONFIG_COOK_DECODER)??????????? += cook.o mdct.o fft.o
...
FFmpeg demuxer連接
libavformat/rm.c
如果我們認為ffmpeg將處理一個虛構的rm文件則發生的第一件事是它作為一個rm文件被識別。它被傳遞到rm demuxer上(rmdec.c)。rm demuxer審查該文件和發現它是一個cook文件。
...
} else if (!strcmp(buf, "cook")) {
st->codec->codec_id = CODEC_ID_COOK;
...
現在ffmpeg了解要初始化什么編解碼器和發送來自容器的負載到哪。所以回到cook.c和初始化過程。
編解碼器代碼
libavcodec/cook.c Init
ffmpeg知道要使用什么編解碼器后,它調用在編解碼器AVCodec結構中聲明的被聲明的初始化函數指針。在cook.c中它被叫做cook_decode_init。在這在我們開始解碼前我們做我們能做的設置。下面事項應在init中被處理,vlc表初始化、表生成、內存分配和額外數據分析。
libavcodec/cook.c Close
cook_decode_close函數是編解碼器清除調用。所有內存、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) {
...
本函數有5個參數:
??avctx是一個到AVCodecContext的指針
??data是到輸出緩存的指針
??data_size是一個應被設成輸出緩存字節尺寸的變量(這實際是解碼的采樣數*通道數*一個樣本的字節尺寸)
??buf是到輸入緩存的指針
??buf_size是輸入緩存的字節尺寸
解碼函數將返回輸入緩存消耗的字節數或在一個錯誤的情況下返回-1。如果解碼期間沒有錯誤則返回值通常是buf_size as buf應僅包含一幀數據。比特流分析器分解比特流成被用為編解碼器部分的“幀”所以一個到解碼函數的調用消耗不少于來自buf的buf_size。現在鼓勵獨立的碼流分析器。
這就是它怎樣工作沒有太多細節。
Glue編解碼器模板
虛擬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,
};
總結
以上是生活随笔為你收集整理的FFmpeg编解码器如何的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java web开发实战经典 李兴华_M
- 下一篇: 工作308:控制change