FFmpeg的IO模型从avio_open()方法开始,核心结构体由AVIOContext和URLProtocol组成。如果需要读取缓冲区buffer数据进行播放,可以通过自定义AVIOContext,并且实现read_packet、write_packet、seek三个方法。如果需要播放加密视频,可以自定义私有协议进行解密,实现URLProtocol的open、read、write、seek、close等方法。

1、avio打开流程

外部调用avformat_open_input方法,内部会调用init_input/avio_open2方法,接着查找协议并且打开,另外是打开fifo。整体的avio打开流程如下:

avio_open()调用avio_open2(),而avio_open2()又调用ffio_open_whitelist(),代码如下:

int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,const AVIOInterruptCB *int_cb, AVDictionary **options,const char *whitelist, const char *blacklist)
{URLContext *h;int err;*s = NULL;err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);if (err < 0)return err;err = ffio_fdopen(s, h);if (err < 0) {ffurl_close(h);return err;}return 0;
}int avio_open2(AVIOContext **s, const char *filename, int flags,const AVIOInterruptCB *int_cb, AVDictionary **options)
{return ffio_open_whitelist(s, filename, flags, int_cb, options, NULL, NULL);
}int avio_open(AVIOContext **s, const char *filename, int flags)
{return avio_open2(s, filename, flags, NULL, NULL);
}

2、AVIOContext结构体

AVIOContext的结构体主要有read_packet、write_packet、seek、read_seek等方法,内部有注释演示读写buffer过程,代码如下:

/*** Bytestream IO Context.* @note None of the function pointers in AVIOContext should be called*       directly, they should only be set by the client application*       when implementing custom I/O. Normally these are set to the*       function pointers specified in avio_alloc_context()*/
typedef struct AVIOContext {const AVClass *av_class;/** The following shows the relationship between buffer, buf_ptr,* buf_ptr_max, buf_end, buf_size, and pos, when reading and when writing************************************************************************************                                   READING***********************************************************************************                            |              buffer_size              |*                            |---------------------------------------|*                            |                                       |**                         buffer          buf_ptr       buf_end*                            +---------------+-----------------------+*                            |/ / / / / / / /|/ / / / / / /|         |*  read buffer:              |/ / consumed / | to be read /|         |*                            |/ / / / / / / /|/ / / / / / /|         |*                            +---------------+-----------------------+************************************************************************************                                   WRITING************************************************************************************                             |          buffer_size                 |*                             |--------------------------------------|*                             |                                      |**                                                buf_ptr_max*                          buffer                 (buf_ptr)       buf_end*                             +-----------------------+--------------+*                             |/ / / / / / / / / / / /|              |*  write buffer:              | / / to be flushed / / |              |*                             |/ / / / / / / / / / / /|              |*                             +-----------------------+--------------+*                               buf_ptr can be in this due to a backward seek**/unsigned char *buffer;  /**< Start of the buffer. */int buffer_size;        /**< Maximum buffer size */unsigned char *buf_ptr; /**< Current position in the buffer */unsigned char *buf_end; /**< End of the data, may be less than buffer+buffer_size*/void *opaque;           /**< A private pointer, passed to the read/write/seek */int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);int64_t (*seek)(void *opaque, int64_t offset, int whence);int64_t pos;            /**< position in the file of the current buffer */int eof_reached;        /**< true if was unable to read due to error or eof */int write_flag;         /**< true if open for writing */int max_packet_size;int error;              /**< contains the error code or 0 if no error happened */int64_t (*read_seek)(void *opaque, int stream_index, int64_t timestamp, int flags);int64_t maxsize;int64_t bytes_read;int orig_buffer_size;int short_seek_threshold;const char *protocol_whitelist;const char *protocol_blacklist;int64_t written;unsigned char *buf_ptr_max;int min_packet_size;
} AVIOContext;

3、URLProtocol结构体

URLProtocol的结构体url_open、url_accpet、url_handshake、url_read、url_write、url_seek、url_close等方法,其中url_open()是对url_open2()的封装,代码如下:

