当前位置: 首页 > news >正文

FFmpeg源码:avio_seek函数分析

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

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

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

FFmpeg源码:read_packet_wrapper、fill_buffer函数分析

FFmpeg源码:avio_read函数分析

FFmpeg源码:avio_seek函数分析

FFmpeg源码:avio_skip函数分析

FFmpeg源码:avio_tell函数分析

FFmpeg源码:ffurl_seek2、ffurl_seek、avio_size函数分析

FFmpeg源码:avio_feof函数分析

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

一、avio_seek函数的声明

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

/*** fseek() equivalent for AVIOContext.* @return new position or AVERROR.*/
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence);

该函数可以看做是AVIOContext版本的fseek函数,根据传入形参whence的值的不同,avio_seek函数的作用也不同。

情况一:whence的值为SEEK_SET时,执行avio_seek函数后,如果执行成功,AVIOContext的文件位置指针将指向以文件开头为基准,偏移offset(指针偏移量)个字节的位置。

情况二:whence的值为SEEK_CUR时,执行avio_seek函数后,如果执行成功,AVIOContext的文件位置指针将指向以当前位置为基准,偏移offset(指针偏移量)个字节的位置。

情况三:whence的值为SEEK_END时,执行avio_seek函数后,如果执行成功,AVIOContext的文件位置指针将指向以文件结尾为基准,偏移offset(指针偏移量)个字节的位置。

形参s:既是输入型参数也是输出型参数。指向一个AVIOContext(字节流上下文结构体)变量。关于AVIOContext结构体可以参考:《FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析》。

形参offset:输入型参数,表示偏移量。正数表示正向偏移,负数表示负向偏移。

形参whence:输入型参数,设定从文件的哪里开始偏移,可能取值为:

/* The possibilities for the third argument to `fseek'.These values should not be changed.  */
#define SEEK_SET	0	/* Seek from beginning of file.  */
#define SEEK_CUR	1	/* Seek from current position.  */
#define SEEK_END	2	/* Seek from end of file.  */

SEEK_SET: 文件开头
SEEK_CUR: 当前位置
SEEK_END: 文件结尾

返回值:失败返回一个负数,成功返回偏移值。

二、avio_seek函数的使用例子

avio_seek(s,100,SEEK_SET):把AVIOContext的文件位置指针移动到离文件开头100字节处;


avio_seek(s,100,SEEK_CUR):把AVIOContext的文件位置指针移动到离文件当前位置100字节处;


avio_seek(s,-100,SEEK_END):把AVIOContext的文件位置指针退回到离文件结尾100字节处。

三、avio_seek函数的定义

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

