FFmpeg源码:avio_read函数分析

2024-08-23 21:20

本文主要是介绍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 buflen = read_packet_wrapper(s, buf, size);if (len == AVERROR_EOF) {/* do not modify buffer if EOF reached so that a seek back canbe 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 buffers->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 buflen = 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 buffers->buf_ptr = s->buffer;s->buf_end = s->buffer/* + len*/;}

这篇关于FFmpeg源码:avio_read函数分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1100525

相关文章

Python+FFmpeg实现视频自动化处理的完整指南

《Python+FFmpeg实现视频自动化处理的完整指南》本文总结了一套在Python中使用subprocess.run调用FFmpeg进行视频自动化处理的解决方案,涵盖了跨平台硬件加速、中间素材处理... 目录一、 跨平台硬件加速:统一接口设计1. 核心映射逻辑2. python 实现代码二、 中间素材处

Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析

《SpringBootInterceptor的原理、配置、顺序控制及与Filter的关键区别对比分析》本文主要介绍了SpringBoot中的拦截器(Interceptor)及其与过滤器(Filt... 目录前言一、核心功能二、拦截器的实现2.1 定义自定义拦截器2.2 注册拦截器三、多拦截器的执行顺序四、过

C++ scoped_ptr 和 unique_ptr对比分析

《C++scoped_ptr和unique_ptr对比分析》本文介绍了C++中的`scoped_ptr`和`unique_ptr`,详细比较了它们的特性、使用场景以及现代C++推荐的使用`uni... 目录1. scoped_ptr基本特性主要特点2. unique_ptr基本用法3. 主要区别对比4. u

pandas使用apply函数给表格同时添加多列

《pandas使用apply函数给表格同时添加多列》本文介绍了利用Pandas的apply函数在DataFrame中同时添加多列,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录一、Pandas使用apply函数给表格同时添加多列二、应用示例一、Pandas使用apply函

Nginx内置变量应用场景分析

《Nginx内置变量应用场景分析》Nginx内置变量速查表,涵盖请求URI、客户端信息、服务器信息、文件路径、响应与性能等类别,这篇文章给大家介绍Nginx内置变量应用场景分析,感兴趣的朋友跟随小编一... 目录1. Nginx 内置变量速查表2. 核心变量详解与应用场景3. 实际应用举例4. 注意事项Ng

Java多种文件复制方式以及效率对比分析

《Java多种文件复制方式以及效率对比分析》本文总结了Java复制文件的多种方式,包括传统的字节流、字符流、NIO系列、第三方包中的FileUtils等,并提供了不同方式的效率比较,同时,还介绍了遍历... 目录1 背景2 概述3 遍历3.1listFiles()3.2list()3.3org.codeha

Python中Namespace()函数详解

《Python中Namespace()函数详解》Namespace是argparse模块提供的一个类,用于创建命名空间对象,它允许通过点操作符访问数据,比字典更易读,在深度学习项目中常用于加载配置、命... 目录1. 为什么使用 Namespace?2. Namespace 的本质是什么?3. Namesp

MySQL中如何求平均值常见实例(AVG函数详解)

《MySQL中如何求平均值常见实例(AVG函数详解)》MySQLavg()是一个聚合函数,用于返回各种记录中表达式的平均值,:本文主要介绍MySQL中用AVG函数如何求平均值的相关资料,文中通过代... 目录前言一、基本语法二、示例讲解1. 计算全表平均分2. 计算某门课程的平均分(例如:Math)三、结合

Nginx分布式部署流程分析

《Nginx分布式部署流程分析》文章介绍Nginx在分布式部署中的反向代理和负载均衡作用,用于分发请求、减轻服务器压力及解决session共享问题,涵盖配置方法、策略及Java项目应用,并提及分布式事... 目录分布式部署NginxJava中的代理代理分为正向代理和反向代理正向代理反向代理Nginx应用场景

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4