Part I is http://blog.csdn.net/xiruanliuwei/article/details/24974873

struct AVIOContext 结构体中几个比较重要的结构体成员:

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 if the read function returned
                                 less data than requested, e.g. for streams where
                                 no more data has been received yet. */

int64_t pos;            /**< position in the file of the current buffer */

int write_flag;         /**< true if open for writing */

int max_packet_size;

buffer_size = buf_end - buffer;

pos这个比较纠结,英语水平有限,看不懂这个英文注释的意思,通过试验发现,其实它是标识AVIOContext关联的那个文件中的位置:

case 1: reading

buffer_size = 32768;  // default value is 32768, 0x8000

avio_read会将数据从文件,读取到buffer缓冲区中,一次读取buffer_size个字节,所以pos总是32768的整数倍;

但是,avio_read中的参数size不必一定是32768或者其整数倍,它可以是任何值,从buffer中读取size个数返回,

剩余的数据供下次继续使用;

case 2 writing:

buffer_size = 32768;  // default value is 32768, 0x8000

avio_write会将数据从参数buf中写入到AVIOContext的buffer中,但是不一定会立即写入AVIOContext关联的文件

中,只有当buffer中的数据大于等于32768时,才会将数据写入文件;

void avio_write(AVIOContext *s, const unsigned char *buf, int size)
{

// s->direct若为1,则不经过AVIOContext中的buffer缓冲,直接将数据从buf中写入到AVIOContext关联的文件中;
    if (s->direct && !s->update_checksum) 
    {
        avio_flush(s);
        
        writeout(s, buf, size);

return;
    }
    
    while (size > 0)
    {

// buf_end - buf_ptr表示buffer剩余的空间,size表示要写的数据量,如果size大于剩余空间,则要分多次写入,

// 否则下面的memcpy会破坏内存中数据,超出的数据会覆盖buffer后面数据;
        int len = FFMIN(s->buf_end - s->buf_ptr, size);
        memcpy(s->buf_ptr, buf, len);
        s->buf_ptr += len;

// 其实没有buf_ptr大于buf_end的情况,那样内存数据就出错了

// 前面的FFMIN措施,保证buf_ptr总是小于等于buf_end

// 等于时,表示AVIOContext中buffer已满,需要将数据写入AVIOContext关联的文件中
        if (s->buf_ptr >= s->buf_end)
            flush_buffer(s);

buf += len;
        size -= len;
    }
}

static void flush_buffer(AVIOContext *s)
{
    if (s->buf_ptr > s->buffer)
    {
        writeout(s, s->buffer, s->buf_ptr - s->buffer);
        
        if (s->update_checksum) 
        {
            s->checksum     = s->update_checksum(s->checksum, s->checksum_ptr,
                                                 s->buf_ptr - s->checksum_ptr);
            s->checksum_ptr = s->buffer;
        }
    }
    s->buf_ptr = s->buffer;
}

buf_ptr > buffer表示AVIOContext的buffer中有数据,能够执行写入操作,当数据写入完毕后,

buf_ptr回到最初,再从头来过  s->buf_ptr = s->buffer;

writeout(s, s->buffer, s->buf_ptr - s->buffer);

static void writeout(AVIOContext *s, const uint8_t *data, int len)
{
    printf("Enter Function %s\n", __FUNCTION__);
    printf("data is: %p\n", data);
    printf("len is: %d\n", len);

// write_packet不要为NULL, 并且error表示数据校验没有错误
    if (s->write_packet && !s->error)
    {
        int ret = s->write_packet(s->opaque, (uint8_t *)data, len);
        if (ret < 0)
        {
            s->error = ret;
        }
    }
    
    s->writeout_count ++;

// 特别留意一下这个语句,只有当真实写操作完成后,pos所表示的文件中的位置才会变化

// 对于非direct的,pos = buffer_size * writeout_count;

// 对于direct的,就只能这样每次pos + len了,因为每次写入的数据的size并非一样的
    s->pos += len;
}