typedef struct URLProtocol {const char *name;int     (*url_open)( URLContext *h, const char *url, int flags);int     (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);int     (*url_accept)(URLContext *s, URLContext **c);int     (*url_handshake)(URLContext *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_get_short_seek)(URLContext *h);int (*url_shutdown)(URLContext *h, int flags);const AVClass *priv_data_class;int priv_data_size;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;

4、打开url

调用avio.c的ffurl_open_whitelist()打开url,内部调用ffurl_alloc和ffurl_connect,代码如下:

int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,const AVIOInterruptCB *int_cb, AVDictionary **options,const char *whitelist, const char* blacklist,URLContext *parent)
{int ret = ffurl_alloc(puc, filename, flags, int_cb);if (ret < 0)return ret;......ret = ffurl_connect(*puc, options);if (!ret)return 0;
fail:ffurl_closep(puc);return ret;
}

其中ffurl_alloc主要实现2个功能:查找协议和分配协议,代码如下:

int ffurl_alloc(URLContext **puc, const char *filename, int flags,const AVIOInterruptCB *int_cb)
{const URLProtocol *p = NULL;p = url_find_protocol(filename);if (p)return url_alloc_for_protocol(puc, p, filename, flags, int_cb);*puc = NULL;return AVERROR_PROTOCOL_NOT_FOUND;
}

url_find_protocol过程主要是遍历协议数组,根据scheme进行匹配:

static const struct URLProtocol *url_find_protocol(const char *filename)
{const URLProtocol **protocols;char proto_str[128], proto_nested[128], *ptr;size_t proto_len = strspn(filename, URL_SCHEME_CHARS);int i;if (filename[proto_len] != ':' &&(strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) ||is_dos_path(filename))strcpy(proto_str, "file");elseav_strlcpy(proto_str, filename,FFMIN(proto_len + 1, sizeof(proto_str)));av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));if ((ptr = strchr(proto_nested, '+')))*ptr = '\0';protocols = ffurl_get_protocols(NULL, NULL);if (!protocols)return NULL;for (i = 0; protocols[i]; i++) {const URLProtocol *up = protocols[i];if (!strcmp(proto_str, up->name)) {av_freep(&protocols);return up;}if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&!strcmp(proto_nested, up->name)) {av_freep(&protocols);return up;}}av_freep(&protocols);return NULL;
}

ffurl_connect主要是判断用url_open2还是url_open来打开:

int ffurl_connect(URLContext *uc, AVDictionary **options)
{......int err =uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) :uc->prot->url_open(uc, uc->filename, uc->flags);......return 0;
}

5、打开fifo

调用aviobuf.c的fifo_fdopen方法打开fifo,主要是分配buffer缓冲区、分配AVIOContext:

int ffio_fdopen(AVIOContext **s, URLContext *h)
{// malloc bufferbuffer = av_malloc(buffer_size);if (!buffer)return AVERROR(ENOMEM);// malloc context*s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h,(int (*)(void *, uint8_t *, int))  ffurl_read,(int (*)(void *, uint8_t *, int))  ffurl_write,(int64_t (*)(void *, int64_t, int))ffurl_seek);fail:av_freep(&buffer);return AVERROR(ENOMEM);
}

6、protocol协议列表

FFmpeg支持的protocol包括:file、http、tcp、udp、hls、rtmp等,在protocols.c中声明为全局常量,列表如下(有删减):

extern const URLProtocol ff_async_protocol;
extern const URLProtocol ff_concat_protocol;
extern const URLProtocol ff_crypto_protocol;
extern const URLProtocol ff_file_protocol;
extern const URLProtocol ff_ftp_protocol;
extern const URLProtocol ff_hls_protocol;
extern const URLProtocol ff_http_protocol;
extern const URLProtocol ff_httpproxy_protocol;
extern const URLProtocol ff_https_protocol;
extern const URLProtocol ff_pipe_protocol;
extern const URLProtocol ff_rtmp_protocol;
extern const URLProtocol ff_rtp_protocol;
extern const URLProtocol ff_tcp_protocol;
extern const URLProtocol ff_tls_protocol;
extern const URLProtocol ff_udp_protocol;

