协议操作对象结构:


协议(文件)操作的顶层结构是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. Django 分页器 缓存 信号 序列化

    阅读目录 分页器 缓存 信号 序列化 Django分页器  (paginator) 导入 from django.core.paginator import Paginator, EmptyPage, ...

  2. while小用

    1.使用while打印1 2 3 4 5 6  8 9 10 #!/usr/bin/env python #encoding: utf-8 num = 1 while num < 11: if ...

  3. delphi webbrowser post自动登录

    delphi webbrowser post自动登录     var  EncodedDataString: WideString;  PostData: OleVariant;  Headers: ...

  4. FAQ | 是什么导致MySQL数据库服务器磁盘I/O高(本文章来自知数堂)

    FAQ | 是什么导致MySQL数据库服务器磁盘I/O高 2016-12-26 叶金荣 老叶茶馆 0.导读 有个MySQL服务器的磁盘I/O总有过高报警,怎么回事? 本文约1500字,阅读时间约10分 ...

  5. 王译潇20162314 实验报告三plus结对编程四则运算第一阶段

    北京电子科技学院BESTI实验报告 课程:程序设计与数据结构 班级: 1623 姓名: 王译潇 学号:20162314 指导教师:娄佳鹏老师.王志强老师 实验日期:2017年5月12号 实验密级: 非 ...

  6. apache性能测试工具ab

    性能测试工具目前最常见的有以下几种:ab.http_load.webbench.siege ab是apache自带的压力测试工具.ab非常实用,它不仅可以对apache服务器进行网站访问压力测试,也可 ...

  7. 伸展树基础(Splay)

    3224: Tyvj 1728 普通平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3948  Solved: 1627 [Submit][St ...

  8. [国家集训队2011]happiness

    Description 高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友.这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友 ...

  9. Effective C++ 条款12:复制对象时勿忘其每一个成分

    void logCall(const std::string& funcName); class Customer { public: ... Customer (const Customer ...

  10. 图论_FatherChristmasFlymouse(Tarjan+dijkstra or spfa)

    堆优化Dij VS Spfa 堆优化Dij小胜一筹. 题目名字:Father Christmas flymouse (POJ 3160) 这题可以说是图论做的比较畅快的一题,比较综合,很想说一说. 首 ...