int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
{FFIOContext *const ctx = ffiocontext(s);int64_t offset1;int64_t pos;int force = whence & AVSEEK_FORCE;int buffer_size;int short_seek;whence &= ~AVSEEK_FORCE;if(!s)return AVERROR(EINVAL);if ((whence & AVSEEK_SIZE))return s->seek ? s->seek(s->opaque, offset, AVSEEK_SIZE) : AVERROR(ENOSYS);buffer_size = s->buf_end - s->buffer;// pos is the absolute position that the beginning of s->buffer corresponds to in the filepos = s->pos - (s->write_flag ? 0 : buffer_size);if (whence != SEEK_CUR && whence != SEEK_SET)return AVERROR(EINVAL);if (whence == SEEK_CUR) {offset1 = pos + (s->buf_ptr - s->buffer);if (offset == 0)return offset1;if (offset > INT64_MAX - offset1)return AVERROR(EINVAL);offset += offset1;}if (offset < 0)return AVERROR(EINVAL);short_seek = ctx->short_seek_threshold;if (ctx->short_seek_get) {int tmp = ctx->short_seek_get(s->opaque);short_seek = FFMAX(tmp, short_seek);}offset1 = offset - pos; // "offset1" is the relative offset from the beginning of s->buffers->buf_ptr_max = FFMAX(s->buf_ptr_max, s->buf_ptr);if ((!s->direct || !s->seek) &&offset1 >= 0 && offset1 <= (s->write_flag ? s->buf_ptr_max - s->buffer : buffer_size)) {/* can do the seek inside the buffer */s->buf_ptr = s->buffer + offset1;} else if ((!(s->seekable & AVIO_SEEKABLE_NORMAL) ||offset1 <= buffer_size + short_seek) &&!s->write_flag && offset1 >= 0 &&(!s->direct || !s->seek) &&(whence != SEEK_END || force)) {while(s->pos < offset && !s->eof_reached)fill_buffer(s);if (s->eof_reached)return AVERROR_EOF;s->buf_ptr = s->buf_end - (s->pos - offset);} else if(!s->write_flag && offset1 < 0 && -offset1 < buffer_size>>1 && s->seek && offset > 0) {int64_t res;pos -= FFMIN(buffer_size>>1, pos);if ((res = s->seek(s->opaque, pos, SEEK_SET)) < 0)return res;s->buf_end =s->buf_ptr = s->buffer;s->pos = pos;s->eof_reached = 0;fill_buffer(s);return avio_seek(s, offset, SEEK_SET | force);} else {int64_t res;if (s->write_flag) {flush_buffer(s);}if (!s->seek)return AVERROR(EPIPE);if ((res = s->seek(s->opaque, offset, SEEK_SET)) < 0)return res;ctx->seek_count++;if (!s->write_flag)s->buf_end = s->buffer;s->buf_ptr = s->buf_ptr_max = s->buffer;s->pos = offset;}s->eof_reached = 0;return offset;
}

四、avio_seek函数的内部实现原理

下面分情况讨论:

(一)whence的值为SEEK_SET

whence的值为SEEK_SET时,avio_seek函数可以化简为:

int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
{FFIOContext *const ctx = ffiocontext(s);int64_t offset1;int64_t pos;int buffer_size;if(!s)return AVERROR(EINVAL);buffer_size = s->buf_end - s->buffer;pos = s->pos - (s->write_flag ? 0 : buffer_size);if (offset < 0)return AVERROR(EINVAL);offset1 = offset - pos;s->buf_ptr_max = FFMAX(s->buf_ptr_max, s->buf_ptr);if ((!s->direct || !s->seek) && offset1 >= 0 && offset1 <= (s->write_flag ? s->buf_ptr_max - s->buffer : buffer_size)) {/* can do the seek inside the buffer */s->buf_ptr = s->buffer + offset1;}s->eof_reached = 0;return offset;
}

大部分情况下,pos的值为0:

    pos = s->pos - (s->write_flag ? 0 : buffer_size);

这时offset1 = offset - pos

= offset - 0

= offset:

    offset1 = offset - pos;

所以这时s->buf_ptr = s->buffer + offset1

= s->buffer + offset:

        s->buf_ptr = s->buffer + offset1;

所以大部分情况下,执行语句avio_seek(s->pb, offset, SEEK_SET)后,s->pb->buf_ptr的值会变为s->pb->buffer + offset。从而实现让AVIOContext的文件位置指针(s->pb->buf_ptr)指向以文件开头(s->pb->buffer)为基准,偏移offset(指针偏移量)个字节的位置。即把文件位置指针移动到离文件开头offset字节处。

(二)whence的值为SEEK_CUR

whence的值为SEEK_CUR时,avio_seek函数可以化简为:

int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
{FFIOContext *const ctx = ffiocontext(s);int64_t offset1;int64_t pos;int buffer_size;if(!s)return AVERROR(EINVAL);buffer_size = s->buf_end - s->buffer;pos = s->pos - (s->write_flag ? 0 : buffer_size);if (whence == SEEK_CUR) {offset1 = pos + (s->buf_ptr - s->buffer);if (offset == 0)return offset1;if (offset > INT64_MAX - offset1)return AVERROR(EINVAL);offset += offset1;}if (offset < 0)return AVERROR(EINVAL);offset1 = offset - pos;s->buf_ptr_max = FFMAX(s->buf_ptr_max, s->buf_ptr);if ((!s->direct || !s->seek) &&offset1 >= 0 && offset1 <= (s->write_flag ? s->buf_ptr_max - s->buffer : buffer_size)) {s->buf_ptr = s->buffer + offset1;} s->eof_reached = 0;return offset;
}

