协议操作对象结构:


协议(文件)操作的顶层结构是AVIOContext,这个对象实现了带缓冲的读写操作;FFMPEG的输入对象AVFormat的pb字段指向一个AVIOContext。

AVIOContext的opaque实际指向一个URLContext对象,这个对象封装了协议对象及协议操作对象,其中prot指向具体的协议操作对象,priv_data指向具体的协议对象。

URLProtocol为协议操作对象,针对每种协议,会有一个这样的对象,每个协议操作对象和一个协议对象关联,比如,文件操作对象为ff_file_protocol,它关联的结构体是FileContext。

代码分析:

初始化AVIOFormat函数调用关系:


我们采用从底至上的方法分析源码。

URLProtocol是FFMPEG操作文件的结构(包括文件,网络数据流等等),包括open、close、read、write、seek等操作。

在在av_register_all()函数中,通过调用REGISTER_PROTOCOL()宏,所有的URLProtocol都保存在以first_protocol为链表头的链表中。

URLProtocol结构体的定义为(简化版,未完全列出所有成员):

  1. typedef struct URLProtocol {
  2. const char *name;
  3. int     (*url_open)( URLContext *h, const char *url, int flags);
  4. int     (*url_read)( URLContext *h, unsigned char *buf, int size);
  5. int     (*url_write)(URLContext *h, const unsigned char *buf, int size);
  6. int64_t (*url_seek)( URLContext *h, int64_t pos, int whence);
  7. int     (*url_close)(URLContext *h);
  8. int (*url_get_file_handle)(URLContext *h);
  9. struct URLProtocol *next;  // 指向下一个URLProtocol对象(所有URLProtocol以链表链接在一起)
  10. int priv_data_size;     // 和该URLProtocol对象关联的对象的大小
  11. const AVClass *priv_data_class;
  12. } URLProtocol;

以文件协议为例,ff_file_protocol变量定义为:

  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. .priv_data_size      = sizeof(FileContext),
  11. .priv_data_class     = &file_class,
  12. };

从中可以看出,.priv_data_size的值为sizeof(FileContext),即ff_file_protocol和FileContext想关联。

FileContext对象的定义为:

  1. typedef struct FileContext {
  2. const AVClass *class;
  3. int fd; <span style="white-space:pre">    </span>// 文件描述符
  4. int trunc;      // 截断属性
  5. int blocksize;  // 块大小,每次读写文件最大字节数
  6. } FileContext;

返回去看ff_file_protocol里的函数指针,以url_read成员为例,它指向file_read()函数,该函数的定义为:

  1. static int file_read(URLContext *h, unsigned char *buf, int size)
  2. {
  3. FileContext *c = h->priv_data;
  4. int r;
  5. size = FFMIN(size, c->blocksize);
  6. r = read(c->fd, buf, size);
  7. return (-1 == r)?AVERROR(errno):r;
  8. }

从代码可以看出:

1. 调用此函数时,URLContext的priv_data指向一个FileContext对象;

2. 该函数每次最大只读取FileContext.blocksize大小的数据。

从上面的代码中,还可发现一个重要的对象:URLContext,根据代码推测,这个对象的priv_data指向一个FileContext,下面来看看这个对象的定义及初始化:

定义:

  1. typedef struct URLContext {
  2. const AVClass *av_class;    /**< information for av_log(). Set by url_open(). */
  3. struct URLProtocol *prot;
  4. void *priv_data;
  5. char *filename;             /**< specified URL */
  6. int flags;
  7. int max_packet_size;        /**< if non zero, the stream is packetized with this max packet size */
  8. int is_streamed;            /**< true if streamed (no seek possible), default = false */
  9. int is_connected;
  10. AVIOInterruptCB interrupt_callback;
  11. int64_t rw_timeout;         /**< maximum time to wait for (network) read/write operation completion, in mcs */
  12. } URLContext;

注释已经很明了,接下来看看这个结构体的初始化,在url.h(URLContext定义的地方)文件中很容易发现有一个ffurl_open()函数,猜测这应该是个初始化函数,从

该函数的内容可知,它首先调用了ffurl_alloc()申请空间,然后调用了ffurl_connect()建立连接。因此我们的目标转向这两个函数

