FFmpeg架构之I/O模块分析

注意:这篇转载的文章比较早,写得很清晰,但是新版的ffmpeg的很多数据结构的名字已经改了。因此只能作参考。(例如ByteIOContext已经改名为AVIOContext)


1概述

ffmpeg项目的数据IO部分主要是在libavformat库中实现,某些对于内存的操作部分在libavutil库中。数据IO是基于文件格式(Format)以及文件传输协议(Protocol)的,与具体的编解码标准无关。 ffmpeg工程转码时数据IO层次关系如图所示:


对于上面的数据IO流程,具体可以用下面的例子来说明,我们从一个http服务器获取音视频数据,格式是flv的,需要通过转码后变成avi格式,然后通过udp协议进行发布。

其过程就如下所示:

1、读入http协议数据流,根据http协议获取真正的文件数据(去除无关报文信息);

2、根据flv格式对数据进行解封装;

3、读取帧进行转码操作;

4、按照目标格式avi进行封装;

5、通过udp协议发送出去。

2相关数据结构介绍

在libavformat库中与数据IO相关的数据结构主要有URLProtocol、URLContext、ByteIOContext、AVFormatContext等,各结构之间的关系如图所示。


1、URLProtocol结构

表示广义的输入文件,该结构体提供了很多的功能函数,每一种广义的输入文件(如:file、pipe、tcp、rtp等等)对应着一个URLProtocol结构,在av_register_all()中将该结构体初始化为一个链表,表头为avio.c里的URLProtocol *first_protocol = NULL;保存所有支持的输入文件协议,该结构体的定义如下:

[cpp]  view plain copy
  1. typedef struct URLProtocol   
  2. {   
  3. const char *name;   
  4. int (*url_open)(URLContext *h, const char *url, int flags);   
  5. int (*url_read)(URLContext *h, unsigned char *buf, int size);   
  6. int (*url_write)(URLContext *h, const unsigned char *buf, int size);   
  7. int64_t (*url_seek)(URLContext *h, int64_t pos, int whence);   
  8. int (*url_close)(URLContext *h); struct URLProtocol *next;   
  9. int (*url_read_pause)(URLContext *h, int pause);  
  10. int64_t (*url_read_seek)(URLContext *h, int stream_index,   
  11.         int64_t timestamp, int flags);  
  12. int (*url_get_file_handle)(URLContext *h);   
  13. int priv_data_size;  
  14. const AVClass *priv_data_class;   
  15. int flags;   
  16. int (*url_check)(URLContext *h, int mask);  
  17. } URLProtocol;  

注意到,URLProtocol是一个链表结构,这是为了协议的统一管理,ffmpeg项目中将所有的用到的协议都存放在一个全局变量first_protocol中,协议的注册是在av_register_all中完成的,新添加单个协议可以调用av_register_protocol2函数实现。而协议的注册就是将具体的协议对象添加至first_protocol链表的末尾。 URLProtocol在各个具体的文件协议中有一个具体的实例,如在file协议中定义为:

[cpp]  view plain copy
  1. URLProtocol ff_file_protocol = {   
  2. .name = "file",   
  3. .url_open = file_open,   
  4. .url_read = file_read,   
  5. .url_write = file_write,   
  6. .url_seek = file_seek,   
  7. .url_close = file_close,  
  8. .url_get_file_handle = file_get_handle,   
  9. .url_check = file_check,  
  10. };  

2、URLContext结构

URLContext提供了与当前打开的具体的文件协议(URL)相关数据的描述,在该结构中定义了指定当前URL(即filename项)所要用到的具体的URLProtocol,即:提供了一个在URLprotocol链表中找到具体项的依据,此外还有一些其它的标志性的信息,如flags, is_streamed等。它可以看成某一种协议的载体。其结构定义如下:

[cpp]  view plain copy
  1. typedef struct URLContext   
  2. {   
  3. const AVClass *av_class; ///< information for av_log(). Set by url_open().   
  4. struct URLProtocol *prot;   
  5. int flags;   
  6. int is_streamed; //< true if streamed (no seek possible), default = false * int max_packet_size;  
  7. void *priv_data;   
  8. char *filename; //< specified URL   
  9. int is_connected;   
  10. } URLContext;  

那么ffmpeg依据什么信息初始化URLContext?然后又是如何初始化URLContext的呢?在打开一个URL时,全局函数ffurl_open会根据filename的前缀信息来确定URL所使用的具体协议,并为该协议分配好资源,再调用ffurl_connect函数打开具体协议,即调用协议的url_open,调用关系如下:

