Android Lollipop 在触摸屏上的视频延迟和 FPS 下降
Video Lag and FPS drop on Android Lollipop on touching screen
我正在使用 MediaCodec 播放 1080p@60fps 视频。这是在具有 Android Lollipop 5.1 的 freescale SabreSD 平台上。
最初由于 BufferQueue 同步模式,FPS 远低于 60.I 现在可以像 JB 一样通过将 BufferQueue 更改为异步来设法以 70FPS 播放。
现在我面临的下一个挑战是当我开始与屏幕交互(下拉通知栏、按下音量按钮等)时视频延迟和 FPS 急剧下降到 40。
所以我 运行 rafika MultiSurfaceActivity 和 Record GL,当没有屏幕被触摸或受到干扰时,我可以看到所有测试都顺利进行,但是一旦我开始从顶部滚动通知栏并继续很长一段时间,fps 会降低到 35-40FPS。
我已经确认在 Kitkat 4.4.2 和 JB 4.2.2 上进行了相同的测试,它们似乎工作正常。
从图库中播放 MP4 时的行为相同。当我们开始播放通知栏时,视频卡住并滞后很多
谁能解释一下从 Kitkat 到 Lollipop 有什么变化会导致这个问题(垂直同步、三重缓冲?)。
从Grafika issue tracker反省一点:
弹跳球是软件渲染的,所以任何吸收 CPU 时间的东西都会让它变慢。在具有中档 CPUs 和大显示器(例如 Nexus 10)的设备上,它永远不会接近 60fps。因此,当您在使用导航栏时 变慢并不让我感到惊讶,但如果即使在您停止使用导航栏后它仍然很慢,那就有点奇怪了。
视频播放受到的影响应该较小,因为 CPU。
调查此类问题通常首先使用 systrace 捕获 "good" 和 "bad" 状态的痕迹,然后比较两者。
BufferQueue "async mode" 的关键点是在消费者跟不上生产者时允许丢帧。它主要用于 SurfaceTexture,其中生产者和消费者在同一个应用程序中,可能在同一个线程上,因此让生产者停止等待消费者可能会导致程序挂起。我不确定你需要它超过 60fps 是什么意思,但我猜你在显示器上投掷帧的速度快于渲染它们的速度......所以你并没有真正增加帧速率,你是只需使用 BufferQueue 来丢弃帧,而不是使用 Choreographer 来决定何时需要自己丢弃它们。
无论如何,我在 2014 年 6 月就离开了 Google,远在 Lollipop 完成之前。如果某些东西在 KitKat 上运行正常但在 Lollipop 上运行异常,恐怕我无法提供太多见解。如果您可以轻松地重现该行为,则可能值得捕获一个演示问题的视频(将第二个智能 phone 指向出现问题的设备,以便他们可以看到您如何操作设备)并提交错误在 http://b.android.com/.
OP上传的部分痕迹:
- https://www.dropbox.com/s/luwovq7ohozccdy/Lollipop_bad.zip
- https://www.dropbox.com/s/zkv0aqw0shecpw2/lollipop_good.zip
- https://www.dropbox.com/s/g7qe01xvmfyvpak/kitkat.zip
查看 kitkat 跟踪,SurfaceFlinger 中发生了一些奇怪的事情。主线程在 postFrameBuffer
中停留了很长时间(23-32 毫秒)。它最终醒来,CPU 行表明它正在等待来自 "galcore daemon" 的 activity,我对此并不熟悉(似乎是 Vivante GPU 特有的)。
棒棒糖痕迹仅显示 CPU 行,就好像捕获是在没有必要标签的情况下完成的。我不相信 systrace 捕获命令在 kitkat 和 lollipop 之间有显着变化,所以我很困惑为什么用户-space-启动的日志记录会消失但内核线程调度的东西会保留下来。确保您已指定 sched gfx view
。
较新的棒棒糖轨迹只有大约一秒钟的良好数据。当您看到 "Did Not Finish" 时,表示 "start" 记录没有匹配的 "end" 记录。您可以使用 -b
标志增加 systrace 日志记录缓冲区的大小。不过我觉得够了。
查看 /system/bin/surfaceflinger
行您可以看到,在 "good" 跟踪中,postFrameBuffer
通常在大约 16 毫秒内完成,但它仍在等待 galcore。放大 388 毫秒(使用 WASD 键)。在 388.196 毫秒处,在 CPU 2 行,您可以看到 galcore 做了一些事情。完成后,surfaceflinger 行顶部的细线立即从浅灰色(休眠)变为绿色(运行ning)。在 388.548 毫秒,再次在 CPU 2,galcore 再次 运行s,紧接着在 surfaceflinger 行你看到 queueBuffer
开始执行。
"bad" 轨迹看起来完全相同。例如,您可以在 101.146 毫秒和 101.666 毫秒处看到两次 galcore 执行,在 surfaceflinger 行上的效果似乎相似。主要区别在于 postFrameBuffer
花费的时间,"good" 大约为 16 毫秒,"bad" 大约为 30 毫秒。
所以这似乎不是行为转变;相反,事情正在花费更长的时间并且错过了最后期限。
据我所知,SurfaceFlinger 被 galcore 守护程序阻止。在 "good" 和 "bad" 情况下都是如此。要查看时间 应该 看起来像什么,您可以 运行 在 Nexus 设备上进行 systrace,或者与来自其他设备的跟踪(例如 this case study or this SO question 中的跟踪)进行比较。如果放大,您可以看到 doComposition
在几毫秒内执行,postFrameBuffer
在十分之几毫秒内完成。
总结:你没有好坏之分,只有坏与坏之分。 :-) 我不知道 galcore 是什么,但您可能需要与 GPU OEM 进行对话。
我正在使用 MediaCodec 播放 1080p@60fps 视频。这是在具有 Android Lollipop 5.1 的 freescale SabreSD 平台上。
最初由于 BufferQueue 同步模式,FPS 远低于 60.I 现在可以像 JB 一样通过将 BufferQueue 更改为异步来设法以 70FPS 播放。
现在我面临的下一个挑战是当我开始与屏幕交互(下拉通知栏、按下音量按钮等)时视频延迟和 FPS 急剧下降到 40。
所以我 运行 rafika MultiSurfaceActivity 和 Record GL,当没有屏幕被触摸或受到干扰时,我可以看到所有测试都顺利进行,但是一旦我开始从顶部滚动通知栏并继续很长一段时间,fps 会降低到 35-40FPS。
我已经确认在 Kitkat 4.4.2 和 JB 4.2.2 上进行了相同的测试,它们似乎工作正常。
从图库中播放 MP4 时的行为相同。当我们开始播放通知栏时,视频卡住并滞后很多
谁能解释一下从 Kitkat 到 Lollipop 有什么变化会导致这个问题(垂直同步、三重缓冲?)。
从Grafika issue tracker反省一点:
弹跳球是软件渲染的,所以任何吸收 CPU 时间的东西都会让它变慢。在具有中档 CPUs 和大显示器(例如 Nexus 10)的设备上,它永远不会接近 60fps。因此,当您在使用导航栏时 变慢并不让我感到惊讶,但如果即使在您停止使用导航栏后它仍然很慢,那就有点奇怪了。
视频播放受到的影响应该较小,因为 CPU。
调查此类问题通常首先使用 systrace 捕获 "good" 和 "bad" 状态的痕迹,然后比较两者。
BufferQueue "async mode" 的关键点是在消费者跟不上生产者时允许丢帧。它主要用于 SurfaceTexture,其中生产者和消费者在同一个应用程序中,可能在同一个线程上,因此让生产者停止等待消费者可能会导致程序挂起。我不确定你需要它超过 60fps 是什么意思,但我猜你在显示器上投掷帧的速度快于渲染它们的速度......所以你并没有真正增加帧速率,你是只需使用 BufferQueue 来丢弃帧,而不是使用 Choreographer 来决定何时需要自己丢弃它们。
无论如何,我在 2014 年 6 月就离开了 Google,远在 Lollipop 完成之前。如果某些东西在 KitKat 上运行正常但在 Lollipop 上运行异常,恐怕我无法提供太多见解。如果您可以轻松地重现该行为,则可能值得捕获一个演示问题的视频(将第二个智能 phone 指向出现问题的设备,以便他们可以看到您如何操作设备)并提交错误在 http://b.android.com/.
OP上传的部分痕迹:
- https://www.dropbox.com/s/luwovq7ohozccdy/Lollipop_bad.zip
- https://www.dropbox.com/s/zkv0aqw0shecpw2/lollipop_good.zip
- https://www.dropbox.com/s/g7qe01xvmfyvpak/kitkat.zip
查看 kitkat 跟踪,SurfaceFlinger 中发生了一些奇怪的事情。主线程在 postFrameBuffer
中停留了很长时间(23-32 毫秒)。它最终醒来,CPU 行表明它正在等待来自 "galcore daemon" 的 activity,我对此并不熟悉(似乎是 Vivante GPU 特有的)。
棒棒糖痕迹仅显示 CPU 行,就好像捕获是在没有必要标签的情况下完成的。我不相信 systrace 捕获命令在 kitkat 和 lollipop 之间有显着变化,所以我很困惑为什么用户-space-启动的日志记录会消失但内核线程调度的东西会保留下来。确保您已指定 sched gfx view
。
较新的棒棒糖轨迹只有大约一秒钟的良好数据。当您看到 "Did Not Finish" 时,表示 "start" 记录没有匹配的 "end" 记录。您可以使用 -b
标志增加 systrace 日志记录缓冲区的大小。不过我觉得够了。
查看 /system/bin/surfaceflinger
行您可以看到,在 "good" 跟踪中,postFrameBuffer
通常在大约 16 毫秒内完成,但它仍在等待 galcore。放大 388 毫秒(使用 WASD 键)。在 388.196 毫秒处,在 CPU 2 行,您可以看到 galcore 做了一些事情。完成后,surfaceflinger 行顶部的细线立即从浅灰色(休眠)变为绿色(运行ning)。在 388.548 毫秒,再次在 CPU 2,galcore 再次 运行s,紧接着在 surfaceflinger 行你看到 queueBuffer
开始执行。
"bad" 轨迹看起来完全相同。例如,您可以在 101.146 毫秒和 101.666 毫秒处看到两次 galcore 执行,在 surfaceflinger 行上的效果似乎相似。主要区别在于 postFrameBuffer
花费的时间,"good" 大约为 16 毫秒,"bad" 大约为 30 毫秒。
所以这似乎不是行为转变;相反,事情正在花费更长的时间并且错过了最后期限。
据我所知,SurfaceFlinger 被 galcore 守护程序阻止。在 "good" 和 "bad" 情况下都是如此。要查看时间 应该 看起来像什么,您可以 运行 在 Nexus 设备上进行 systrace,或者与来自其他设备的跟踪(例如 this case study or this SO question 中的跟踪)进行比较。如果放大,您可以看到 doComposition
在几毫秒内执行,postFrameBuffer
在十分之几毫秒内完成。
总结:你没有好坏之分,只有坏与坏之分。 :-) 我不知道 galcore 是什么,但您可能需要与 GPU OEM 进行对话。