先看ffurl_alloc()函数,该函数先调用url_find_protocol()查找和输入文件名称对应的协议对象,然后调用url_alloc_for_protocol()申请空间。

url_find_protocol()易知:filename和协议匹配的规则是finename的前缀名(本地文件被统一处理为file前缀)和协议的name字段所指向的字符串相等。

再看url_alloc_for_protocol()函数:

  1. static int url_alloc_for_protocol(URLContext **puc, struct URLProtocol *up,
  2. const char *filename, int flags,
  3. const AVIOInterruptCB *int_cb)
  4. {
  5. URLContext *uc;
  6. int err;
  7. // ...
  8. uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1); // 注意申请的空间大小
  9. if (!uc) {
  10. err = AVERROR(ENOMEM);
  11. goto fail;
  12. }
  13. uc->av_class = &ffurl_context_class;
  14. uc->filename = (char *)&uc[1];   // 指向uc+sizeof(URLContext)的低昂
  15. strcpy(uc->filename, filename);
  16. uc->prot            = up;
  17. uc->flags           = flags;
  18. uc->is_streamed     = 0; /* default = not streamed */
  19. uc->max_packet_size = 0; /* default: stream file */
  20. if (up->priv_data_size) {
  21. uc->priv_data = av_mallocz(up->priv_data_size);
  22. if (!uc->priv_data) {
  23. err = AVERROR(ENOMEM);
  24. goto fail;
  25. }
  26. if (up->priv_data_class) {
  27. int proto_len= strlen(up->name);
  28. char *start = strchr(uc->filename, ',');
  29. *(const AVClass **)uc->priv_data = up->priv_data_class;
  30. av_opt_set_defaults(uc->priv_data);
  31. // ...
  32. }
  33. }

由上代码可以看出,URLContext的filename保存了输入文件名,prot字段保存了查找到的协议操作对象指针,flags由入参指定,is_streamed及max_packet_size默认值为0,

priv_data指向了一个由协议操作对象的priv_data_size指定大小的空间(考虑ff_file_protocol,即指向了一个sizeof(FileContext)大小的空间),且该空间的被初始化为0。

至此,URLContext的大部分内容已初始化,ffurl_alloc()工作已经完成,接着看ffurl_connect(),从代码易知,它调用了prot->url_open()函数打开协议,并设置了URLContext的

is_connected和is_streamed为1。

注意,av_opt_set_defaults(uc->priv_data)为为priv_data所指向的结构也进行了默认赋值操作,针对ff_file_protocol,赋值字段及值可参考file_options[]的定义:

  1. static const AVOption file_options[] = {
  2. { "truncate", "truncate existing files on write", offsetof(FileContext, trunc), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
  3. { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
  4. { NULL }
  5. };

即,truncate字段赋值为1,blocksize字段赋值为INT_MAX。

最后看看file_open()函数,从代码容易看出,该函数打开了文件,并将文件句柄保存在FileContext的fd字段。并且,该函数还根据文件属性设置了FileContext的is_streamed的值。

URLContext再往上一层是AVIOContext,该结构体的定义为:

  1. typedef struct AVIOContext {
  2. const AVClass *av_class;
  3. unsigned char *buffer;  /**< 读写缓冲buffer起始地址 */
  4. int buffer_size;        /**< buffer大小 */
  5. unsigned char *buf_ptr; /**< Current position in the buffer */
  6. unsigned char *buf_end; /**< End of the data */
  7. void *opaque;           /**< 指向URLContext */
  8. int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);   /* 指向ffurl_read() */
  9. int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);  /* 指向ffurl_write() */
  10. int64_t (*seek)(void *opaque, int64_t offset, int whence);      /* 指向ffurl_seek() */
  11. int64_t pos;            /**< 当前buffer对应的文件内容中的位置 */
  12. int must_flush;         /**< true if the next seek should flush */
  13. int eof_reached;        /**< true if eof reached */
  14. int write_flag;         /**< true if open for writing */
  15. int max_packet_size;
  16. unsigned long checksum;
  17. unsigned char *checksum_ptr;
  18. unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
  19. int error;              /**< contains the error code or 0 if no error happened */
  20. int (*read_pause)(void *opaque, int pause);/
  21. int64_t (*read_seek)(void *opaque, int stream_index,
  22. int64_t timestamp, int flags);
  23. int seekable;   // 是否可seek,0表示不可搜索
  24. int64_t maxsize;
  25. int direct;
  26. int64_t bytes_read;
  27. int seek_count;
  28. int writeout_count;
  29. int orig_buffer_size;
  30. }AVIOContext;

