博客摘录「 Nuplayer 音视频同步学习笔记」2024年4月9日

4. AVsync Audio更新锚点时间
(1) AVsync原理
系统时间和媒体时间应该是线性关系: (mediaTimeUs - anchorTimeMediaUs) = PlaybackRate*(nowUs - anchorTimeRealUs)。

所以理论上,我们可以根据锚点, 计算出任意一点的媒体时间对应的系统时间(Buffer应该播放的时间). AVsync 的进本机理就是通过Audio每隔一段时间更新锚点, Video的Buffer根据锚点和媒体时间计算出应该播放的时(系统时间)

更新锚点时间 anchorTimeMediaUs anchorTimeRealUs
anchorTimeRealUs  更新锚点时的 系统时间
anchorTimeMediaUs 更新锚点时的 媒体时间(正在播放的媒体的时间)
下面我们主要来看一下Audio如何更新锚点

(2) Audio如何更新锚点
anchorTimeRealUs = nowUs
锚点系统时间被设置为当前系统时间, 所以 anchorTimeMediaUs 就应该当前正在播放的媒体时间,接下来重点是如何确定anchorTimeMediaUs. 

<1> 如何计算当前正在播放的媒体时间

当前正在播放的媒体时间 = 正在写入的媒体时间 - 已经写入但是没有播放的数据需要播放的时间
pendingAudioPlayoutDurationUs: 已经写入但是没有播放的数据需要播放的时间(主要是在AudioBuffer里面的数据)
anchorTimeMediaUs = mediaTimeUs - pendingAudioPlayoutDurationUs;

<2> 计算已经写入但是没有播放的数据需要播放的时间
writtenAudioDurationUs: 已经写入的数据的持续时间
PlayedOutDurationUs: 当前已经播放的数据的持续时间
两者相减就是pendingAudioPlayoutDurationUs
pendingAudioPlayoutDurationUs = writtenAudioDurationUs - PlayedOutDurationUs
<3> 已经写入的数据的持续时间
mNumFramesWritten 已经写入的数据的帧的个数 * 每一帧多少时间(1000000LL / sampleRate)
writtenAudioDurationUs = mNumFramesWritten * (1000000LL / sampleRate)
 <4> 计算当前已经播放的数据的持续时间
ts.mPosition * 1000000LL/mSampleRateHz 在 ts.mTime时间已经播放的数据的时间
因为nowUs与ts.mTime不相等, 最后需要根据nowUs进行调整
PlayedOutDurationUs = ts.mPosition * 1000000LL/mSampleRateHz + nowUs - ts.mTime

