ffmpeg(8) AVIOContext II
来源:互联网 发布:淘宝店铺宝贝分类模板 编辑:程序博客网 时间:2024/04/27 19:59
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
- ffmpeg(8) AVIOContext II
- ffmpeg(3) AVIOContext
- ffmpeg(3) AVIOContext
- ffmpeg之AVIOContext
- FFMPEG结构体分析:AVIOContext
- FFMPEG结构体分析:AVIOContext
- FFMPEG结构体分析:AVIOContext
- FFMPEG结构体分析 AVIOContext
- FFMPEG结构体分析:AVIOContext
- FFMPEG结构体分析:AVIOContext
- AVIOContext
- AVIOContext
- ffmpeg结构体熟悉——AVIOContext
- ffmpeg重要结构体之AVIOContext
- FFmpeg总结(七)AV系列结构体之AVIOContext
- FFmpeg总结(七)AV系列结构体之AVIOContext
- ffmpeg框架阅读笔记二 : 寻找AVIOContext初始化过程,自定义初始化。
- 爆裂吧世界(world)
- 人生需要不断的历练,restart
- jvm基础学习
- dp_5
- 关于C的指针,Java/Python的引用,形参与实参个人理解
- ffmpeg(8) AVIOContext II
- ASP.NET内置对象
- java中的session介绍
- Git stash
- 如何查看linux命令源代码和函数源代码
- (转)无人驾驶的感知
- ZooKeeper命令行工具zkCli的使用
- opencv的学习——显示图像
- Eclipse Neon 配置文件优化 - 瞎搞篇