avio_open2()负责初始化AVIOContext,从代码易知,该函数首先调用ffurl_open()申请了一个URLContext对象并打开了文件。然后调用ffio_fdopen()申请一个AVIOContext对象并赋初值。

ffio_fdopen()先申请了一个读写缓冲buffer(结合文件协议,buffer大小为IO_BUFFER_SIZE),然后调用avio_alloc_context()赋值,注意ffurl_read,ffurl_write,ffurl_seek的地址作为入参被传入。

最终调用的是ffio_init_context赋值,结合入参可知:

AVIOContext.buffer指向申请到的buffer, AVIOContext.orig_buffer_size和AVIOContext.buffer_size值为buffer_size(I_BUFFER_SIZE),buf_ptr字段初始化为buffer的开始地址,opaque指向URLContext对象。

再关注下avio_read()函数:

  1. int avio_read(AVIOContext *s, unsigned char *buf, int size)
  2. {
  3. int len, size1;
  4. size1 = size;
  5. while (size > 0) {
  6. len = s->buf_end - s->buf_ptr;
  7. if (len > size)
  8. len = size;
  9. if (len == 0 || s->write_flag) {
  10. if((s->direct || size > s->buffer_size) && !s->update_checksum){
  11. if(s->read_packet)
  12. len = s->read_packet(s->opaque, buf, size);
  13. if (len <= 0) {
  14. /* do not modify buffer if EOF reached so that a seek back can
  15. be done without rereading data */
  16. s->eof_reached = 1;
  17. if(len<0)
  18. s->error= len;
  19. break;
  20. } else {
  21. s->pos += len;
  22. s->bytes_read += len;
  23. size -= len;
  24. buf += len;
  25. s->buf_ptr = s->buffer;
  26. s->buf_end = s->buffer/* + len*/;
  27. }
  28. } else {
  29. fill_buffer(s);
  30. len = s->buf_end - s->buf_ptr;
  31. if (len == 0)
  32. break;
  33. }
  34. } else {
  35. memcpy(buf, s->buf_ptr, len);
  36. buf += len;
  37. s->buf_ptr += len;
  38. size -= len;
  39. }
  40. }
  41. if (size1 == size) {
  42. if (s->error)      return s->error;
  43. if (url_feof(s))   return AVERROR_EOF;
  44. }
  45. return size1 - size;
  46. }

从代码易知,该函数实现了缓冲读写功能,即调用者传递的size比buffer_size大,则buffer_size的倍数部分会直接读取到buf,余数部分会首先读取buffer_size大小的数据到buffer(fill_buffer()函数),然后再拷贝到入参buf指向的地址中。

AVIOContext再往上一层是AVFormatContext对象,也是ffmpeg的顶层对象,AVFormatContext的pb字段指向一个AVIOContext对象。

原文地址:http://blog.csdn.net/finewind/article/details/39433055

