FFmpeg源码:avio_read函数分析

=================================================================

AVIOContext结构体和其相关的函数分析:

FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析

FFmpeg源码:read_packet_wrapper、fill_buffer函数分析

FFmpeg源码:avio_read函数分析

FFmpeg源码:avio_tell函数分析

=================================================================

一、avio_read函数的声明

avio_read函数声明在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的头文件libavformat/avio.h中:

/**
 * Read size bytes from AVIOContext into buf.
 * @return number of bytes read or AVERROR
 */
int avio_read(AVIOContext *s, unsigned char *buf, int size);

该函数的作用是:首先尝试从AVIOContext输入缓冲区中读取数据,如果输入缓冲区中没有数据或者数据已被读完或者读完后还不够size个字节,通过文件描述符去读取本地媒体文件中的数据或者通过socket接收网络流中的数据,保存到形参buf指向的缓冲区中。

形参s:既是输入型参数也是输出型参数。指向一个AVIOContext(字节流上下文结构体)变量。执行avio_read函数后,s->buf_ptr等成员会发生相应变化。

形参buf:输出型参数。保存读上来的数据的缓冲区。

形参size:输入型参数。要读取的字节数。

返回值:返回一个非负数表示成功,此时返回实际读取到的字节数;返回一个负数表示出错。

二、avio_read函数的定义

avio_read函数定义在源文件libavformat/aviobuf.c中:

int avio_read(AVIOContext *s, unsigned char *buf, int size)
{
    int len, size1;

    size1 = size;
    while (size > 0) {
        len = FFMIN(s->buf_end - s->buf_ptr, size);
        if (len == 0 || s->write_flag) {
            if((s->direct || size > s->buffer_size) && !s->update_checksum && s->read_packet) {
                // bypass the buffer and read data directly into buf
                len = read_packet_wrapper(s, buf, size);
                if (len == AVERROR_EOF) {
                    /* do not modify buffer if EOF reached so that a seek back can
                    be done without rereading data */
                    s->eof_reached = 1;
                    break;
                } else if (len < 0) {
                    s->eof_reached = 1;
                    s->error= len;
                    break;
                } else {
                    s->pos += len;
                    ffiocontext(s)->bytes_read += len;
                    s->bytes_read = ffiocontext(s)->bytes_read;
                    size -= len;
                    buf += len;
                    // reset the buffer
                    s->buf_ptr = s->buffer;
                    s->buf_end = s->buffer/* + len*/;
                }
            } else {
                fill_buffer(s);
                len = s->buf_end - s->buf_ptr;
                if (len == 0)
                    break;
            }
        } else {
            memcpy(buf, s->buf_ptr, len);
            buf += len;
            s->buf_ptr += len;
            size -= len;
        }
    }
    if (size1 == size) {
        if (s->error)      return s->error;
        if (avio_feof(s))  return AVERROR_EOF;
    }
    return size1 - size;
}

三、avio_read函数的内部实现分析

avio_read函数中,首先会判断AVIOContext输入缓冲区中还有多少数据未被读取(s->buf_end - s->buf_ptr),得到该值和“要读取的字节数(形参size的值)”中的最小值,保存到变量len中:

len = FFMIN(s->buf_end - s->buf_ptr, size);

如果变量len的值不为0,意味着AVIOContext输入缓冲区中还有数据未被读取,通过memcpy函数从AVIOContext输入缓冲区(s->buf_ptr指向的缓冲区)中拷贝len个字节到形参buf指向的缓冲区中:

        if (len == 0 || s->write_flag) {
//...
        } else {
            memcpy(buf, s->buf_ptr, len);
            buf += len;
            s->buf_ptr += len;
            size -= len;
        }

如果读完了AVIOContext输入缓冲区后还不满足要读取的字节数(形参size的值),调用fill_buffer函数( 关于fill_buffer函数的用法可以参考:《FFmpeg源码:read_packet_wrapper、fill_buffer函数分析》)使AVIOContext输入缓冲区得到新的数据,使得可以在下一次while循环中继续从AVIOContext输入缓冲区读取数据:

        if (len == 0 || s->write_flag) {
            if((s->direct || size > s->buffer_size) && !s->update_checksum && s->read_packet) {
//...
            } else {
                fill_buffer(s);
                len = s->buf_end - s->buf_ptr;
                if (len == 0)
                    break;
            }

如果要读取的数据大于AVIOContext输入缓冲区的最大大小( size > s->buffer_size),调用read_packet_wrapper函数直接对本地媒体文件或网络流进行读取,不把读上来的数据保存到AVIOContext输入缓冲区,而是直接保存到形参buf指向的缓冲区中:

if((s->direct || size > s->buffer_size) && !s->update_checksum && s->read_packet) {
// bypass the buffer and read data directly into buf
    len = read_packet_wrapper(s, buf, size);
//...
}

如果通过read_packet_wrapper函数读取成功,让s->pos增加实际读取到的字节数大小,重置AVIOContext输入缓冲区(让s->buf_ptr和s->buf_end都指向AVIOContext输入缓冲区的开头):

                len = read_packet_wrapper(s, buf, size);
                if (len == AVERROR_EOF) {
                    //...
                } else if (len < 0) {
                    //...
                } else {
                    s->pos += len;
                    ffiocontext(s)->bytes_read += len;
                    s->bytes_read = ffiocontext(s)->bytes_read;
                    size -= len;
                    buf += len;
                    // reset the buffer
                    s->buf_ptr = s->buffer;
                    s->buf_end = s->buffer/* + len*/;
                }

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CMake是一个跨平台的开源构建工具,可用于自动化管理项目的编译过程。FFmpeg是一个开源的音视频处理库,可以用来处理多种格式的音视频文件。而最后的下载的文件opencv_ffmpeg_64.dll是OpenCV库所需的FFmpeg动态链接库。 当我们在使用CMake构建一个项目时,可能会用到FFmpeg库来进行音视频处理。其中,OpenCV是一个广泛使用的计算机视觉库,它也能够使用FFmpeg进行音视频的编解码与处理。而opencv_ffmpeg_64.dll是OpenCV库所需的FFmpeg依赖库,这个库在运行OpenCV相关功能时需要被加载。 如果在使用CMake构建一个依赖于OpenCV和FFmpeg的项目时,若缺少opencv_ffmpeg_64.dll文件,可以通过下载获得该文件。可以通过在网上搜索opencv_ffmpeg_64.dll文件的下载链接,并将其下载到本地。下载完成后,将该文件放置在项目中指定的位置,一般来说是与其他的动态链接库(.dll文件)放在一起的。然后重新进行CMake构建,以确保项目能够正确加载该库文件。 需要注意的是,下载的文件必须与你的系统和项目的架构相匹配。例如,如果你的系统是64位的,那么你需要下载64位的opencv_ffmpeg_64.dll文件,而不是32位的。如果下载的文件与你的系统不匹配,可能会导致项目构建失败或运行时错误。 总结来说,下载opencv_ffmpeg_64.dll文件是为了满足OpenCV库在进行音视频处理时所依赖的FFmpeg库的加载需求。在使用CMake构建项目时,下载并正确放置该文件,可以确保项目能够正确运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值