void NuPlayer::Renderer::onNewAudioMediaTime(int64_t mediaTimeUs)// 设置初始锚点媒体时间setAudioFirstAnchorTimeIfNeeded_l(mediaTimeUs);int64_t nowUs = ALooper::GetNowUs();if (mNextAudioClockUpdateTimeUs >= 0)//是否需要更新锚点时间, 根据kMinimumAudioClockUpdatePeriodUs的时间if (nowUs >= mNextAudioClockUpdateTimeUs) //  (1) nowMediaUs:  当前正在播放的媒体时间//  mediaTimeUs: 当前正在写入到Audio的数据的媒体时间//  getPendingAudioPlayoutDurationUs //  已经写入到Audio但是还没有播放的数据持续时间int64_t nowMediaUs = mediaTimeUs - getPendingAudioPlayoutDurationUs(nowUs);//  根据nowMediaUs和mediaTimeUs更新锚点时间mMediaClock->updateAnchor(nowMediaUs, nowUs, mediaTimeUs);mNextAudioClockUpdateTimeUs = nowUs + kMinimumAudioClockUpdatePeriodUs;// (2) 需要分析一下getPendingAudioPlayoutDurationUs
// 计算方法使用 writtenAudioDurationUs - PlayedOutDurationUs
int64_t NuPlayer::Renderer::getPendingAudioPlayoutDurationUs(int64_t nowUs)// (3) writtenAudioDurationUs 已经写入的数据的持续时间// mNumFramesWritten * (1000000LL / sampleRate)// 写入的帧的个数 * 每一帧的持续时间(us)// (1000000LL / sampleRate): sampleRate一秒的采样数, 取倒数每一个采样的持续时间int64_t writtenAudioDurationUs = getDurationUsIfPlayedAtSampleRate(mNumFramesWritten);// PlayedOutDurationUs 已经播放时间, 需要从Audio侧获取return writtenAudioDurationUs - mAudioSink->getPlayedOutDurationUs(nowUs);//(4) 当前已经播放的数据的持续时间
int64_t MediaPlayerService::AudioOutput::getPlayedOutDurationUs(int64_t nowUs)// 从Audio侧获取已经获取当前播放的帧,和对应的系统时间// 注意ts.mPosition并不是正在播放的帧的位置, // 应该是ts.mTime这个系统时间点正在播放的帧的位置// ts.mTime与当前时间nowUs并不相等,会有ms级的差别status_t res = mTrack->getTimestamp(ts);if (res == OK)numFramesPlayed = ts.mPosition;numFramesPlayedAt = ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;//  numFramesPlayed * 1000000LL / mSampleRateHz: ts.mTime时间点已经播放的时间// 最后计算的时候需要考虑ts.mTime与当前时间nowUs之间的差异// durationUs  nowUs时间点已经播放的时间(正在播放的媒体时间)int64_t durationUs = (int64_t)((int32_t)numFramesPlayed * 1000000LL / mSampleRateHz) + nowUs - numFramesPlayedAt;// (5) 调用MediaClock::updateAnchor更新锚点
//    传入参数:
//    anchorTimeMediaUs     正在播放的媒体时间
//    anchorTimeRealUs      anchorTimeMediaUs对应的系统时间
void MediaClock::updateAnchor(int64_t anchorTimeMediaUs, int64_t anchorTimeRealUs, int64_t maxTimeMediaUs)// 获得当前的系统时间, 可能与anchorTimeRealUs有差别int64_t nowUs = ALooper::GetNowUs();// 获得当前正在播放的媒体时间 nowMediaUsint64_t nowMediaUs =anchorTimeMediaUs + (nowUs - anchorTimeRealUs) * (double)mPlaybackRate;// 更新当前播放的媒体时间为锚点媒体时间// 更新当前系统时间为锚点系统时间mAnchorTimeRealUs = nowUs;mAnchorTimeMediaUs = nowMediaUs;

5. AVsync Video 获取显示的时间(系统时间)
     AVsync的目的是获得Buffer显示的时间(buffer的系统时间).我们可以根据媒体时间和系统时间的线性关系计算出显示的时间
(mediaTimeUs - anchorTimeMediaUs) = PlaybackRate*(nowUs - anchorTimeMediaUs)

(1) nowMediaUs 当前播放媒体时间
根据当前的系统时间和锚点时间计算出当前播放媒体时间
nowMediaUs = mAnchorTimeMediaUs + (realUs - mAnchorTimeRealUs) * mPlaybackRate

(2) outRealUs Buffer的显示时间
根据当前播放的媒体时间和系统时间, 计算出Buffer的显示时间(系统时间)
outRealUs = (targetMediaUs - nowMediaUs) / (double)mPlaybackRate + nowUs

 

// outRealUs 获得当前Buffer播放的系统时间(应该在这个时间点播放)
// 传入参数 targetMediaUs. 当前Buffer的媒体时间
status_t MediaClock::getRealTimeFor(int64_t targetMediaUs, int64_t *outRealUs)int64_t nowUs = ALooper::GetNowUs();// (1) nowMediaUs video正在播放的媒体时间, nowUs对应的媒体时间getMediaTime_l(nowUs, &nowMediaUs, true /* allowPastMaxTime */);// (2) 计算出Buffer的显示时间*outRealUs = (targetMediaUs - nowMediaUs) / (double)mPlaybackRate + nowUs;// outMediaUs video正在播放的媒体时间, nowUs对应的媒体时间
// realUs  当前系统时间
status_t MediaClock::getMediaTime_l(int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime)//mediaUs 当前Audio正在播放的媒体时间, 对应video正在播放的媒体时间int64_t mediaUs = mAnchorTimeMediaUs+ (realUs - mAnchorTimeRealUs) * (double)mPlaybackRate;*outMediaUs = mediaUs;