调用write_packet函数指针

int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);

指向的函数,将数据写入到具体文件中,当然,在write_packet指向的函数中,还有嵌套;

(在aviobuf.c文件中, AVIOContext结构体实例在创建时,write_packet函数指针被赋值指向函数ffur_write)

*s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h,
                            (void*)ffurl_read, (void*)ffurl_write, (void*)ffurl_seek);

int ffurl_write(URLContext *h, const unsigned char *buf, int size)
{
    printf("Enter Function %s\n", __FUNCTION__);
    // print_backtrace();
    
    if (!(h->flags & AVIO_FLAG_WRITE))
        return AVERROR(EIO);
    
    /* avoid sending too big packets */
    if (h->max_packet_size && size > h->max_packet_size)
        return AVERROR(EIO);

return retry_transfer_wrapper(h, (unsigned char *)buf, size, size, (void*)h->prot->url_write);
}

h->prot是具体的协议,URL的协议

另外一个比较纠结的变量:

int write_flag;         /**< true if open for writing */

搜索代码,给write_flag赋值的只有一个地方:

static int url_resetbuf(AVIOContext *s, int flags)
{
    av_assert1(flags == AVIO_FLAG_WRITE || flags == AVIO_FLAG_READ);

if (flags & AVIO_FLAG_WRITE)
    {
        s->buf_end = s->buffer + s->buffer_size;
        s->write_flag = 1;
    }
    else
    {
        s->buf_end = s->buffer;
        s->write_flag = 0;
    }
    
    return 0;
}

通过代码可以知道,需要写文件时,write_flag为1,否则write_flag都是0

写文件包含两种情况:

#define AVIO_FLAG_WRITE 2                                      /**< write-only */
#define AVIO_FLAG_READ_WRITE (AVIO_FLAG_READ|AVIO_FLAG_WRITE)  /**< read-write pseudo flag */

AVIO_FLAG_WRITE:以写的方式打开文件;

AVIO_FLAG_READ_WRITE:以读、写方式打开文件;

int ffio_init_context(AVIOContext *s,
                  unsigned char *buffer,
                  int buffer_size,
                  int write_flag,
                  void *opaque,
                  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))

中:

if (!read_packet && !write_flag)
    {
        s->pos     = buffer_size;
        s->buf_end = s->buffer + buffer_size;
    }

read_packet表示没有从文件中往buffer中读取数据的函数;

write_flag为0,表示文件是以只读方式打开;

不清楚这种设置的意义?

又是读,又是写,比较乱,比较难分析,分成两种情况:

1. 只读 write_flag == 0

ffio_init_context

s->buffer      = buffer;
    s->orig_buffer_size = s->buffer_size = buffer_size;
    s->buf_ptr     = buffer;

s->buf_end = s->buffer;
        s->write_flag = 0;

s->pos             = 0;

if (!read_packet && !write_flag)
    {
        s->pos     = buffer_size;
        s->buf_end = s->buffer + buffer_size;
    }

此时write_flag必然是0,这样的设置不知道什么意义,难道是read_packet为null时,设置成一个buffer已经填充满了,可以从这里面读取数据?

read_packet不为null时,则不进行设置,因为此时可以通过read_packet从文件中读取数据到buffer中;

2. 只写 write_flag == 1

???后续分析~

需要注意,在AVIOContext中存在一个缓冲区,

/**
     * avio_read and avio_write should if possible be satisfied directly
     * instead of going through a buffer, and avio_seek will always
     * call the underlying seek function directly.
     */
    int direct;

当direct为1时,

1. reading

AVIOContext关联的文件 ---> user buf

2. writing

user buf ---> AVIOContext关联的文件

当direct为0时,

1. reading

AVIOContext关联的文件 ---> AVIOContext.buffer ---> user buf

2. writing

user buf ---> AVIOContext.buffer ---> AVIOContext关联的文件

only AVIO_FLAG_READ 时:

此时, write_flag == 0