int av_open_input_file(AVFormatContext **ic_ptr, const char *filename, AVInputFormat *fmt, int buf_size, AVFormatParameters *ap)

int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options)

static int init_input(AVFormatContext *s, const char *filename)

int avio_open(AVIOContext **s, const char *filename, int flags)

int ffurl_open(URLContext **puc, const char *filename, int flags)

int ffurl_alloc(URLContext **puc, const char *filename, int flags)

static int url_alloc_for_protocol (URLContext **puc, struct URLProtocol *up, const char *filename, int flags)

浅蓝色部分的函数完成了URLContext函数的初始化,URLContext使ffmpeg外所暴露的接口是统一的,而不是对于不同的协议用不同的函数,这也是面向对象思维的体现。在此结构中还有一个值得说的是priv_data项,这是结构的一个可扩展项,具体协议可以根据需要添加相应的结构,将指针保存在这就行。

3、AVIOContext结构

AVIOContext(即:ByteIOContext)是由URLProtocol和URLContext结构扩展而来,也是ffmpeg提供给用户的接口,它将以上两种不带缓冲的读取文件抽象为带缓冲的读取和写入,为用户提供带缓冲的读取和写入操作。数据结构定义如下:

[cpp]  view plain copy
  1. typedef struct {   
  2. unsigned char *buffer; /**< Start of the buffer. */   
  3. int buffer_size; /**< Maximum buffer size */   
  4. unsigned char *buf_ptr; /**< Current position in the buffer */   
  5. unsigned char *buf_end;   
  6. void *opaque; /关联URLContext  
  7. int (*read_packet)(void *opaque,uint8_t *buf,int buf_size);  
  8. int (*write_packet)(void *opaque,uint8_t *buf,int buf_size);   
  9. int64_t (*seek)(void *opaque,int64_t offset,int whence);   
  10. int64_t pos;   
  11. int must_flush;  
  12. int eof_reached; /**< true if eof reached */   
  13. int write_flag; /**< true if open for writing */   
  14. int max_packet_size;   
  15. unsigned long checksum;   
  16. unsigned char *checksum_ptr;   
  17. unsigned long (*update_checksum)(unsigned long checksum,const uint8_t *buf,unsigned int size);   
  18. int error;   
  19. int (*read_pause)(void *opaque,int pause)   
  20. int64_t (*read_seek)(void *opaque,int stream_index,int64_t timestamp,int flags);   
  21. int seekable;   
  22. } AVIOContext;  

结构简单的为用户提供读写容易实现的四个操作,read_packet write_packet read_pause read_seek,极大的方便了文件的读取,四个函数在加了缓冲机制后被中转到,URLContext指向的实际的文件协议读写函数中。 下面给出0.8版本中是如何将AVIOContext的读写操作中转到实际文件中的。 在avio_open()函数中调用了ffio_fdopen()函数完成了对AVIOContex的初始化,其调用过程如下:

int avio_open(AVIOContext **s, const char *filename, int flags)

ffio_fdopen(s, h); //h是URLContext指针 ffio_init_context(*s, buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h, (void*)

ffurl_read,(void*)ffurl_write,(void*)ffurl_seek)

蓝色部分的函数调用完成了对AVIOContext的初始化,在初始化的过程中,将AVIOContext的read_packet、write_packet、seek分别初始化为:ffurl_read ffurl_write ffurl_seek,而这三个函数又将具体的读写操作中转为:h->prot->url_read、h->prot->url_write、h->prot->url_seek,另外两个变量初始化时也被相应的中转,如下:

(*s)->read_pause = (int (*)(void *, int))h->prot->url_read_pause;

(*s)->read_seek = (int64_t (*)(void *, int, int64_t, int))h->prot->url_read_seek;

所以,可以简要的描述为:AVIOContext的接口口是加了缓冲后的URLProtocol的函数接口。

在aviobuf.c中定义了一系列关于ByteIOContext这个结构体的函数,如下 put_xxx系列:

