问题描述
我想用MediaRecorder
代替MediaCodec
来录制视频,因为众所周知,它很容易使用。
我还想在录制时使用OpenGL处理帧
然后,我使用Grafika的ContinuousCaptureActivity示例中的示例代码来初始化EGL渲染上下文,创建cameraTexture
并将其作为Surface
https://github.com/google/grafika/blob/master/app/src/main/java/com/android/grafika/ContinuousCaptureActivity.java#L392传递给Camera2 API
并从我们的encodeSurface
https://github.com/google/grafika/blob/master/app/src/main/java/com/android/grafika/ContinuousCaptureActivity.java#L418
recorderSurface
依此类推(处理帧与Grafika示例中的帧相同,与示例代码Grafika代码中的所有内容相同)
然后,当我开始录制(MediaRecorder.start()
)时,如果未设置音频源,它会录制好视频
但是如果还启用了录音
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC)
...
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
然后,最终视频的时长(长度)很大,因此无法真正播放。因此,使用Surface作为输入和使用GLES添加和处理帧时,MediaRecorder音频编码器会毁掉一切
我不知道如何解决它。
这是我处理框架的代码(基于Grafika示例,几乎相同):
class GLCameraFramesRender(
private val width: Int,private val height: Int,private val callback: Callback,recorderSurface: Surface,eglCore: EglCore
) : OnFrameAvailableListener {
private val fullFrameBlit: FullFrameRect
private val textureId: Int
private val encoderSurface: WindowSurface
private val tmpMatrix = FloatArray(16)
private val cameraTexture: SurfaceTexture
val cameraSurface: Surface
init {
encoderSurface = WindowSurface(eglCore,recorderSurface,true)
encoderSurface.makeCurrent()
fullFrameBlit = FullFrameRect(Texture2dProgram(Texture2dProgram.ProgramType.TEXTURE_EXT))
textureId = fullFrameBlit.createTextureObject()
cameraTexture = SurfaceTexture(textureId)
cameraSurface = Surface(cameraTexture)
cameraTexture.setOnFrameAvailableListener(this)
}
fun release() {
cameraTexture.setOnFrameAvailableListener(null)
cameraTexture.release()
cameraSurface.release()
fullFrameBlit.release(false)
eglCore.release()
}
override fun onFrameAvailable(surfaceTexture: SurfaceTexture) {
if (callback.isRecording()) {
drawFrame()
} else {
cameraTexture.updateTexImage()
}
}
private fun drawFrame() {
cameraTexture.updateTexImage()
cameraTexture.getTransformMatrix(tmpMatrix)
GLES20.glViewport(0,width,height)
fullFrameBlit.drawFrame(textureId,tmpMatrix)
encoderSurface.setPresentationTime(cameraTexture.timestamp)
encoderSurface.swapBuffers()
}
interface Callback {
fun isRecording(): Boolean
}
}
解决方法
您的时间戳很可能不在同一时基中。媒体记录系统通常希望在uptimeMillis时基中使用时间戳,但是许多相机设备在elapsedRealtime时基中生成数据。一种计算设备进入深度睡眠的时间,另一种则不计算时间。自您重新启动设备以来,时间越长,差异就越大。
在添加音频之前没有关系,因为MediaRecorder的内部音频时间戳记将在uptimeMillis中,而摄像机帧的时间戳记将是经过时的Realtime。视音频同步不良,可能会导致超过几分之一秒的差异。几分钟或更长时间只会搞乱一切。
当相机直接与媒体记录堆栈对话时,它会自动调整时间戳;因为您将GPU放在了中间,所以不会发生这种情况(因为相机不知道帧最终将在哪里移动)。
您可以通过SENSOR_INFO_TIMESTAMP_SOURCE检查相机是否使用elapsedRealtime作为时基。但是无论如何,您都有几种选择:
- 如果摄像机使用TIMESTAMP_SOURCE_REALTIME,请在记录开始时测量两个时间戳之间的时差,并相应地调整您输入到setPresentationTime(
delta = elapsedRealtime - uptimeMillis; timestamp = timestamp - delta;
)中的时间戳 - 只需使用
uptimeMillis() * 1000000
作为setPresentationTime的时间即可。这可能会导致过多的A / V偏斜,但是很容易尝试。