// buf: user buf
// size: 表示要读取的字节数量
int avio_read(AVIOContext *s, unsigned char *buf, int size)
{
    int len, size1;

size1 = size;
    while (size > 0)
    {
        // len表示AVIOContext.buffer中剩余的可供读取的字节数量
        len = s->buf_end - s->buf_ptr;
        if (len > size)
            len = size;
        
        // 根据前提,此时write_flag为0
        // len = 0,表示AVIOContext中buffer中没有可用数据
        if (len == 0 || s->write_flag) 
        {
            // s->direct 表示不经过AVIOContext.buffer,直接从文件读取
            // size > s->buffer_size 表示所要读取的字节数量,超过AVIOContext.buffer的容量
            if((s->direct || size > s->buffer_size) && !s->update_checksum)
            {
                // 直接从文件中读取数据到user buf中,读取size字节
                if(s->read_packet)
                    len = s->read_packet(s->opaque, buf, size);

// len 是读取的字节数量,为0表示到文件末尾,无数据可读取, < 0是表示读取出错吧?
                if (len <= 0)
                {
                    /* do not modify buffer if EOF reached so that a seek back can
                    be done without rereading data */
                    s->eof_reached = 1;
                    if(len<0)
                        s->error= len;
                    break;
                } 
                else 
                {
                    // 读取了len字节,需要更新s->pos,其表示在文件中的偏移位置
                    s->pos += len;
                    s->bytes_read += len;
                    size -= len;
                    buf += len;
                    // 此时,原来buffer中的数据已经没有意义,复位重置一下
                    s->buf_ptr = s->buffer;
                    s->buf_end = s->buffer/* + len*/;
                }
            } 
            // 不是s->direct为1,也不是size大于buffer的容量,则走正常的渠道,
            // 先将数据从文件中读取到AVIOContext.buffer中,再从buffer中读取
            // user buf中
            else 
            {
                // 从文件中读取数据到AVIOContext.buffer中
                fill_buffer(s);
                // AVIOContext.buffer中可用数据的字节数量
                len = s->buf_end - s->buf_ptr;
                if (len == 0)
                    break;
            }
        }
        else 
        {
            // 从AVIOContext.buffer中读取数据到user buf,这是最正常的情况
            memcpy(buf, s->buf_ptr, len);
            buf += len;
            s->buf_ptr += len;
            size -= len;
        }
    }
    
    if (size1 == size)
    {
        if (s->error)      return s->error;
        if (url_feof(s))   return AVERROR_EOF;
    }
    
    return size1 - size;
}

void avio_write(AVIOContext *s, const unsigned char *buf, int size)
{
    // s->direct 为1,则直接写入到文件中
    if (s->direct && !s->update_checksum) 
    {
        avio_flush(s);
        writeout(s, buf, size);
        return;
    }
    
    while (size > 0) 
    {
        int len = FFMIN(s->buf_end - s->buf_ptr, size);
        memcpy(s->buf_ptr, buf, len);
        s->buf_ptr += len;

// 正常情况下,当AVIOContext.buffer中数据满了,才将其中的数据写入文件
        if (s->buf_ptr >= s->buf_end)
            flush_buffer(s);

buf += len;
        size -= len;
    }
}

AVIO_FLAG_WRITE or AVIO_FLAG_READ_WRITE 时:

此时, write_flag == 1