[cpp]  view plain copy
  1. put_xxx系列:  
  2. void put_byte(ByteIOContext *s, int b);  
  3. void put_buffer(ByteIOContext *s, const unsigned char *buf, int size);  
  4. void put_le64(ByteIOContext *s, uint64_t val);  
  5. void put_be64(ByteIOContext *s, uint64_t val);  
  6. void put_le32(ByteIOContext *s, unsigned int val);  
  7. void put_be32(ByteIOContext *s, unsigned int val);  
  8. void put_le24(ByteIOContext *s, unsigned int val);  
  9. void put_be24(ByteIOContext *s, unsigned int val);  
  10. void put_le16(ByteIOContext *s, unsigned int val);  
  11. void put_be16(ByteIOContext *s, unsigned int val);  
  12. void put_tag(ByteIOContext *s, const char *tag);  
  13. get_xxx系列:  
  14. int get_buffer(ByteIOContext *s, unsigned char *buf, int size);  
  15. int get_partial_buffer(ByteIOContext *s, unsigned char *buf, int size);  
  16. int get_byte(ByteIOContext *s);  
  17. unsigned int get_le24(ByteIOContext *s);  
  18. unsigned int get_le32(ByteIOContext *s);  
  19. uint64_t get_le64(ByteIOContext *s);  
  20. unsigned int get_le16(ByteIOContext *s);  
  21. char *get_strz(ByteIOContext *s, char *buf, int maxlen);  
  22. unsigned int get_be16(ByteIOContext *s);  
  23. unsigned int get_be24(ByteIOContext *s);  
  24. unsigned int get_be32(ByteIOContext *s);  
  25. uint64_t get_be64(ByteIOContext *s);  


这些put_xxx及get_xxx函数是用于从缓冲区buffer中写入或者读取若干个字节,对于读写整型数据,分别实现了大端和小端字节序的版本。而缓冲区buffer中的数据又是从何而来呢,有一个fill_buffer的函数,在fill_buffer函数中调用了ByteIOContext结构的read_packet接口。在调用put_xxx函数时,并没有直接进行真正写入操作,而是先缓存起来,直到缓存达到最大限制或调用flush_buffer函数对缓冲区进行刷新,才使用write_packet函数进行写入操作。