摘自:Nuplayer 音视频同步学习笔记_ondrainaudioqueue-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/22020.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

三维模型轻量化工具:手工模型、BIM、倾斜摄影等皆可用!

老子云是全球领先的数字孪生引擎技术及服务提供商&#xff0c;它专注于让一切3D模型在全网多端轻量化处理与展示&#xff0c;为行业数字化转型升级与数字孪生应用提供成套的3D可视化技术、产品与服务。 老子云是全球领先的数字孪生引擎技术及服务提供商&#xff0c;它专注于让…

Docker安装Bitbucket

centos7版本 [rootlocalhost ~]# cat /etc/os-release NAME"CentOS Linux" VERSION"7 (Core)" ID"centos" ID_LIKE"rhel fedora" VERSION_ID"7" PRETTY_NAME"CentOS Linux 7 (Core)" ANSI_COLOR"0;31"…

sectigo和certum ip ssl证书的区别

IP SSL证书是一种数字证书&#xff0c;为客户端和服务器之间的信息传输提供加密服务。但是和应用比较广泛的域名SSL证书相比&#xff0c;IP SSL证书是为只有公网IP地址的网站准备的数字证书。市场上常见的IP SSL证书品牌就是Sectigo和Certum&#xff0c;那么&#xff0c;这两种…

[AI Google] Ask Photos: 使用Gemini搜索照片的新方法

借助Gemini模型&#xff0c;将Google Photos提升到一个新的水平。 Google Photos是我们最早以AI为核心构建的产品之一&#xff0c;让你能够搜索照片和视频中的人、宠物、地点等。现在&#xff0c;我们通过我们最强大的AI模型Gemini对Google Photos进行重大升级。通过Ask Photos…

【C/C++】——小白初步了解——内存管理

目录 1. C/C内存分布 代码区&#xff08;Code Segment&#xff09;&#xff1a; 数据区&#xff08;Data Segment&#xff09;&#xff1a; 堆区&#xff08;Heap&#xff09;&#xff1a; 栈区&#xff08;Stack&#xff09;&#xff1a; 常量区&#xff08;Constant Seg…

数据中心横向虚拟化 M-LAG 技术

M-LAG 一、M-LAG概述 1、M-LAG定义&#xff1a;M-LAG&#xff08;Multichassis Link Aggregation Group&#xff09;是跨设备链路聚合组。可以将两台设备进行跨设备链路聚合&#xff0c;从而把链路的可靠性从单板机提高到了设备级。 2、优势&#xff1a; (1)、M-LAG系统的两台…

UE 打包报错 MarketplaceRules.dll‘ does not exist.

Precompiled rules assembly /Users/unity/Library/Application Support/Epic/UnrealEngine/Intermediate/Build/BuildRules/MarketplaceRules.dll does not exist. Window下找到该DLL 拷到Mac对应的目录下即可。如没有则需要手动创建相应的文件夹 /Users/unity/Library/Appl…

# 全面解剖 消息中间件 RocketMQ-(5)

全面解剖 消息中间件 RocketMQ-&#xff08;5&#xff09; 一、RocketMQ &#xff1a;过滤消息的两种方式 1、Tag 过滤 在大多数情况下&#xff0c;TAG 是一个简单而有用的设计&#xff0c;其可以来选择您想要的消息。 例如: DefaultMoPushconsumer consumer new DefaultM…

vuePC 录制桌面 并下载到本地

页面代码 <button click"startRecording">开始录制桌面</button> <button click"stopRecording" :disabled"!isRecording">结束录制</button> js代码 // 录制桌面 保存到本地 methods&#xff1a;{async startRecordi…

文件夹突变解析:类型变文件的数据恢复与预防

在数字化时代&#xff0c;文件夹作为我们存储和组织数据的基本单元&#xff0c;其重要性不言而喻。然而&#xff0c;有时我们可能会遇到一种令人困惑的情况——文件夹的类型突然变为文件&#xff0c;导致无法正常访问其中的内容。这种现象不仅会影响我们的工作效率&#xff0c;…