file协议默认白名单有file/crypto/data,位于libavformat/file.c,定义如下:

const URLProtocol ff_file_protocol = {.name                = "file",.url_open            = file_open,.url_read            = file_read,.url_write           = file_write,.url_seek            = file_seek,.url_close           = file_close,.url_get_file_handle = file_get_handle,.url_check           = file_check,.url_delete          = file_delete,.url_move            = file_move,.priv_data_size      = sizeof(FileContext),.priv_data_class     = &file_class,.url_open_dir        = file_open_dir,.url_read_dir        = file_read_dir,.url_close_dir       = file_close_dir,.default_whitelist   = "file,crypto,data"
};

http协议默认白名单有http、https、tls、rtp、tcp、udp、crypto、httpproxy、data,位于libavformat/http.c,定义如下:

const URLProtocol ff_http_protocol = {.name                = "http",.url_open2           = http_open,.url_accept          = http_accept,.url_handshake       = http_handshake,.url_read            = http_read,.url_write           = http_write,.url_seek            = http_seek,.url_close           = http_close,.url_get_file_handle = http_get_file_handle,.url_get_short_seek  = http_get_short_seek,.url_shutdown        = http_shutdown,.priv_data_size      = sizeof(HTTPContext),.priv_data_class     = &http_context_class,.flags               = URL_PROTOCOL_FLAG_NETWORK,.default_whitelist   = "http,https,tls,rtp,tcp,udp,crypto,httpproxy,data"
};

7、makefile配置

libavformat的makefile脚本,支持enable/disable某种协议,如果开关设为true最终xxx.o文件被链接到动态库或者静态库。具体如下:

# protocols I/O
OBJS-$(CONFIG_ASYNC_PROTOCOL)            += async.o
OBJS-$(CONFIG_CONCAT_PROTOCOL)           += concat.o
OBJS-$(CONFIG_CRYPTO_PROTOCOL)           += crypto.o
OBJS-$(CONFIG_FILE_PROTOCOL)             += file.o
OBJS-$(CONFIG_FTP_PROTOCOL)              += ftp.o urldecode.o
OBJS-$(CONFIG_HLS_PROTOCOL)              += hlsproto.o
OBJS-$(CONFIG_HTTP_PROTOCOL)             += http.o httpauth.o urldecode.o
OBJS-$(CONFIG_HTTPPROXY_PROTOCOL)        += http.o httpauth.o urldecode.o
OBJS-$(CONFIG_HTTPS_PROTOCOL)            += http.o httpauth.o urldecode.o
OBJS-$(CONFIG_PIPE_PROTOCOL)             += file.o
OBJS-$(CONFIG_RTMP_PROTOCOL)             += rtmpproto.o rtmpdigest.o rtmppkt.o
OBJS-$(CONFIG_RTP_PROTOCOL)              += rtpproto.o ip.o
OBJS-$(CONFIG_TCP_PROTOCOL)              += tcp.o
TLS-OBJS-$(CONFIG_OPENSSL)               += tls_openssl.o
OBJS-$(CONFIG_TLS_PROTOCOL)              += tls.o $(TLS-OBJS-yes)
OBJS-$(CONFIG_UDP_PROTOCOL)              += udp.o ip.o

