带有OpenGL的MediaRecorder曲面输入-如果启用了音频录制,则会出现此问题

问题描述

我想用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

创建EGLSurface 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作为时基。但是无论如何,您都有几种选择:

  1. 如果摄像机使用TIMESTAMP_SOURCE_REALTIME,请在记录开始时测量两个时间戳之间的时差,并相应地调整您输入到setPresentationTime(delta = elapsedRealtime - uptimeMillis; timestamp = timestamp - delta;)中的时间戳
  2. 只需使用uptimeMillis() * 1000000作为setPresentationTime的时间即可。这可能会导致过多的A / V偏斜,但是很容易尝试。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...