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

相关文章

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

Python itertools中accumulate函数用法及使用运用详细讲解

《Pythonitertools中accumulate函数用法及使用运用详细讲解》:本文主要介绍Python的itertools库中的accumulate函数,该函数可以计算累积和或通过指定函数... 目录1.1前言:1.2定义:1.3衍生用法:1.3Leetcode的实际运用:总结 1.1前言:本文将详

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

MySQL数据库函数之JSON_EXTRACT示例代码

《MySQL数据库函数之JSON_EXTRACT示例代码》:本文主要介绍MySQL数据库函数之JSON_EXTRACT的相关资料,JSON_EXTRACT()函数用于从JSON文档中提取值,支持对... 目录前言基本语法路径表达式示例示例 1: 提取简单值示例 2: 提取嵌套值示例 3: 提取数组中的值注意

C#使用DeepSeek API实现自然语言处理,文本分类和情感分析

《C#使用DeepSeekAPI实现自然语言处理,文本分类和情感分析》在C#中使用DeepSeekAPI可以实现多种功能,例如自然语言处理、文本分类、情感分析等,本文主要为大家介绍了具体实现步骤,... 目录准备工作文本生成文本分类问答系统代码生成翻译功能文本摘要文本校对图像描述生成总结在C#中使用Deep

Java function函数式接口的使用方法与实例

《Javafunction函数式接口的使用方法与实例》:本文主要介绍Javafunction函数式接口的使用方法与实例,函数式接口如一支未完成的诗篇,用Lambda表达式作韵脚,将代码的机械美感... 目录引言-当代码遇见诗性一、函数式接口的生物学解构1.1 函数式接口的基因密码1.2 六大核心接口的形态学

Go中sync.Once源码的深度讲解

《Go中sync.Once源码的深度讲解》sync.Once是Go语言标准库中的一个同步原语,用于确保某个操作只执行一次,本文将从源码出发为大家详细介绍一下sync.Once的具体使用,x希望对大家有... 目录概念简单示例源码解读总结概念sync.Once是Go语言标准库中的一个同步原语,用于确保某个操

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis主从复制的原理分析

《Redis主从复制的原理分析》Redis主从复制通过将数据镜像到多个从节点,实现高可用性和扩展性,主从复制包括初次全量同步和增量同步两个阶段,为优化复制性能,可以采用AOF持久化、调整复制超时时间、... 目录Redis主从复制的原理主从复制概述配置主从复制数据同步过程复制一致性与延迟故障转移机制监控与维