ffmpeg(8) AVIOContext II相关推荐

  1. 【Android FFMPEG 开发】FFMPEG 初始化 ( 网络初始化 | 打开音视频 | 查找音视频流 )

    文章目录 I . FFMPEG 初始化流程 II . FFMPEG 网络初始化 avformat_network_init() III . FFMPEG 打开媒体地址 avformat_open_in ...

  2. 【FFmpeg】自定义回调函数处理AVIOContext中的数据

    1.简述 AVIOContext是FFmpeg管理输入输出数据的结构体,它的成员变量有指向数据的指针.大小以及处理数据的回调函数指针等等.如果使用avio_open或avio_open2来创建,它会根 ...

  3. ffmpeg系列-协议操作解析-AVIOContext,URLContext,URLProtocol,HTTPContext

    1.协议操作对象结构 协议操作对象结构: 协议操作的顶层结构是AVIOContext,这个对象实现了带缓冲的读写操作:FFMPEG的输入对象AVFormatContext的pb字段指向一个AVIOCo ...

  4. FFMPEG结构体分析:AVIOContext

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

  5. FFmpeg源码分析:AVIOContext、IO模型与协议

    FFmpeg的IO模型从avio_open()方法开始,核心结构体由AVIOContext和URLProtocol组成.如果需要读取缓冲区buffer数据进行播放,可以通过自定义AVIOContext ...

  6. 【FFmpeg】结构体详解(一):AVCodec、AVCodecContext、AVCodecParserContext、AVFrame、AVFormatContext 、AVIOContext

    FFmpeg结构体详解 一.FFmpeg中最关键的结构体之间的关系 1.解协议(http,rtsp,rtmp,mms) 2.解封装(flv,avi,rmvb,mp4) 3.解码(h264,mpeg2, ...

  7. FFmpeg API 变更记录

    最近一两年内FFmpeg项目发展的速度很快,本来是一件好事.但是随之而来的问题就是其API(接口函数)一直在发生变动.这么一来基于旧一点版本的FFmpeg的程序的代码在最新的类库上可能就跑不通了. 例 ...

  8. ffmpeg 4.2.1 版本升级日志 APIChanges

    ffmpeg的版本升级日志,可以查询各个版本的API变更情况,可以比对avcodec_version()返回结果确定版本等.该日志在ffmpeg源码的doc目录下.有的小伙伴可能没有源码,故在此发一份 ...

  9. ffmpeg framework

    目录(?)[+] ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结 ...

  10. ffmpeg的API函数变化记录

    最近在搞ffmpeg的时候发现下面函数出错: tutor.o: In function \`our_get_buffer\': tutor.c:530: undefined reference to ...

最新文章

  1. 什么是闭包?变量作用域和闭包。
  2. 关于MyEclipse连接SQLServer和Mariadbsql
  3. nyoj 710 外星人的供给站
  4. 我所碰到的智能手机自动重启的情况
  5. ElasticSearch API文档查看
  6. Vue2.0 的漫长学习ing-2-6
  7. KERMIT,XMODEM,YMODEM,ZMODEM传输协议小结
  8. linux测试游戏下载,一波超人内测版最新下载-一波超人内测版游戏下载v1.0.2-Linux公社...
  9. 开玩笑呢?学习KMP算法能改变自我认知? | 原力计划
  10. 开源 java CMS - FreeCMS2.3会员我的简历
  11. 警惕新型“二进制植入”漏洞,立即更新至 Npm 最新版本
  12. MWeb Pro for Mac(静态博客生成软件)
  13. python复制文件到指定文件夹并重命名_python文件、文件夹的移动、复制、删除、重命名...
  14. opera档案学习(一)
  15. 一些有意思的函数(连载中)
  16. Canto加速市场的发展,连接全球的金融衍生品市场
  17. 事务控制语言(DTL)
  18. 别出心裁的Linux系统调用学习法
  19. 在Js和Java自动生成账号的方法
  20. 文字排版--删除线(text-decoration:line-through)

热门文章

  1. Sailfish预研结果
  2. Cocos2d-x for Android iOS开发环境配置最佳实践
  3. asp.net删除cookie
  4. Spring中的两种AOP织入方式
  5. 多线程3,线程池封装库
  6. IOS 多个UIImageView 加载高清大图时内存管理
  7. iOS---实现在屏幕上实时绘图的简单效果---CAShaperLayer和UIBezierPath的简单运用
  8. C# 程序中使用 SQLite 数据库
  9. Centos7.4 安装Docker CE版
  10. 13.15. ftp fs