2——FFMPEG之协议(文件)操作----AVIOContext, URLContext, URLProtocol的更多相关文章

  1. FFmpeg开发实战(二):FFmpeg 文件操作

    FFmpeg 提供了丰富的API供我们使用,下面我们来讲述一下文件操作相关的API: FFmpeg 删除文件:avpriv_io_delete() FFmpeg 重命名文件:avpriv_io_mov ...

  2. FFMPEG结构体分析:AVIOContext

    注:写了一系列的结构体的分析的文章,在这里列一个列表: FFMPEG结构体分析:AVFrame FFMPEG结构体分析:AVFormatContext FFMPEG结构体分析:AVCodecConte ...

  3. javaCV入门指南:调用FFmpeg原生API和JavaCV是如何封装了FFmpeg的音视频操作?

    通过"javaCV入门指南:序章 "大家知道了处理音视频流媒体的前置基本知识,基本知识包含了像素格式.编解码格式.封装格式.网络协议以及一些音视频专业名词,专业名词不会赘述,自行搜 ...

  4. 归档NSKeyedArchiver解归档NSKeyedUnarchiver与文件管理类NSFileManager (文件操作)

    ========================== 文件操作 ========================== 一.归档NSKeyedArchiver 1.第一种方式:存储一种数据. // 归档 ...

  5. 前端学PHP之文件操作

    × 目录 [1]文件类型 [2]文件属性 [3]目录路径[4]目录遍历[5]目录统计[6]目录增删[7]目录复制[8]文件操作[9]文件内容 前面的话 在程序运行时,程序本身和数据一般都存在内存中,当 ...

  6. python基础-文件操作

    一.文件操作 打开文件时,需要指定文件路径和以何等方式打开文件,打开后,即可获取该文件句柄,日后通过此文件句柄对该文件操作. 打开文件的模式有: r ,只读模式[默认模式,文件必须存在,不存在则抛出异 ...

  7. 前端学PHP之文件操作(认真读读)

    前面的话 在程序运行时,程序本身和数据一般都存在内存中,当程序运行结束后,存放在内存中的数据被释放.如果需要长期保存程序运行所需的原始数据,或程序运行产生的结果,就需要把数据存储在文件或数据库.一般地 ...

  8. 总结文件操作函数-文件夹(三)-C语言

    获取.改变当前文件夹: 原型为: #include <unistd.h>   //头文件 char *getcwd(char *buf, size_t size); //获取当前文件夹.相 ...

  9. 阿里云服务器+ftp文件操作+基于Centos7的vsftpd配置

    路径问题:一定要注意此位置是否需要加入"/" 文件上传方式:被动模式 vsftp完整配置: # # The default compiled in settings are fai ...

随机推荐

  1. boost atomic

    文档: http://www.boost.org/doc/libs/1_53_0/doc/html/atomic.html Presenting Boost.Atomic Boost.Atomic i ...

  2. 将php数组存取到本地文件

    存数组: <?php $data = array( "a" => "aaaaaa", "b" => "bbbbb ...

  3. jsp验证正则表达式

    jsp验证正则表达式 下面都是我收集的一些比较常用的正则表达式,因为平常可能在表单验证的时候,用到的比较多.特发出来,让各位朋友共同使用.呵呵. 匹配中文字符的正则表达式: [u4e00-u9fa5] ...

  4. 14链表中倒数第k个结点

    题目描述 输入一个链表,输出该链表中倒数第k个结点.   思路: 快慢指针 快指针 先走k 步, 然后快慢指针一起走 当快指针走到null 时, 慢指针就是所求的倒数第k个节点 tips: 判断k是否 ...

  5. 什么是EventLoop

    Event Loop 是一个很重要的概念,指的是计算机系统的一种运行机制. JavaScript语言就采用这种机制,来解决单线程运行带来的一些问题. 本文参考C. Aaron Cois的<Und ...

  6. javaEE中的字符编码问题

    0 web.xml中注册的CharacterEncodingFilter <!-- 配置字符集过滤器 --> <filter> <filter-name>encod ...

  7. Python 面向对象的综合应用

    # 面向对象的综合应用 # 计算器:实现一些基本的计算操作,已经打印结果 # --------------- 代码1 ---------------------- def add(x, y): ret ...

  8. linux 进阶命令___0002

    #列出重复文件,首先检查文件大小,再检查md5sum find -not -empty -type f -printf "%s\n" | sort -rn | uniq -d | ...

  9. [USACO08DEC]在农场万圣节Trick or Treat on the Farm

    题目描述 Every year in Wisconsin the cows celebrate the USA autumn holiday of Halloween by dressing up i ...

  10. FckEditor 配置手册中文教程详细说明

    http://www.jb51.net/article/17965.htm 首先,FCKEDITOR的性能是非常好的,用户只需很少的时间就可以载入 FCKEDITOR所需文件.对于其他在线编辑器来说, ...