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

BufferQueue低延迟优化,以及SurfaceView帧率上限问题解决

目录

了解BufferQueue

为什么会出现问题?

如何优化?


最近在做一个与音视频播放相关的项目,使用到了MediaCodec解码后送到SurfaceView播放场景。发现SurfaceView播放上限是60HZ,不符合项目需求,故而进行了研究并找到了解决办法。

然而此方法需要修改aosp代码,并不合适普通的应用开发。

环境:AOSP14

了解BufferQueue

Android的显示框架使用了BufferQueue作为图形缓冲区的缓存队列,我们不需要深究BufferQueue的实现细节,只需要了解其大概工作原理即可。

Android图形系统使用BufferQueue实现了一个非常典型的生产者消费者模型。

生产者(如app的view或者视频解码器等)通过dequeueBuffer方法从BufferQueue中获取空闲图形缓冲区,填充数据后通过queueBuffer方法归还图形缓冲区,同时通知消费者可以消费。

消费者(一般为SurfaceFlinger,负责图像的显示)接收到生产者的通知后,通过acquirebuffer方法从BufferQueue获取一个可显示的图形缓冲区,等待垂直同步事件到来后显示(每秒60次)。显示完毕后通过releaseBuffer释放图形缓冲区。

为什么会出现问题?

假设生产和消费都是每秒60帧的速率时,理想的生产消费时序如下图所示:

生产和消费有序进行,生产一个图形缓冲区随后便消费一个。BufferQueue待消费的缓冲区始终不超过1个。

但是实际的情况,尤其是涉及到网络时,生产速率远没有如此理想,如下图所示:

生产端因为网络抖动的问题,有两帧没用及时到达,导致两次生产和消费被跳过。随后网络恢复后,极短的时间内达到了3帧数据(本次帧+延迟达到的2帧)。因为消费者只能按照固定的显示速度显示,生产也会继续以相同的速率生产,此时BufferQueue内会永远积压大于1帧的数据。直到下次网络抖动导致无生产输出,消费者才会消耗队列内的缓冲区。

这种模型有助于平滑抖动,轻微抖动时可以天然的被平滑过渡,不易被察觉卡顿。但是随之而来的是,队列积压导致的延迟增加。

更要致命的是,如果生产者是120帧的视频,但是显示是60帧,那视频就会被慢放至原来的二分之一。

典型的如SurfaceView,其播放速率为60HZ,如果播放大于此帧率的视频会被慢放。TextureView无此问题,但是TextureView需要GPU绘制,无论从延迟还是内存带宽占用上都是不小的开销,况且不需要后期处理的场景GPU绘制一次完全多此一举。

当前这种问题在播放30帧视频时问题不大,因为消费快于生产,积压很快会被消费。但是仍有短期的积压可能。

在延迟方面,SurfaceView整体占用小,延迟低,但是可能短期丢堆积帧,还有帧率上限;TextureView没有帧率上限,但是延迟高啊!如何既要又要?

如何优化?

如果是需要低延迟的场景,优化也很简单。

方案一:判断积压缓冲数量,积压较多时通知生产者丢帧处理或者降低生产速度。但是Android框架下无法得到积压数量,改动框架成本较大。放弃。

方案二:生产者向BufferQueue插入新的缓冲区时,如果BufferQueue存在未及时消费的缓冲区,直接丢弃。理所当然,Android也考虑到了这点,BufferQueue支持此操作,图形缓冲区有一个mIsDroppable属性,为True时若此帧在下一帧插入队列时未被消费,则丢弃。但是尚不清楚如何使之生效。故代码内直接写死,简单有效。

编辑frameworks/native/libs/gui/BufferQueueProducer.cpp

相关文章:

  • 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-09-26
  • 【移植】小型系统平台驱动移植
  • 计算机毕业设计 基于Flask+Vue的博客系统 Python毕业设计 前后端分离 附源码 讲解 文档
  • Python PyQt5 在frame中生成多个QLabel控件和彻底销毁QLabel控件
  • 【工具分享】Chimera勒索病毒解密工具
  • 流行的微前端框架有哪些,适应场景是什么
  • overlayscrollbars使用
  • 基于大数据技术的颈椎病预防交流与数据分析及可视化系统
  • 易燃气体检测系统源码分享
  • 统信服务器操作系统【qcow2 镜像空间扩容】方案
  • 【速成Redis】03 Redis 五大高级数据结构介绍及其常用命令 | 消息队列、地理空间、HyperLogLog、BitMap、BitField
  • CVE-2024-1112 Resource Hacker 缓冲区溢出分析
  • VBA技术资料MF205:移动工作表时名称重复的处理
  • 吹爆这份Stable diffusion提示词攻略!
  • python爬虫:将知乎专栏文章转为pdf
  • .pyc 想到的一些问题
  • 4个实用的微服务测试策略
  • Android Volley源码解析
  • ComponentOne 2017 V2版本正式发布
  • Git初体验
  • IIS 10 PHP CGI 设置 PHP_INI_SCAN_DIR
  • IOS评论框不贴底(ios12新bug)
  • JavaScript 基础知识 - 入门篇(一)
  • Java基本数据类型之Number
  • Python中eval与exec的使用及区别
  • QQ浏览器x5内核的兼容性问题
  • spring学习第二天
  • Sublime text 3 3103 注册码
  • ucore操作系统实验笔记 - 重新理解中断
  • unity如何实现一个固定宽度的orthagraphic相机
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 从@property说起(二)当我们写下@property (nonatomic, weak) id obj时,我们究竟写了什么...
  • 从伪并行的 Python 多线程说起
  • 电商搜索引擎的架构设计和性能优化
  • 工作踩坑系列——https访问遇到“已阻止载入混合活动内容”
  • 关于Java中分层中遇到的一些问题
  • 记一次删除Git记录中的大文件的过程
  • 经典排序算法及其 Java 实现
  • 来,膜拜下android roadmap,强大的执行力
  • 前端js -- this指向总结。
  • 全栈开发——Linux
  • 如何使用 OAuth 2.0 将 LinkedIn 集成入 iOS 应用
  • 一个SAP顾问在美国的这些年
  • 源码安装memcached和php memcache扩展
  • 在electron中实现跨域请求,无需更改服务器端设置
  • MiKTeX could not find the script engine ‘perl.exe‘ which is required to execute ‘latexmk‘.
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • # 职场生活之道:善于团结
  • #100天计划# 2013年9月29日
  • #define与typedef区别
  • #知识分享#笔记#学习方法
  • $HTTP_POST_VARS['']和$_POST['']的区别
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (env: Windows,mp,1.06.2308310; lib: 3.2.4) uniapp微信小程序
  • (十二)Flink Table API