控制帧率
一般而言,使用MediaCodec配合OpenGLES编码摄像头数据是常规做法。如果要限制帧率,最简单的方法是限制OpenGLES的绘制间隔时间。
假设使用GLSurfaceView
进行绘制,设置RenderMode为RENDERMODE_WHEN_DIRTY
,在SurfaceTexture
监听onFrameAvailable()
来通知GLSurfaceView
绘制时,一般手机能达到60FPS。
按照上面的说法,例如要限制到30FPS,只需要在onFrameAvailable()
或者onDrawFrame()
中加入sleep()
,每次绘制后睡眠一定时间,这样预览画面和实际编码出来的MP4将会是同样的30FPS。
注:使用Camera
设定预览帧率貌似不起作用,起码在我的华为Mate9 Pro上没有效果。
但是,我希望可以做到预览画面帧率不变,编码时限制FPS,就需要额外启动线程来处理。
由于MediaCodec中使用InputSurface编码数据时需要运行在GL线程,一般的做法都是额外启动线程->初始化EGL环境->编码。在这基础上,只要我们限制通知MediaCodec编码的频率,就可以限制编码出来的数据的FPS。
下面是自己demo中的部分代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| @Override public void onDrawFrame(GL10 gl) { if (!mMediaRecorder.isInit()) { mMediaRecorder.setEglContext(EGL14.eglGetCurrentContext()); mMediaRecorder.setTextureId(mCameraDrawable.getTextureId()); mMediaRecorder.init(); }
if (!mMediaRecorder.isStart()) { mMediaRecorder.startRecord();
new Thread() { @Override public void run() { int fps = 20; long fpsTime = 1000 / fps; long time = System.currentTimeMillis(); long diffTime = 0;
while (true) { LogUtils.i("start"); final SurfaceTexture surfaceTexture = mCameraDrawable.getSurfaceTexture(); surfaceTexture.getTransformMatrix(mTextureMartix); mMediaRecorder.record(surfaceTexture.getTimestamp(), mTextureMartix); diffTime = System.currentTimeMillis() - time; if (diffTime < fpsTime) { try { sleep(fpsTime - diffTime); } catch (InterruptedException e) { e.printStackTrace(); } } if(!mMediaRecorder.isStart()){ break; } time = System.currentTimeMillis(); } } }.start(); }
mCameraDrawable.draw();
mFpsPrinter.printFPS(); }
|
上述代码中新起的线程就是控制MediaCodec编码频率的。值得一提的是,MediaCodec使用InputSurface编码时,都是需要通过SurfaceTexture
来获取每一帧的时间戳。过去我曾经陷入过迷思,不知道如何控制时间戳来控制帧率。后来想想,其实在编译每一帧时获取其对应的时间戳即可。
编码时加速、减速视频
假设我们想保持帧率不变,但是实际编码出来的MP4是加速或减速过的视频,可以通过修改上面提到的时间戳效果。比如,将输入的SurfaceTexture.getTimestamp()
乘以2,再传递给EGLExt.eglPresentationTimeANDROID()
,最终的效果就是整个视频是加快一倍的;减速同理,除以指定系数即可。
评论