[MySQL最详细的知识点]

MySQL 关系型数据库以一行作为一个记录,列数据库以一列为一个记录一行是一个记录,一列是一个字段一行是一个实体,一列是一个属性 MySQL引擎: MySQL引擎&#xff1a;可以理解为&#xff0c;MySQL的“文件系统”&#xff0c;只不过功能更加强大。​MySQL引擎功能&#xff1a;除…

mysql 分区

目标 给一个表&#xff08;半年有800万&#xff09;增加分区以增加查询速度 约束 分区不能有外键否则会报错 https://blog.csdn.net/yabingshi_tech/article/details/52241034 主键 按照时间列进行分区 https://blog.csdn.net/winerpro/article/details/135736454 参看以…

微波炉触摸芯片的工作原理及技术特点

随着科技的不断发展&#xff0c;微波炉已经成为现代家庭中不可或缺的厨房电器之一。而触摸面板作为微波炉的重要组成部分&#xff0c;其操作的便捷性和灵敏度直接影响用户的使用体验。在触摸面板的设计中&#xff0c;触摸芯片起着关键的作用。本文将深入探讨微波炉触摸面板中触…

安全测试 之 常见安全漏洞:CORS

1. 背景 安全测试定义&#xff1a;安全测试&#xff0c;是在软件产品开发基本完成时&#xff0c;验证产品是否符合安全需求定义和产品质量标准的过程。目的&#xff1a;通过对系统进行全面的脆弱性安全测试&#xff0c;发现系统未知的安全隐患并提出相关建议&#xff0c;确保系…

BUAA操作系统万字笔记-课堂笔记-期末考试-考研必备-北航961系列

文章目录 1 概论1.1 CPU漏洞攻击1.2 操作系统简史1.2.1 体系结构1.2.2 系统发展 1.3 操作系统基本实现机制1.3.1 异常&#xff1a;陷阱和中断 2 系统引导3 内存管理3.1 预备知识-链接与装载3.2 存储管理基础3.2.1 存储器管理目标3.2.2 存储器硬件发展3.2.3 存储管理的功能3.2.4…

【python】成功解决“TypeError: not enough arguments for format string”错误的全面指南

成功解决“TypeError: not enough arguments for format string”错误的全面指南 一、引言 在Python编程中&#xff0c;TypeError: not enough arguments for format string错误是一个常见的字符串格式化问题。这个错误通常发生在使用str.format()方法时&#xff0c;提供的参数…

frp之XTCP实现内网穿透家用电脑远程桌面公司电脑

官网XTCP介绍 《XTCP介绍》 实现图 fprs.toml # frps 服务端口&#xff08;不填&#xff0c;则默认&#xff1a;7000&#xff09; bindPort 81 auth.token "token 令牌"公司电脑frpc.toml serverAddr "frps公网服务器域名或ip" serverPort frps 服…

Java图形用户界面程序设计所需要使用的工具

Java图形用户界面程序设计 前言一、图形用户界面程序设计的概述GUI概述Java GUI技术的发展 二、AWT概述简介AWT继承体系总结 三、Swing概述Swing概述优势Swing的特征总结 前言 推荐一个网站给想要了解或者学习人工智能知识的读者&#xff0c;这个网站里内容讲解通俗易懂且风趣…

docker安装RabbitMQ及整合使用

1. docker安装RabbitMQ docker下载及配置环境 docker pull rabbitmq:management # 创建用于挂载的目录 mkdir -p /home/docker/rabbitmq/{data,conf,log} # 创建完成之后要对所创建文件授权权限,都设置成777 否则在启动容器的时候容易失败 chmod -R 777 /home/docker/rabbit…

普通最小二乘法的推导证明

普通最小二乘法的推导证明 1、什么是最小二乘思想 简单地说&#xff0c;最小二乘的思想就是要使得观测点和估计点的距离的平方和达到最小.这里的“二乘”指的是用平方来度量观测点与估计点的远近&#xff08;在古汉语中“平方”称为“二乘”&#xff09;&#xff0c;“最小”…