ffmpeg(8) AVIOContext II
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相关推荐
- 【Android FFMPEG 开发】FFMPEG 初始化 ( 网络初始化 | 打开音视频 | 查找音视频流 )
文章目录 I . FFMPEG 初始化流程 II . FFMPEG 网络初始化 avformat_network_init() III . FFMPEG 打开媒体地址 avformat_open_in ...
- 【FFmpeg】自定义回调函数处理AVIOContext中的数据
1.简述 AVIOContext是FFmpeg管理输入输出数据的结构体,它的成员变量有指向数据的指针.大小以及处理数据的回调函数指针等等.如果使用avio_open或avio_open2来创建,它会根 ...
- ffmpeg系列-协议操作解析-AVIOContext,URLContext,URLProtocol,HTTPContext
1.协议操作对象结构 协议操作对象结构: 协议操作的顶层结构是AVIOContext,这个对象实现了带缓冲的读写操作:FFMPEG的输入对象AVFormatContext的pb字段指向一个AVIOCo ...
- FFMPEG结构体分析:AVIOContext
注:写了一系列的结构体的分析的文章,在这里列一个列表: FFMPEG结构体分析:AVFrame FFMPEG结构体分析:AVFormatContext FFMPEG结构体分析:AVCodecConte ...
- FFmpeg源码分析:AVIOContext、IO模型与协议
FFmpeg的IO模型从avio_open()方法开始,核心结构体由AVIOContext和URLProtocol组成.如果需要读取缓冲区buffer数据进行播放,可以通过自定义AVIOContext ...
- 【FFmpeg】结构体详解(一):AVCodec、AVCodecContext、AVCodecParserContext、AVFrame、AVFormatContext 、AVIOContext
FFmpeg结构体详解 一.FFmpeg中最关键的结构体之间的关系 1.解协议(http,rtsp,rtmp,mms) 2.解封装(flv,avi,rmvb,mp4) 3.解码(h264,mpeg2, ...
- FFmpeg API 变更记录
最近一两年内FFmpeg项目发展的速度很快,本来是一件好事.但是随之而来的问题就是其API(接口函数)一直在发生变动.这么一来基于旧一点版本的FFmpeg的程序的代码在最新的类库上可能就跑不通了. 例 ...
- ffmpeg 4.2.1 版本升级日志 APIChanges
ffmpeg的版本升级日志,可以查询各个版本的API变更情况,可以比对avcodec_version()返回结果确定版本等.该日志在ffmpeg源码的doc目录下.有的小伙伴可能没有源码,故在此发一份 ...
- ffmpeg framework
目录(?)[+] ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结 ...
- ffmpeg的API函数变化记录
最近在搞ffmpeg的时候发现下面函数出错: tutor.o: In function \`our_get_buffer\': tutor.c:530: undefined reference to ...
最新文章
- 什么是闭包?变量作用域和闭包。
- 关于MyEclipse连接SQLServer和Mariadbsql
- nyoj 710 外星人的供给站
- 我所碰到的智能手机自动重启的情况
- ElasticSearch API文档查看
- Vue2.0 的漫长学习ing-2-6
- KERMIT,XMODEM,YMODEM,ZMODEM传输协议小结
- linux测试游戏下载,一波超人内测版最新下载-一波超人内测版游戏下载v1.0.2-Linux公社...
- 开玩笑呢?学习KMP算法能改变自我认知? | 原力计划
- 开源 java CMS - FreeCMS2.3会员我的简历
- 警惕新型“二进制植入”漏洞,立即更新至 Npm 最新版本
- MWeb Pro for Mac(静态博客生成软件)
- python复制文件到指定文件夹并重命名_python文件、文件夹的移动、复制、删除、重命名...
- opera档案学习(一)
- 一些有意思的函数(连载中)
- Canto加速市场的发展,连接全球的金融衍生品市场
- 事务控制语言(DTL)
- 别出心裁的Linux系统调用学习法
- 在Js和Java自动生成账号的方法
- 文字排版--删除线(text-decoration:line-through)