注意:这篇转载的文章比较早,写得很清晰,但是新版的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;保存所有支持的输入文件协议,该结构体的定义如下:

  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协议中定义为:

  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等。它可以看成某一种协议的载体。其结构定义如下:

  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提供给用户的接口,它将以上两种不带缓冲的读取文件抽象为带缓冲的读取和写入,为用户提供带缓冲的读取和写入操作。数据结构定义如下:

  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系列:

  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函数进行写入操作。

(转)FFmpeg架构之I/O模块分析的更多相关文章

  1. ffmpeg架构和解码流程分析

    转 一,ffmpeg架构 1. 简介 FFmpeg是一个集录制.转换.音/视频编码解码功能为一体的完整的开源解决方案.FFmpeg的 开发是基于Linux操作系统,但是可以在大多数操作系统中编译和使用 ...

  2. FFmpeg的HEVC解码器源代码简单分析:环路滤波(Loop Filter)

    ===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...

  3. FFmpeg的HEVC解码器源代码简单分析:CTU解码(CTU Decode)部分-TU

    ===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...

  4. FFmpeg的HEVC解码器源代码简单分析:CTU解码(CTU Decode)部分-PU

    ===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...

  5. C语言的面向对象设计 —— 对 X264/FFMPEG 架构探讨

    1.为什么要用C语言 直到今天,C语言虽然不是使用人数最多的语言了,但是C没有老去,在很多的核心系统代码里,依然跑的是设计精美的C,绝大多数的嵌入式开发核心库软件是C开发的,多数标准算法是基于标准C设 ...

  6. FFmpeg的HEVC解码器源代码简单分析:概述

    ===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...

  7. C语言的面向对象设计之 X264,FFMPEG 架构探讨

    FFMPEG架构分析 使用面向对象的办法来设想这样一个编解码库,首先让人想到的是构造各种编解码器的类,然后对于它们的抽象基类确定运行数据流的规则,根据算法转换输入输出对象. 在实际的代码,将这些编解码 ...

  8. TYPESDK手游聚合SDK服务端设计思路与架构之一:应用场景分析

    TYPESDK 服务端设计思路与架构之一:应用场景分析 作为一个渠道SDK统一接入框架,TYPESDK从一开始,所面对的需求场景就是多款游戏,通过一个统一的SDK服务端,能够同时接入几十个甚至几百个各 ...

  9. 搭建一套自己实用的.net架构(2)【日志模块-log4net】

    先谈谈简单的模块,日志.在系统中日志模块是必须的,什么系统日志,操作日志,调试日志.这里用的是log4net. 对log4net还不熟悉的小伙伴们赶快去搜索基础教程哦, 我这里就不温故了. 那么有人要 ...

随机推荐

  1. cmake 手册详解【转】

    https://www.cnblogs.com/coderfenghc/tag/cmake/   CMake 手册详解(二十三) SirDigit 2012-12-16 22:03 阅读:11058 ...

  2. 常用的web api总结

    1.querySelector 获取指定元素中匹配css选择器的元素. // 作用在document document.querySelector("#nav"); // 获取文档 ...

  3. 微信小程序之自定义底部弹出框动画

    最近做小程序时,会经常用到各种弹框.直接做显示和隐藏虽然也能达到效果,但是体验性太差,也比较简单粗暴.想要美美地玩,添加点动画还是非常有必要的.下面做一个底部上滑的弹框. wxml <view ...

  4. PHP 输出日志到文件 DEMO

    首先需要确保输出文件有权限写入,一般设置权限为 chown -R nginx.nginx 输出的文件路径 如果以上方法还是无效,可以直接将文件设置有777,但是这种方式只能用于测试环境 chmod - ...

  5. 终于解决了python 3.x import cv2 “ImportError: DLL load failed: 找不到指定的模块” 及“pycharm关于cv2没有代码提示”的问题

    终于解决了python 3.x import cv2 “ImportError: DLL load failed: 找不到指定的模块” 及“pycharm关于cv2没有代码提示”的问题   参考 :h ...

  6. android studio 运行按钮为灰色的解决办法之一

    sync project with gradle files按钮(如下图)同步一下就好了 3.2的  3.3同步按钮变成了一只大象+箭头

  7. openresty开发系列4--nginx的配置文件说明

    openresty开发系列4--nginx的配置文件说明 Nginx基本配置 Nginx的主配置文件是:nginx.conf,nginx.conf主要组成如下: # 全局区   有一个工作子进程,一般 ...

  8. 函数返回new对象

    #include <iostream> using namespace std; // foo()函数本质上没什么问题,但建议你不要这样写代码 string &foo() { st ...

  9. Codeforces Round #556 (Div. 2) D. Three Religions 题解 动态规划

    题目链接:http://codeforces.com/contest/1150/problem/D 题目大意: 你有一个参考串 s 和三个装载字符串的容器 vec[0..2] ,然后还有 q 次操作, ...

  10. php+memcache高速缓存

    原文地址:https://blog.csdn.net/qq_33571752/article/details/86494667 #在lnmp基础上修改 依赖libevent程序库 cd lnmp/ta ...