android帧率,android帧率测试
Android流畅度评估及卡顿优化
Google定义:界面呈现是指从应用生成帧并将其显示在屏幕上的动作。要确保用户能够流畅地与应用互动,应用呈现每帧的时间不应超过16ms,以达到每秒60帧的呈现速度(为什么是60fps?)。
创新互联建站专注于昔阳企业网站建设,成都响应式网站建设公司,成都商城网站开发。昔阳网站建设公司,为昔阳等地区提供建站服务。全流程按需网站策划,专业设计,全程项目跟踪,创新互联建站专业和态度为您提供的服务
如果应用存在界面呈现缓慢的问题,系统会不得不跳过一些帧,这会导致用户感觉应用不流畅,我们将这种情况称为卡顿。
来源于: Google Android的为什么是60fps?
16ms意味着1000/60hz,相当于60fps。这是因为人眼与大脑之间的协作无法感知超过60fps的画面更新。12fps大概类似手动快速翻动书籍的帧率, 这明显是可以感知到不够顺滑的。24fps使得人眼感知的是连续线性的运动,这其实是归功于运动模糊的效果。 24fps是电影胶圈通常使用的帧率,因为这个帧率已经足够支撑大部分电影画面需要表达的内容,同时能够最大的减少费用支出。 但是低于30fps是 无法顺畅表现绚丽的画面内容的,此时就需要用到60fps来达到想要的效果,超过60fps就没有必要了。如果我们的应用没有在16ms内完成屏幕刷新的全部逻辑操作,就会发生卡顿。
首先要了解Android显示1帧图像,所经历的完整过程。
如图所示,屏幕显示1帧图像需要经历5个步骤:
常见的丢帧情况: 渲染期间可能出现的情况,渲染大于16ms和小于16ms的情况:
上图中应该绘制 4 帧数据 , 但是实际上只绘制了 3 帧 , 实际帧率少了一帧
判断APP是否出现卡顿,我们从通用应用和游戏两个纬度的代表公司标准来看,即Google的Android vitals性能指标和地球第一游戏大厂腾讯的PrefDog性能指标。
以Google Vitals的卡顿描述为准,即呈现速度缓慢和帧冻结两个维度判断:
PerfDog Jank计算方法:
帧率FPS高并不能反映流畅或不卡顿。比如:FPS为50帧,前200ms渲染一帧,后800ms渲染49帧,虽然帧率50,但依然觉得非常卡顿。同时帧率FPS低,并不代表卡顿,比如无卡顿时均匀FPS为15帧。所以平均帧率FPS与卡顿无任何直接关系)
当了解卡顿的标准以及渲染原理之后,可以得出结论,只有丢帧情况才能准确判断是否卡顿。
dumpsys 是一种在设备上运行并转储需要关注的系统服务状态信息的 Android 工具。通过向 dumpsys 传递 gfxinfo 命令,可以提供 logcat 格式的输出,其中包含与录制阶段发生的动画帧相关的性能信息。
借助 Android 6.0(API 级别 23),该命令可将在整个进程生命周期中收集的帧数据的聚合分析输出到 logcat。例如:
这些总体统计信息可以得到期间的FPS、Jank比例、各类渲染异常数量统计。
命令 adb shell dumpsys gfxinfo PACKAGE_NAME framestats 可提供最近120个帧中,渲染各阶段带有纳秒时间戳的帧时间信息。
关键参数说明:
通过gfxinfo输出的帧信息,通过定时reset和打印帧信息,可以得到FPS(帧数/打印间隔时间)、丢帧比例((janky_frames / total_frames_rendered)*100 %)、是否有帧冻结(帧耗时700ms)。
根据第2部分的通用应用卡顿标准,可以通过丢帧比例和帧冻结数量,准确判断当前场景是否卡顿。并且通过定时截图,还可以根据截图定位卡顿的具体场景。
如上图所示,利用gfxinfo开发的检查卡顿的小工具,图中参数和卡顿说明如下:
根据上面对gfxinfo的帧信息解析,可以准确计算出每一帧的耗时。从而可以开发出满足腾讯PerfDog中关于普通卡顿和严重卡顿的判断。
依赖定时截图,即可准确定位卡顿场景。如下图所示(此处以PerfDog截图示例):
通过第3部分的卡顿评估方法,我们可以定位到卡顿场景,但是如何定位到具体卡顿原因呢。
首先了解卡顿问题定位工具,然后再了解常见的卡顿原因,即可通过复现卡顿场景的同时,用工具去定位具体卡顿问题。
重点就是,充分利用gfxinfo输出的帧信息,对卡顿问题进行分类。
了解了高效定位卡顿的方法和卡顿问题定位工具,再熟悉一下常见的卡顿原因,可以更熟练的定位和优化卡顿。
SurfaceFlinger 负责 Surface 的合成,一旦 SurfaceFlinger 主线程调用超时,就会产生掉帧。
SurfaceFlinger 主线程耗时会也会导致 hwc service 和 crtc 不能及时完成,也会阻塞应用的 binder 调用,如 dequeueBuffer、queueBuffer 等。
后台进程活动太多,会导致系统非常繁忙,cpu \ io \ memory 等资源都会被占用,这时候很容易出现卡顿问题,这也是系统这边经常会碰到的问题。
dumpsys cpuinfo 可以查看一段时间内 cpu 的使用情况:
当线程为 Runnable 状态的时候,调度器如果迟迟不能对齐进行调度,那么就会产生长时间的 Runnable 线程状态,导致错过 Vsync 而产生流畅性问题。
system_server 的 AMS 锁和 WMS 锁 , 在系统异常的情况下 , 会变得非常严重 , 如下图所示 , 许多系统的关键任务都被阻塞 , 等待锁的释放 , 这时候如果有 App 发来的 Binder 请求带锁 , 那么也会进入等待状态 , 这时候 App 就会产生性能问题 ; 如果此时做 Window 动画 , 那么 system_server 的这些锁也会导致窗口动画卡顿。
Android P 修改了 Layer 的计算方法 , 把这部分放到了 SurfaceFlinger 主线程去执行, 如果后台 Layer 过多,就会导致 SurfaceFlinger 在执行 rebuildLayerStacks 的时候耗时 , 导致 SurfaceFlinger 主线程执行时间过长。
主线程执行 Input \ Animation \ Measure \ Layout \ Draw \ decodeBitmap 等操作超时都会导致卡顿 。
Activity resume 的时候, 与 AMS 通信要持有 AMS 锁, 这时候如果碰到后台比较繁忙的时候, 等锁操作就会比较耗时, 导致部分场景因为这个卡顿, 比如多任务手势操作。
应用里面涉及到 WebView 的时候, 如果页面比较复杂, WebView 的性能就会比较差, 从而造成卡顿。
如果屏幕帧率和系统的 fps 不相符 , 那么有可能会导致画面不是那么顺畅. 比如使用 90 Hz 的屏幕搭配 60 fps 的动画。
由上面的分析可知对象分配、垃圾回收(GC)、线程调度以及Binder调用 是Android系统中常见的卡顿原因,因此卡顿优化主要以下几种方法,更多的要结合具体的应用来进行:
在计算机和通信领域,帧是一个包括“帧同步串行”的数字数据传输单元或数字数据包。
在视频领域,电影、电视、数字视频等可视为随时间连续变换的许多张画面,其中帧是指每一张画面。
Android -- 音视频基础知识
帧,是视频的一个基本概念,表示一张画面,如上面的翻页动画书中的一页,就是一帧。一个视频就是由许许多多帧组成的。
帧率,即单位时间内帧的数量,单位为:帧/秒 或fps(frames per second)。一秒内包含多少张图片,图片越多,画面越顺滑,过渡越自然。 帧率的一般以下几个典型值:
24/25 fps:1秒 24/25 帧,一般的电影帧率。
30/60 fps:1秒 30/60 帧,游戏的帧率,30帧可以接受,60帧会感觉更加流畅逼真。
85 fps以上人眼基本无法察觉出来了,所以更高的帧率在视频里没有太大意义。
这里我们只讲常用到的两种色彩空间。
RGB的颜色模式应该是我们最熟悉的一种,在现在的电子设备中应用广泛。通过R G B三种基础色,可以混合出所有的颜色。
这里着重讲一下YUV,这种色彩空间并不是我们熟悉的。这是一种亮度与色度分离的色彩格式。
早期的电视都是黑白的,即只有亮度值,即Y。有了彩色电视以后,加入了UV两种色度,形成现在的YUV,也叫YCbCr。
Y:亮度,就是灰度值。除了表示亮度信号外,还含有较多的绿色通道量。
U:蓝色通道与亮度的差值。
V:红色通道与亮度的差值。
音频数据的承载方式最常用的是 脉冲编码调制 ,即 PCM 。
在自然界中,声音是连续不断的,是一种模拟信号,那怎样才能把声音保存下来呢?那就是把声音数字化,即转换为数字信号。
我们知道声音是一种波,有自己的振幅和频率,那么要保存声音,就要保存声音在各个时间点上的振幅。
而数字信号并不能连续保存所有时间点的振幅,事实上,并不需要保存连续的信号,就可以还原到人耳可接受的声音。
根据奈奎斯特采样定理:为了不失真地恢复模拟信号,采样频率应该不小于模拟信号频谱中最高频率的2倍。
根据以上分析,PCM的采集步骤分为以下步骤:
采样率,即采样的频率。
上面提到,采样率要大于原声波频率的2倍,人耳能听到的最高频率为20kHz,所以为了满足人耳的听觉要求,采样率至少为40kHz,通常为44.1kHz,更高的通常为48kHz。
采样位数,涉及到上面提到的振幅量化。波形振幅在模拟信号上也是连续的样本值,而在数字信号中,信号一般是不连续的,所以模拟信号量化以后,只能取一个近似的整数值,为了记录这些振幅值,采样器会采用一个固定的位数来记录这些振幅值,通常有8位、16位、32位。
位数越多,记录的值越准确,还原度越高。
最后就是编码了。由于数字信号是由0,1组成的,因此,需要将幅度值转换为一系列0和1进行存储,也就是编码,最后得到的数据就是数字信号:一串0和1组成的数据。
整个过程如下:
声道数,是指支持能不同发声(注意是不同声音)的音响的个数。 单声道:1个声道
双声道:2个声道
立体声道:默认为2个声道
立体声道(4声道):4个声道
码率,是指一个数据流中每秒钟能通过的信息量,单位bps(bit per second)
码率 = 采样率 * 采样位数 * 声道数
这里的编码和上面音频中提到的编码不是同个概念,而是指压缩编码。
我们知道,在计算机的世界中,一切都是0和1组成的,音频和视频数据也不例外。由于音视频的数据量庞大,如果按照裸流数据存储的话,那将需要耗费非常大的存储空间,也不利于传送。而音视频中,其实包含了大量0和1的重复数据,因此可以通过一定的算法来压缩这些0和1的数据。
特别在视频中,由于画面是逐渐过渡的,因此整个视频中,包含了大量画面/像素的重复,这正好提供了非常大的压缩空间。
因此,编码可以大大减小音视频数据的大小,让音视频更容易存储和传送。
视频编码格式有很多,比如H26x系列和MPEG系列的编码,这些编码格式都是为了适应时代发展而出现的。
其中,H26x(1/2/3/4/5)系列由ITU(International Telecommunication Union)国际电传视讯联盟主导
MPEG(1/2/3/4)系列由MPEG(Moving Picture Experts Group, ISO旗下的组织)主导。
当然,他们也有联合制定的编码标准,那就是现在主流的编码格式H264,当然还有下一代更先进的压缩编码标准H265。
H264是目前最主流的视频编码标准,所以我们后续的文章中主要以该编码格式为基准。
H264由ITU和MPEG共同定制,属于MPEG-4第十部分内容。
我们已经知道,视频是由一帧一帧画面构成的,但是在视频的数据中,并不是真正按照一帧一帧原始数据保存下来的(如果这样,压缩编码就没有意义了)。
H264会根据一段时间内,画面的变化情况,选取一帧画面作为完整编码,下一帧只记录与上一帧完整数据的差别,是一个动态压缩的过程。
在H264中,三种类型的帧数据分别为
I帧:帧内编码帧。就是一个完整帧。
P帧:前向预测编码帧。是一个非完整帧,通过参考前面的I帧或P帧生成。
B帧:双向预测内插编码帧。参考前后图像帧编码生成。B帧依赖其前最近的一个I帧或P帧及其后最近的一个P帧。
全称:Group of picture。指一组变化不大的视频帧。
GOP的第一帧成为关键帧:IDR
IDR都是I帧,可以防止一帧解码出错,导致后面所有帧解码出错的问题。当解码器在解码到IDR的时候,会将之前的参考帧清空,重新开始一个新的序列,这样,即便前面一帧解码出现重大错误,也不会蔓延到后面的数据中。
DTS全称:Decoding Time Stamp。标示读入内存中数据流在什么时候开始送入解码器中进行解码。也就是解码顺序的时间戳。
PTS全称:Presentation Time Stamp。用于标示解码后的视频帧什么时候被显示出来。
前面我们介绍了RGB和YUV两种图像色彩空间。H264采用的是YUV。
YUV存储方式分为两大类:planar 和 packed。
planar如下:
packed如下:
上面说过,由于人眼对色度敏感度低,所以可以通过省略一些色度信息,即亮度共用一些色度信息,进而节省存储空间。因此,planar又区分了以下几种格式:YUV444、 YUV422、YUV420。
YUV 4:4:4采样,每一个Y对应一组UV分量。
YUV 4:2:2采样,每两个Y共用一组UV分量。
YUV 4:2:0采样,每四个Y共用一组UV分量。
其中,最常用的就是YUV420。
YUV420属于planar存储方式,但是又分两种类型:
YUV420P:三平面存储。数据组成为YYYYYYYYUUVV(如I420)或YYYYYYYYVVUU(如YV12)。
YUV420SP:两平面存储。分为两种类型YYYYYYYYUVUV(如NV12)或YYYYYYYYVUVU(如NV21)
原始的PCM音频数据也是非常大的数据量,因此也需要对其进行压缩编码。
和视频编码一样,音频也有许多的编码格式,如:WAV、MP3、WMA、APE、FLAC等等,音乐发烧友应该对这些格式非常熟悉,特别是后两种无损压缩格式。
但是,我们今天的主角不是他们,而是另外一个叫AAC的压缩格式。
AAC是新一代的音频有损压缩技术,一种高压缩比的音频压缩算法。在MP4视频中的音频数据,大多数时候都是采用AAC压缩格式。
AAC格式主要分为两种:ADIF、ADTS。
ADIF:Audio Data Interchange Format。音频数据交换格式。这种格式的特征是可以确定的找到这个音频数据的开始,不需进行在音频数据流中间开始的解码,即它的解码必须在明确定义的开始处进行。这种格式常用在磁盘文件中。
ADTS:Audio Data Transport Stream。音频数据传输流。这种格式的特征是它是一个有同步字的比特流,解码可以在这个流中任何位置开始。它的特征类似于mp3数据流格式。
ADIF数据格式:
ADTS 一帧 数据格式(中间部分,左右省略号为前后数据帧):
AAC内部结构也不再赘述,可以参考AAC 文件解析及解码流程
细心的读者可能已经发现,前面我们介绍的各种音视频的编码格式,没有一种是我们平时使用到的视频格式,比如:mp4、rmvb、avi、mkv、mov...
没错,这些我们熟悉的视频格式,其实是包裹了音视频编码数据的容器,用来把以特定编码标准编码的视频流和音频流混在一起,成为一个文件。
例如:mp4支持H264、H265等视频编码和AAC、MP3等音频编码。
我们在一些播放器中会看到,有硬解码和软解码两种播放形式给我们选择,但是我们大部分时候并不能感觉出他们的区别,对于普通用户来说,只要能播放就行了。
那么他们内部究竟有什么区别呢?
在手机或者PC上,都会有CPU、GPU或者解码器等硬件。通常,我们的计算都是在CPU上进行的,也就是我们软件的执行芯片,而GPU主要负责画面的显示(是一种硬件加速)。
所谓软解码,就是指利用CPU的计算能力来解码,通常如果CPU的能力不是很强的时候,一则解码速度会比较慢,二则手机可能出现发热现象。但是,由于使用统一的算法,兼容性会很好。
硬解码,指的是利用手机上专门的解码芯片来加速解码。通常硬解码的解码速度会快很多,但是由于硬解码由各个厂家实现,质量参差不齐,非常容易出现兼容性问题。
MediaCodec 是Android 4.1(api 16)版本引入的编解码接口,是所有想在Android上开发音视频的开发人员绕不开的坑。
由于Android碎片化严重,虽然经过多年的发展,Android硬解已经有了很大改观,但实际上各个厂家实现不同, 还是会有一些意想不到的坑。
相对于FFmpeg,Android原生硬解码还是相对容易入门一些,所以接下来,我将会从MediaCodec入手,讲解如何实现视频的编解码,以及引入OpenGL实现对视频的编辑,最后才引入FFmpeg来实现软解,算是一个比较常规的音视频开发入门流程吧。
Android性能测试(内存、cpu、fps、流量、GPU、电量)——adb篇
3)查看进程列表:adb shell "ps",同时也能获取到应用的UID,方式如下(不需root权限):
u0_a开头的都是Android的应用进程,Android的应用的UID是从10000开始,到19999结束,可以在Process.java中查看到(FIRST_APPLICATION_UID和LAST_APPLICATION_UID),u0_a后面的数字就是该应用的UID值减去FIRST_APPLICATION_UID所得的值,所以,对于截图这个应用进程,它是u0_a155,按前面的规制,它的UID就是155 + FIRST_APPLICATION_UID = 10155。
VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)
一般来说内存占用大小有如下规律:VSS = RSS = PSS = USS
使用 adb shell "dumpsys meminfo -s pakagename | pid"命令,输出结果分以下4部分:
PS:在apk内调用运行获取其他app的内存数据则需要root权限
adb命令:adb shell dumpsys gfxinfo package | pid
正常情况下帧率应该在16.67ms左右,1秒60帧,执行结果如下:
详细计算方法如下:
还有一个命令是: adb shell dumpsys SurfaceFlinger --latency LayerName
其中LayerName在各个不同系统中获取的命令是不一样的
在Android 6系统直接就是SurfaceView
在Android 7系统中可以通过 dumpsys window windows | grep mSurface | grep SurfaceView 然后通过数据截取到
在Android 8系统中可以通过 dumpsys SurfaceFlinger | grep android包名获取到
执行命令结果如下:
计算方法比较简单,一般打印出来的数据是129行(部分机型打印两次257行,但是第一部分是无效数据,取后半部分),取len-2的第一列数据为end_time,取len-128的第一列数据为start_time
fps = 127/((end_time - start_time) / 1000000.0)
至于为啥要取第一列数据,这里不做过多介绍,欢迎参看这两篇文章
老罗的文章SurfaceView原理
Android性能测试之fps获取
至于为啥要处于1000000,因为命令打印出来的是纳秒单位,要转为毫秒进行计算,127就是因为命令一次打印出来127帧的数据而已
有两种方法可以获取
1) adb shell "top -n 5 | grep package | pid" ,第三列就是实时监控的CPU占用率(-n 指定执行次数,不需root权限),这边top命令执行需要2到3s左右,一般可以采用busybox 的top命令执行,效率会快很多
2) adb shell "dumpsys cpuinfo | grep package | pid"
两种方法直接区别在于,top是持续监控状态,而dumpsys cpuinfo获取的实时CPU占用率数据
adb命令:adb shell "dumpsys batterystats package | pid" (Android 5.0后引入)
获取单个应用的耗电量信息,具体返回结果待研究
adb命令:adb shell "dumpsys battery"
出现信息解读:
AC powered:false 是否连接AC(电源)充电线
USB powered:true 是否连接USB(PC或笔记本USB插口)充电
Wireless powered:false 是否使用了无线电源
status: 1 电池状态,2为充电状态,其他为非充电状态
level:58 电量(%)
scale: 100. 电量最大数值
voltage: 3977 当前电压(mV)
current now: -335232. 当前电流(mA)
temperature:355 电池温度,单位为0.1摄氏度
adb 命令:adb shell "dumpsys package | pid | grep UID" [通过ps命令,获取app的UID(安装后唯一且固定)]
adb shell cat /proc/uid_stat/UID/tcp_rcv [cat为查看命令,读取tcp_rcv获取应用接收流量信息(设备重启后清零)]
adb shell cat /proc/uid_stat/UID/tcp_snd [cat为查看命令,读取tcp_snd获取应用发送流量信息(设备重启后清零)]
计算流量消耗步骤:
或者还有一种方式获取应用流量消耗:
首先判断类型:
cat /sys/class/thermal/thermal_zone*/type
只有红框框出来的是有效的
cat /sys/class/thermal/thermal_zone*/temp
获取CPU温度
dumpsys battery | grep temperature 单位0.1摄氏度
获取/proc/stat文件内容(无权限限制)
总的cpu时间片是 total = user+nice+system+idle+iowait+irq+softirq
忙碌时间为 notidle = user+nice+system +iowait+irq+softirq
cpu使用率计算方法为,先取开始的total值和忙碌时间notidle,隔一段时间片,再取一次计算total2,notidle2, cpuuse = (notidle2 – notidle) * 100 / (total2 - total)%
PS:由于Android 8权限收紧,在Android 8系统手机内apk内读取文件内容为空,需要shell权限才可获取文件内容,下同
读/sys/devices/system/cpu/cpuX/cpufreq/scaling_cur_freq文件的值,X不定,看是几核手机,scaling_cur_freq是否存在也不一定,需要判断
至于为啥不取cpuinfo_cur_freq文件的值,原因是android 6,7系统获取的时候,这个文件shell没有读取权限,需要root权限
参考文章:
Android 6,7系统可执行
dumpsys window windows | grep "mCurrentFocus"
执行结果一般为类似:
mCurrentFocus=Window{81caaa5 u0 com.tencent.mobileqq/com.tencent.mobileqq.activity.SplashActivity}
按照一定规则把com.tencent.mobileqq提取出来即可
直接apk内读取文件即可,不需要shell权限(支持到Android8)
Gpu使用率获取:会得到两个值,(前一个/后一个)*100%=使用率
adb shell cat /sys/class/kgsl/kgsl-3d0/gpubusy
Gpu工作频率:
adb shell cat /sys/class/kgsl/kgsl-3d0/gpuclk
adb shell cat /sys/class/kgsl/kgsl-3d0/devfreq/cur_freq
Gpu最大、最小工作频率:
adb shell cat /sys/class/kgsl/kgsl-3d0/devfreq/max_freq
adb shell cat /sys/class/kgsl/kgsl-3d0/devfreq/min_freq
Gpu可用频率
adb shell cat /sys/class/kgsl/kgsl-3d0/gpu_available_frequencies
adb shell cat /sys/class/kgsl/kgsl-3d0/devfreq/available_frequencies
Gpu可用工作模式:
adb shell cat /sys/class/kgsl/kgsl-3d0/devfreq/available_governors
Gpu当前工作模式:
adb shell cat /sys/class/kgsl/kgsl-3d0/devfreq/governor
网站题目:android帧率,android帧率测试
网站链接:http://azwzsj.com/article/dsschpi.html