FFmpeg源码分析:AVIOContext、IO模型与协议相关推荐

  1. FFMPEG源码分析(一)

    FFMPEG源码分析(一) ffmpeg之前公司项目中就使用过,但是多停留于应用层面,实现某个功能时,需要哪些结构体以及调用哪些函数.最近想系统的学习一下ffmpeg,于是开始看雷霄骅https:// ...

  2. FFMPEG源码分析(二)

    ffmpeg源码分析之数据流 本文主要介绍ffmpeg的数据流,在ffmpeg中主要分有三个主要用途用于媒体流的解码播放,媒体流的转换(解码之后再编码)和媒体流录制. 媒体流的解码播放 在ffmpeg ...

  3. FFMPEG 源码分析

    FFMPEG基本概念: ffmpeg是一个开源的编解码框架,它提供了一个音视频录制,解码和编码库.FFMPEG是在linux下开发的,但也有windows下的编译版本. ffmpeg项目由以下几部分组 ...

  4. FFmpeg源码分析-直播延迟-内存泄漏

    FFmpeg源码分析-直播延迟-内存泄漏|FFmpeg源码分析方法|ffmpeg播放为什么容易产生延迟|解复用.解码内存泄漏分析 专注后台服务器开发,包括C/C++,Linux,Nginx,ZeroM ...

  5. Rxjava源码分析之IO.Reactivex.Observable

    Rxjava 源码系列目录 Rxjava源码分析之IO.Reactivex.Observer Rxjava源码分析之IO.Reactivex.CompositeDisposable Rxjava源码分 ...

  6. Rxjava源码分析之IO.Reactivex.CompositeDisposable

    Rxjava 源码系列目录 Rxjava源码分析之IO.Reactivex.Observer Rxjava源码分析之IO.Reactivex.CompositeDisposable Rxjava源码分 ...

  7. Rxjava源码分析之IO.Reactivex.Observer

    Android 中的观察者模式,Rxjava中有两个重要的类Observable和Observer,函数响应式编程具体表现为一个观察者(Observer)订阅一个可观察对象(Observable).通 ...

  8. ffmpeg源码分析-parse_optgroup

    本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8 ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv,分析其内部逻辑. a. ...

  9. ffmpeg源码分析-ffmpeg_parse_options

    本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8 ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv,分析其内部逻辑. a. ...

  10. ffmpeg源码分析-transcode_step

    本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8 ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv,分析其内部逻辑. a. ...

最新文章

  1. HDOJ HDU 1106 排序 ACM 1106 IN HDU
  2. axure9 邮件点击效果_总是收到无关的工作邮件?这个有意思的工具可以帮你消灭它们...
  3. tkinter 笔记 checkbutton 勾选项 (莫烦python笔记)
  4. java解析xml转为Map
  5. CodeForces - 432D Prefixes and Suffixes(KMP的next数组性质)
  6. CentOS+tomcat jsp笔记
  7. lua协程的使用列子分析
  8. python视频教程哪个好-Python教学视频哪个好?老男孩Python培训
  9. AdapterView学习总结
  10. RS-485接口详解
  11. 三层vxlan原理_VXLAN技术在园区网的应用探讨
  12. java 字符串特殊符号_Java去除字符串中的特殊符号或指定的字符
  13. 14期《读万卷书,行万里路》4月刊
  14. 174. 地下城游戏;剑指 Offer 40. 最小的k个数;378. 有序矩阵中第K小的元素;703. 数据流中的第K大元素
  15. npm install 安装软件,出现 operation not permitted, mkdir
  16. 从孙子兵法理解围棋大龙攻杀的要诀: 攻守双方口诀
  17. 3Com发展史(摘录)
  18. 1121 Damn Single (25分)
  19. 丘钛科技公布2018年8月份产品销售数量
  20. Pr学习DAY2-----详解“项目面板“与“时间轴面板“

热门文章

  1. 软件测试实习生 带人计划 Plan for Training Inten
  2. 2022Java面试笔记(上)
  3. 红米note10和红米note8pro哪个好
  4. 【游戏建模】将Ciri转为守望先锋的角色
  5. 全面吃透JAVA Stream流操作,让代码更加的优雅
  6. hibernate之@FilterDef @Filter注解的使用
  7. 2017283418魏扬
  8. 给领导敬酒杯子非要低于领导吗?
  9. linux 安装qt5和qtcreator开发工具
  10. 如何获取土豆网等在线视频FLV地址