[时间:2016-07] [状态:Open]

[关键词:ffmpeg,libavcodec,libavformat]

FFmpeg接触几年了,用的比较多的是libavcodec和libavformat两个库,偶尔也会用用libswresample(主要处理音频PCM的转换,比如不同的声道数、频率、采样位数、量化位数转换)和libswscale(视频原始数据处理,比如缩放、色度格式、量化位数的转换)。

libavformat的主要机制

libavformat主要完成针对多媒体文件或流媒体(FFmpeg内部成为URL)的数据解析,包括数据读取、格式分析以及包读取;多媒体文件生成。其中主要包含几种主要的结构体:

  • AVFormatContext

    最核心的结构体,对于每个URL,这里面都会demuxer/muxer,一个AVStream数组以及一个AVIOContext。
  • AVStream

    记录媒体文件中包含的流信息,比如音频、视频或者数据流及其类型。
  • AVInputFormat(demuxer)

    作为解析器,是libavformat中很多的结构,其对外接口如下:
int(* read_probe )(AVProbeData *)
Tell if a given file has a chance of being parsed as this format. int(* read_header )(struct AVFormatContext *)
Read the format header and initialize the AVFormatContext structure. int(* read_packet )(struct AVFormatContext *, AVPacket *pkt)
Read one packet and put it in 'pkt'. int(* read_close )(struct AVFormatContext *)
Close the stream. int(* read_seek )(struct AVFormatContext *, int stream_index, int64_t timestamp, int flags)
Seek to a given timestamp relative to the frames in stream component stream_index. int64_t(* read_timestamp )(struct AVFormatContext *s, int stream_index, int64_t *pos, int64_t pos_limit)
Get the next timestamp in stream[stream_index].time_base units.
  • AVOutputFormat(muxer)

    作为复用器,其对外接口如下:
int(* 	write_header )(struct AVFormatContext *)

int(* 	write_packet )(struct AVFormatContext *, AVPacket *pkt)

int(* 	write_trailer )(struct AVFormatContext *)

int(* 	interleave_packet )(struct AVFormatContext *, AVPacket *out, AVPacket *in, int flush)
Currently only used to set pixel format if not YUV420P. int(* query_codec )(enum AVCodecID id, int std_compliance)
Test if the given codec can be stored in this container. void(* get_output_timestamp )(struct AVFormatContext *s, int stream, int64_t *dts, int64_t *wall) int(* control_message )(struct AVFormatContext *s, int type, void *data, size_t data_size)
Allows sending messages from application to device. int(* write_uncoded_frame )(struct AVFormatContext *, int stream_index, AVFrame **frame, unsigned flags)
Write an uncoded AVFrame. int(* get_device_list )(struct AVFormatContext *s, struct AVDeviceInfoList *device_list)
Returns device list with it properties. int(* create_device_capabilities )(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps)
Initialize device capabilities submodule. int(* free_device_capabilities )(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps)
Free device capabilities submodule. int(* init )(struct AVFormatContext *)
Initialize format. void(* deinit )(struct AVFormatContext *)
Deinitialize format. int(* check_bitstream )(struct AVFormatContext *, const AVPacket *pkt)
Set up any necessary bitstream filtering and extract any extra data needed for the global header.
  • AVIOContext、URLProtocol(协议解析)

    这里面涉及输入输出的机制,与具体协议有关,比如http、tcp、udp、rtp、rtsp等。

    URLProtocol的接口如下:
