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编解码器如何相关推荐

  1. 【Android FFMPEG 开发】FFMPEG 获取编解码器 ( 获取编解码参数 | 查找编解码器 | 获取编解码器上下文 | 设置上下文参数 | 打开编解码器 )

    文章目录 博客简介 . FFMPEG 编解码器获取流程 I . FFMPEG 获取音视频流的编解码参数 AVCodecParameters *codecpar II . FFMPEG 查找解码器 av ...

  2. 【开源项目】使用FFMPEG解析H264编码为YUV格式

    头文件 #pragma once#ifndef _VIDEO_DECODING_HEADER_ #define _VIDEO_DECODING_HEADER_#define INBUF_SIZE 40 ...

  3. 基于FFMPEG采集摄像头图像编码MP4视频+时间水印

    1.硬件平台 操作系统:Ubuntu18.04 ffmpeg版本:ffmpeg4.2.5 摄像头:电脑自带或USB免驱摄像头 水印处理:avfilter 图像渲染:SDL库   摄像头图像采集+MP4 ...

  4. Ubuntu下mingw和aarch64交叉编译x264/x265/openssl/freetype2的ffmpeg

    1.ffmpeg下载 前往ffmpeg官网下载地址下载需要的最新ffmpeg发布版本 2.ffmpeg编译选项说明 在ffmpeg官网中有对ffmpeg编译选项的部分说明内容如下: --enable- ...

  5. FFmpeg教程【转】

    来自:https://github.com/yinwenjie/FFmpeg_Tutorial FFmpeg工具和sdk库的使用demo 一.使用FFmpeg命令行工具和批处理脚本进行简单的音视频文件 ...

  6. 《FFmpeg Basics》中文版-11-格式之间转换

    正文 ffmpeg工具的最常见用法是从一种音频或视频格式转换为另一种相关的格式. 格式参数在输出文件之前由-f选项设置,或者在输入文件之前也有原始输入,具体的可用格式信息在[显示帮助和功能]一章中列出 ...

  7. FFMPEG采集摄像头图像SDL渲染+MP4格式视频编码

    FFMPEG采集摄像头图像SDL渲染+MP4格式视频编码 FFmpeg是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开源计算机程序.采用LGPL或GPL许可证.它提供了录制.转换以及流化音 ...

  8. VC++实现视频聊天:VFW视频采集+FFmpeg编码

    上次介绍了视频聊天软件的界面.文字聊天.文件传输部分,这此介绍视频聊天功能,这算是音视频领域一个很广的应用.首先视频聊天的双方需要有一个USB摄像头(或者笔记本摄像头),在windows系统下,一个完 ...

  9. FFmpeg Tutorial

    本文转载自Github上面殷汶杰-yinwenjie的一个项目FFmpeg_Tutorial,关于FFMPEG的学习可以参考雷霄骅的CSDN博客https://blog.csdn.net/leixia ...

  10. 嵌入式Linux下基于FFmpeg的视频硬件编解码[图]

    转自:http://tech.c114.net/167/a674033.html 摘要: 对FFmpeg多媒体解决方案中的视频编解码流程进行研究.结合对S3C6410处理器视频硬件编解码方法的分析,阐 ...

最新文章

  1. NodeJS + PhantomJS 前端自动化资源监控
  2. c、c++---linux上的GetTickCount函数
  3. oracle计费系统相关问题,用radius和oracle做认证计费系统
  4. 梯度反向传播求解示例
  5. 专栏 | 基于 Jupyter 的特征工程手册:特征选择(三)
  6. HDU-不容易系列之(3)—— LELE的RPG难题
  7. html效果属性是,htmltransition属性
  8. Excel 2010 下拉菜单的制作方法
  9. Luogu P1471 方差
  10. 【BZOJ 4170】 4170: 极光 (CDQ分治)
  11. 如何用C#在Excel中生成图表?
  12. vaadin_Vaadin提示:以声明方式构建UI
  13. Linux之父和Redis之父,Redis之父:10x程序员应该具备哪些素质
  14. java struts 读取文件,Struts中读写文件的路径问题
  15. Python实战从入门到精通第二十讲——调用父类方法
  16. 在线假单词随机生成器
  17. eclipse启动tomcat报错:org.apache.logging.log4j.web.Log4jServletContextListener
  18. 转《18个实时音视频开发中会用到开源项目》
  19. OMRON_PLC_CP1H_HostLink通讯协议解析
  20. smobiler中实现页面切换_使用Smobiler实现类似美团的界面

热门文章

  1. 浅谈MES系统质量管理功能模块
  2. C# WinForm界面设计教程
  3. 李航统计学习方法 Chapter1 统计学习方法概论
  4. 内录音频工具哪个好 怎么录制电脑音频
  5. 扑克之星网站服务器连不上,扑克之星服务器崩溃一小时引发玩家不满
  6. ODBC、OLE DB、 ADO的区别
  7. PRD产品需求文档原型模版 Axure RP格式
  8. python参考手册下载_Python中文手册【Word版 】
  9. oracle中md5加密解密_Oracle中的MD5加密详解
  10. 前端运行python代码几种方式_前端和python