随着高帧率手机的普及,在很多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或者activityzhong
<application android:hardwareAccelerated="true" ...>
//你的activity中
window.setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
)
//在你的view中
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)
///------------------NDK------------------///
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;//你需要的fps
//time = SDL_GetTicks() 或者你给一个初始值,在开始之前设置最好
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中
话不多说,直接上代码:
//自定义surfaceview,然后在绘制方法中
long time = System.currentTimeMillis();
drow或者其他的绘制方法
//这里的30是1000/30约等于33帧的意思
while((System.currentTimeMillis() - time) <= 30) {
Thread.yield(); //yield我个人感觉是最佳选择,但是没法控制,类似你一直Thread.Sleep(0)一样,精细的同学可以考虑Thread.sleep(30-(System.currentTimeMillis() - time));不过因为这里是动态方法,你需要考虑到可能造成超时的问题,我的方案是直接在catch中也写上刷新方法…
}
以上就是大致的意思。总的来说,无非就集中方案,就是分用处去限制罢了。