大部分情况下,pos的值为0:

    pos = s->pos - (s->write_flag ? 0 : buffer_size);

这时offset1 = s->buf_ptr - s->buffer。s->buf_ptr - s->buffer为AVIOContext输入缓冲区中当前读取到的位置距离输入缓冲区的开头的偏移。即文件位置指针当前位置(s->buf_ptr)相对于文件首(s->buffer)的偏移字节数:

        offset1 = pos + (s->buf_ptr - s->buffer);

所以这时offset = offset  + offset1

= offset + (s->buf_ptr - s->buffer):

offset += offset1;

这时让offset1 = offset - pos

= offset - 0

= offset

= 最开始的形参offset的值 + (s->buf_ptr - s->buffer) :

offset1 = offset - pos; 

所以这时s->buf_ptr = s->buffer + offset1

= s->buffer + 最开始的形参offset的值 + (s->buf_ptr - s->buffer)

= s->buf_ptr + 最开始的形参offset的值:

s->buf_ptr = s->buffer + offset1;

所以大部分情况下,执行语句avio_seek(s->pb, offset, SEEK_CUR)后,s->pb->buf_ptr的值会变为s->pb->buf_ptr+ offset。从而实现让AVIOContext的文件位置指针(s->pb->buf_ptr)指向以当前位置(s->pb->buf_ptr)为基准,偏移offset(指针偏移量)个字节的位置。即把文件位置指针移动到离文件当前位置offset字节处。

(三)whence的值为SEEK_END

暂略,后续补充。

相关文章:

  • 【Linux 23】线程池
  • golang fmt.Sprintf 引用前述变量
  • 远程服务器安装anaconda并创建虚拟环境
  • Mybatis的基本使用
  • SSTI基础
  • “大数据+高职”:VR虚拟仿真实训室的发展前景
  • 动态库的加载全过程
  • 深入浅出MySQL事务处理:从基础概念到ACID特性及并发控制
  • Ubuntu24.04.1系统下VideoMamba环境配置
  • Certbot自动申请并续期https证书
  • Linux基础(二):磁盘分区
  • Rust调用tree-sitter支持自定义语言解析
  • 等保2.0数据库测评之达梦数据库测评
  • 启动hadoop集群出现there is no HDFS_NAMENODE_USER defined.Aborting operation
  • go 使用笔记
  • 时间复杂度分析经典问题——最大子序列和
  • 2017届校招提前批面试回顾
  • Android Volley源码解析
  • Babel配置的不完全指南
  • Go 语言编译器的 //go: 详解
  • Javascript Math对象和Date对象常用方法详解
  • JavaScript类型识别
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • mysql中InnoDB引擎中页的概念
  • oschina
  • text-decoration与color属性
  • Windows Containers 大冒险: 容器网络
  • 服务器从安装到部署全过程(二)
  • 前端面试总结(at, md)
  • 三栏布局总结
  • 一天一个设计模式之JS实现——适配器模式
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • ​马来语翻译中文去哪比较好?
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • # Maven错误Error executing Maven
  • #php的pecl工具#
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • (13)Latex:基于ΤΕΧ的自动排版系统——写论文必备
  • (二)测试工具
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (利用IDEA+Maven)定制属于自己的jar包
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • (十三)Flink SQL
  • (一)Docker基本介绍
  • (已解决)vscode如何选择python解释器
  • .NET Core中的去虚
  • .NET关于 跳过SSL中遇到的问题
  • .NET开发不可不知、不可不用的辅助类(一)
  • .one4-V-XXXXXXXX勒索病毒数据怎么处理|数据解密恢复
  • .pings勒索病毒的威胁:如何应对.pings勒索病毒的突袭?
  • @JsonFormat 和 @DateTimeFormat 的区别