适合fresh man上手 ffmpeg 库。 目录 ------------------------- 目录 第一章 多媒体概念介绍 6 1.1 视频格式 6 1.1.1 常见格式 6 1.2 音频格式 8 1.2.1 常见格式 9 1.2.2 比较 14 1.3 字幕格式 14 1.3.1 外挂字幕与内嵌字幕的阐述 14 1.3.2 外挂字幕视频与内嵌字幕视频的画面比较 15 1.3.3 外挂字幕的三种格式 15 1.4 采集录制和播放渲染 15 1.4.1 视频采集 15 1.4.2 视频录制 16 1.4.3 视频渲染 16 1.5 编解码器 18 1.6 容器和协议 18 1.6.1 容器格式和编码格式 18 1.6.2 协议 24 1.6.2.1 视频协议 25 1.6.2.2 音频协议 25 1.6.2.3 上层通讯协议 25 1.7 常用概念介绍 26 1.7.1 硬解 26 1.7.2 IBP 帧 26 1.7.3 DTS 和PTS 30 1.7.4 分辨率 30 1.7.5 码率 30 1.7.6 帧率 30 1.7.7 RGB 和YUV 30 1.7.8 实时和非实时 30 1.7.9 复合视频和 s-video 31 1.7.10 硬件加速 31 1.7.11 FFmpeg Device 31 第二章 FFmpeg 框架 32 2.1 FFmpeg 概述 32 2.1.1 简介 32 2.1.2 功能 32 2.1.3 模块组成 33 2.1.4 命令集 33 2.2 媒体播放器三大底层框架 35 第三章 编译及简单应用 39 3.1 FFmpeg 库编译和入门介绍 41 39 3.2 流媒体数据流程讲解 40 3.3 简单应用 42 3.4 SDL( Simple Direct Layer) 45 3.4.1 SDL 显示视频 45 3.4.2 SDL 显示音频 46 3.5 ffmpeg 程序的使用(ffmpeg.exe,ffplay.exe,ffprobe.exe) 46 3.5.1 ffmpeg.exe 46 3.5.2 ffplay.exe 46 3.5.3 ffprobe.exe 46 第四章 数据结构 50 4.1 AVCodec 结构体 51 4.2 AVCodecContext 结构体 52 4.3 AVInputFormat 结构体 53 4.4 AVFormatContext 结构体 62 4.5 MovContext 结构体 63 4.6 URLProtocol 结构体 63 4.7 URLContext 结构体 64 4.8 AVIOContext 结构体(老版本为:ByteIOContext) 64 4.9 AVStream 结构体 65 4.10 MOVStreamContext 结 构体 66 4.11 AVPacket 结 构体 67 4.12 AVPacketList 结 构体 67 4.13 AVFrame 结构体 53 第五章 重要模块 68 5.1 libavutil 公共模块 68 1 文件列表 68 2 common.h 文件 68 3 bswap.h 文件 70 4 rational.h 文件 71 5 mathematics.h 文件 71 6 avutil.h 文件 72 5.2 libavcodec 编解码模块 73 1 文件列表 73 2 avcodec.h 文件 74 3 allcodec.c 文件 78 4 dsputil.h 文件 79 5 dsputil.c 文件 79 6 utils_codec.c 文件 80 7 imgconvert_template.h 文件 90 8 imgconvert.c 文件 110 9 msrle.c 文件 152 10 turespeech_data.h 文件 159 11 turespeech.c 文件 162 5.3 libavformat 容器模块 171 1 文件列表 171 2 avformat.h 文件 172 3 allformat.c 文件 177 4 cutils.c 文件 178 5 file.c 文件 179 6 avio.h 文件 182 7 avio.c 文件 184 8 aviobuf.c 文件 188 9 utils_format.c 文件 197 10 avidec.c 文件 208 5.4 libswscale 视频色彩空间转换 230 5.5 libswresample 音频重采样 230 5.6 libavfilter 音视频滤器 230 5.7 libavdevice 设备输入和输出容器 230 5.8 libpostproc 视频后期处理 230 第六章 播放器 230 6.1 视频播放器 230 6.1.1 ffmpeg 库的配置 230 6.1.2 一个简单的视频播放器 231 6.2 音频播放器 234 6.3 一个完整的播放器--ffplay 240 6.3.1 ffplay 流程图 240 6.3.2 ffplay 源码剖析 240 第七章 应用开发 262 7.1 ffmpeg 库的使用:编码 262 第八章 关键函数介绍 267 8.1 avformat_open_input 267 8.2 avcodec_register_all() 268 8.3 av_read_frame() 269 8.4 avcodec_decode_video2() 270 8.5 transcode_init() 270 8.6 transcode() 280 第九章 ffmpeg 相关工程 288 9.1 ffdshow 288 ffdshow 源代码分析 1 : 整体结构 288 ffdshow 源代码分析 2: 位图覆盖滤镜(对话框部分 Dialog) 290 ffdshow 源代码分析 3: 位图覆盖滤镜(设置部分Settings) 297 ffdshow 源代码分析 4: 位图覆盖滤镜(滤镜部分Filter) 301 ffdshow 源代码分析 5: 位图覆盖滤镜(总结) 306 ffdshow 源代码分析 6: 对解码器的 dll 的封装(libavcodec) 306 ffdshow 源代码分析 8: 视频解码器类(TvideoCodecDec) 328 ffdshow 源代码分析 9: 编解码器有关类的总结 335 9.2 LAV filters 340 LAV Filter 源代码分析 1: 总体结构 340 LAV Filter 源代码分析 2: LAV Splitter 341 LAV Filter 源代码分析 3: LAV Video (1) 364 LAV Filter 源代码分析 4: LAV Video (2) 382 9.3 MPlayer 408 9.3.1 Mplayer 支持的格式 408 9.3.2 Mplayer 中头文件的功能分析 408 9.3.3 MPlayer.main 主流程简要说明 408 9.3.4 Mplayer 源码分析 409 第十章 开发实例 416 第十一章 mp4 文件封装协议分析 416 11.1 概述 416 11.2 mp4 的物理结构 416 11.3 数据的组织结构 417 11.4 mp4 的时间结构 417 11.5 文件结构分析 418 11.5.1 File Type Box(ftyp) 418 11.5.2 Movie Box(moov) 418 第十二章 flv 文件格式分析 437 12.1 概述 437 12.2 文件总体结构 437 12.3 文件结构分析 438 12.3.1 flv 文件头的结构 438 12.3.2 body 主体结构 439 附录A:常见问题 444 1 ffmpeg 从内存中读取数据 444 2 MFC 中使用SDL 播放音频没有声音的解决方法 444 附录B:经典代码示例 445 附录 c:ffmpeg 参数中文详细解释 456 附录D:ffplay 的快捷键以及选项 458 附录E: ffmpeg 处理 rtmp 流媒体 459
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值