typedef struct URLProtocol {
const char *name;
int (*url_open)( URLContext *h, const char *url, int flags);
/**
* This callback is to be used by protocols which open further nested
* protocols. options are then to be passed to ffurl_open()/ffurl_connect()
* for those nested protocols.
*/
int (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
int (*url_accept)(URLContext *s, URLContext **c);
int (*url_handshake)(URLContext *c); /**
* Read data from the protocol.
* If data is immediately available (even less than size), EOF is
* reached or an error occurs (including EINTR), return immediately.
* Otherwise:
* In non-blocking mode, return AVERROR(EAGAIN) immediately.
* In blocking mode, wait for data/EOF/error with a short timeout (0.1s),
* and return AVERROR(EAGAIN) on timeout.
* Checking interrupt_callback, looping on EINTR and EAGAIN and until
* enough data has been read is left to the calling function; see
* retry_transfer_wrapper in avio.c.
*/
int (*url_read)( URLContext *h, unsigned char *buf, int size);
int (*url_write)(URLContext *h, const unsigned char *buf, int size);
int64_t (*url_seek)( URLContext *h, int64_t pos, int whence);
int (*url_close)(URLContext *h);
int (*url_read_pause)(URLContext *h, int pause);
int64_t (*url_read_seek)(URLContext *h, int stream_index,
int64_t timestamp, int flags);
int (*url_get_file_handle)(URLContext *h);
int (*url_get_multi_file_handle)(URLContext *h, int **handles,
int *numhandles);
int (*url_shutdown)(URLContext *h, int flags);
int priv_data_size;
const AVClass *priv_data_class;
int flags;
int (*url_check)(URLContext *h, int mask);
int (*url_open_dir)(URLContext *h);
int (*url_read_dir)(URLContext *h, AVIODirEntry **next);
int (*url_close_dir)(URLContext *h);
int (*url_delete)(URLContext *h);
int (*url_move)(URLContext *h_src, URLContext *h_dst);
const char *default_whitelist;
} URLProtocol;
  • AVPacket

    解复用或者复用之后的数据包,通常包含一帧视频或者一段音频数据。

就我而言,我用的比较多的是demuxer。通常FFmpeg的处理流程是,先通过demuxer的read_probe函数确定URL包含的容器类型,然后调用read_header读取多媒体的信息头,完成基本的初始化;之后正常读取packet通过read_packetread_timestamp;最后在读取结束的时候调用read_close,完成反初始化操作。另外可以通过read_seek实现媒体文件的seek操作(快进/快退)。

libavcodec主要机制

libavcodec主要结合libavformat实现解码、编码及音视频解析(单独格式分包或打包)。其中主要包含以下结构:

  • AVCodecContext

    AVCodecContext是libavcodec最核心的结构体,包含编码器或解码器类型,一个AVCodec、一个AVHWAccel以及一些其他编解码参数。
  • AVCodec

    这个结构包是编码器/解码器的对外的封装格式,其对外接口包括:
void(* 	init_static_data )(struct AVCodec *codec)
Initialize codec static data, called from avcodec_register(). int(* init )(AVCodecContext *) int(* encode_sub )(AVCodecContext *, uint8_t *buf, int buf_size, const struct AVSubtitle *sub) int(* encode2 )(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr)
Encode data to an AVPacket. int(* decode )(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt) int(* close )(AVCodecContext *) int(* send_frame )(AVCodecContext *avctx, const AVFrame *frame)
Decode/encode API with decoupled packet/frame dataflow. int(* send_packet )(AVCodecContext *avctx, const AVPacket *avpkt) int(* receive_frame )(AVCodecContext *avctx, AVFrame *frame) int(* receive_packet )(AVCodecContext *avctx, AVPacket *avpkt) void(* flush )(AVCodecContext *)
Flush buffers. Frame-level threading support functions
int(* init_thread_copy )(AVCodecContext *)
If defined, called on thread contexts when they are created. int(* update_thread_context )(AVCodecContext *dst, const AVCodecContext *src)
Copy necessary context variables from a previous thread context to the current one.
  • AVHWAccel

    这里包含硬件解码的结构体,需要依赖特定硬件才可以正常运行。其统一接口如下:
int(* 	alloc_frame )(AVCodecContext *avctx, AVFrame *frame)
Allocate a custom buffer. int(* start_frame )(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size)
Called at the beginning of each frame or field picture. int(* decode_slice )(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size)
Callback for each slice. int(* end_frame )(AVCodecContext *avctx)
Called at the end of each frame or field picture. void(* decode_mb )(struct MpegEncContext *s)
Called for every Macroblock in a slice. int(* init )(AVCodecContext *avctx)
Initialize the hwaccel private data. int(* uninit )(AVCodecContext *avctx)
Uninitialize the hwaccel private data.
  • AVCodecParserContext和AVCodecParser

    这是用于特定音频或视频的parser,比如h264、aac等,其统一对外接口如下:
int(* 	parser_init )(AVCodecParserContext *s)

int(* 	parser_parse )(AVCodecParserContext *s, AVCodecContext *avctx, const uint8_t **poutbuf, int *poutbuf_size, const uint8_t *buf, int buf_size)

void(* 	parser_close )(AVCodecParserContext *s)

int(* 	split )(AVCodecContext *avctx, const uint8_t *buf, int buf_size)
  • AVFrame

    这里面存储了音频、视频解码之后的原始数据或者编码器的输入数据。其中存储的音视频数据具体格式需要参考AVFrame::format(音频格式是AVSampleFormat,视频格式是AVPixelFormat)。

我使用FFmpeg的libavcodec是decoder和parser,当然为了获取音视频原始数据,还需要了解必要的AVFrame结构。

通常decoder的调用逻辑是通过AVCodec的init初始化解码器,调用decode函数解码数据,调用close反初始化解码器,在需要的时候(比如解码结束,切换流)调用flush清空解码器内部缓存数据。

当然很多音视频比特流需要通过parser才送入解码器,这样就可以调用parser_parse实现。

关于硬解码的实现可能有很多细节,有兴趣的可以参考下Hardware acceleration introduction with FFmpeg

ffmpeg主体架构分析的更多相关文章

  1. FFmpeg源代码简单分析:结构体成员管理系统-AVOption

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  2. Flickr 网站架构分析

    Flickr 网站架构分析 Flickr.com 是网上最受欢迎的照片共享网站之一,还记得那位给Windows Vista拍摄壁纸的Hamad Darwish吗?他就是将照片上传到Flickr,后而被 ...

  3. FFmpeg源代码简单分析:libavdevice的gdigrab

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  4. FFmpeg源代码简单分析:libavdevice的avdevice_register_all()

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  5. FFmpeg源代码简单分析:configure

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  6. FFmpeg源代码简单分析:makefile

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  7. FFmpeg源代码简单分析:libswscale的sws_scale()

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  8. FFmpeg源代码简单分析:libswscale的sws_getContext()

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  9. FFmpeg源代码简单分析:结构体成员管理系统-AVClass

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

随机推荐

  1. 分布式系统缓存系列之guava cache

      guava是google的一个开源java框架,其github地址是 https://github.com/google/guava.guava工程包含了若干被Google的 Java项目广泛依赖 ...

  2. C++雾中风景番外篇2:Gtest 与 Gmock,聊聊C++的单元测试

    正式工作之后,公司对于单元测试要求比较严格.(笔者之前比较懒,一般很少写完整的单测~~).作为一个合格的开发工程师,需要为所编写代码编写适量的单元测试是十分必要的,在实际进行的开发工作之中,TDD(T ...

  3. 前端解读面向切面编程(AOP)

    前言 面向对象(OOP)作为经典的设计范式,对于我们来说可谓无人不知,还记得我们入行起始时那句经典的总结吗-万事万物皆对象. 是的,基于OOP思想封装.继承.多态的特点,我们会自然而然的遵循模块化.组 ...

  4. ReportNG报表显示中文乱码和TestNG显示中文乱码实力解决办法

    最近在进军测试自动化框架学习阶段,但无意间总是会伴随小问题的困扰,比如中文乱码,而导致显示总是不舒服,个人觉得,就一定要解决,似乎有点点强迫症.所以遇到ReportNG报表显示中文乱码和TestNG显 ...

  5. Ubuntu python Compression requires the (missing) zlib module

    描述: 在Ubuntu中安装setuptools时出现   Compression requires the (missing) zlib module 解决方法步骤: ①Ubuntu下安装zlib: ...

  6. 学习ApiCloud遇到的问题

    1,当前账户xx 似乎没有权限访问此应用的云端数据,请切换账 检查项目的config.xml的id与apicloud的应用id是否一致

  7. SQL LOAD TABLE tbl_name FROM MASTER语法 把表的拷贝从主服务器转移到从属服务器。

    用于把表的拷贝从主服务器转移到从属服务器.本语句的主要作用是调试LOAD DATA FROM MASTER.它要求用于连接主服务器的帐户拥有对主服务器的RELOAD和SUPER权限,并拥有对要载入的主 ...

  8. [BZOJ3674]可持久化并查集加强版&[BZOJ3673]可持久化并查集 by zky

    思路: 用主席树维护并查集森林,每次连接时新增结点. 似乎并不需要启发式合并,我随随便便写了一个就跑到了3674第一页?3673是这题的弱化版,本来写个暴力就能过,现在借用加强版的代码(去掉异或),直 ...

  9. java.net.SocketException四大异常解决方案

    java.net.SocketException如何才能更好的使用呢?这个就需要我们先要了解有关这个语言的相关问题.希望大家有所帮助.那么我们就来看看有关java.net.SocketExceptio ...

  10. php基本类型

    php是一种弱类型语言,即变量不需要声明为特定的数据类型,因此在代码编写过程中做'类型处理'很重要. 处理方法: 1.检测类型: 2.转换类型: 3.依赖良好清晰的文档. php类型检查函数:     ...