随着高帧率手机的普及,在很多APP内需要限制手机的帧率来进行省电优化。今天的例子就是视频(直播/点播)播放的软件。
推流优化
首先我们说,在视频采集时,有两种采集方式:屏幕采集和camera采集。
camera采集
这里我们可以直接限制camera的采集频率。这里有几个优化方案供参考
android.hardware.camera2.params.StreamConfigurationMap
现在的很多手机采集一般为50HZ-60HZ,部分低端机前置为30HZ(** 也有部分安卓机器的Camera接口无法实现30FPS及以上的数据采集频率,导致FPS始终为20,这里可以灵活判断当前帧率采取优化 *),所以这里需要手动设置StreamConfigurationMap
的采集帧率为30HZ,注意*:这里设置采集范围的时,fpsMax需要大于fpsMin,且需要是30的倍数。
不仅需要优化帧率,在采集视频时推荐采集低分辨率的视频。CameraX 会自动选择内部 Camera2 界面分辨率,默认目标分辨率设置为 640x480,Camera最大支持1080P的视频输出。( ** 如果你希望采集的视频值大于640x480时,那么你需要参考使用 setTargetResolution 或者 settargetaspectratio 这两个方法 *)。这里推荐你使用360640或更低的分辨率,设置方法为
val imageAnalysis = ImageAnalysis.Builder() .setTargetResolution(Size(640, 360)) .build()
|
另外,你可以在你的推流SDK中打开硬件支持,如果你是手动推流,你可以自己打开硬件支持(APP中是默认开启的,但是推流的SDK貌似都不是)
<application android:hardwareAccelerated="true" ...>
window.setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED )
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
|
或者你可以使用MediaCodec,具体参考这篇文章,写的很不错:Android 用MediaCodec实现视频硬解码
MediaCodec官方文档:https://developer.android.com/reference/android/media/MediaCodec
屏幕采集:
这里我就简单说一下通过MediaProjectionManager
采集视频的方案,简单多了:
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setOutputFile( mRecordFilePath );
mMediaRecorder.setVideoSize(mRecordWidth, mRecordHeight);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncodingBitRate((int) (mRecordWidth * mRecordHeight * 3.6));
mMediaRecorder.setVideoFrameRate(20);
|
这是MediaProjectionManager
的一些属性,在开始录制之前你设置其中的一些属性即可。例如分辨率,比特率,以及帧率。
一般如果是人直播,可以设置为25FPS,游戏直播可以设置为35-45,具体可以自行判断。比特率是影响速度的一大因素,也是直接影响画质的一大因素。如果是画面变化快的游戏或者人物直播,可以适当调低。
播放优化
视频播放就简单多了。如果是你自定义的播放view,我们只需要限制view的帧率即可。在上面的方法中,我们看到mMediaRecorder.setVideoFrameRate(20);
这样限制帧率的方法存在,那么在我们自定义的view中有没有这样的方法?答案是有
推荐setFrameRate()方法
首先,这是一个API29以上的方法…,如果你的兼容用户低于30,那么请暂时忽略这部分。
- Surface.setFrameRate()
- SurfaceControl.Transaction.setFrameRate()
- ANativeWindow_setFrameRate()
- ASurfaceTransaction_setFrameRate()
使用方法:
1 public void setFrameRate (float frameRate, int compatibility)
2 public SurfaceControl.Transaction setFrameRate (SurfaceControl sc, float frameRate, int compatibility)
3 int32_t ANativeWindow_setFrameRate( ANativeWindow *window, float frameRate, int8_t compatibility )
4 void ASurfaceTransaction_setFrameRate( ASurfaceTransaction *transaction, ASurfaceControl *surface_control, float frameRate, int8_t compatibility )
|
c++中(有问题,在改)
SDL_Event event;
static int time = 0; static float fps=20;
while(running){
while (SDL_PollEvent(&event)) { SDL_WaitEvent(&event); OnEvent(&event); time = SDL_GetTicks(); }
if(SDL_GetTicks()-time>(1000/fps)) { time = SDL_GetTicks(); OnUpdate(); OnRender(); }
}
|
native中
话不多说,直接上代码:
long time = System.currentTimeMillis(); drow或者其他的绘制方法
while((System.currentTimeMillis() - time) <= 30) { Thread.yield(); }
|
以上就是大致的意思。总的来说,无非就集中方案,就是分用处去限制罢了。