Search code examples
androidkotlinandroid-cameravideo-captureandroid-camera2

Android Camera Video is blank with white lines


I have implemented Camera2 API of Android using MediaRecorder to record video from the Camera. The Camera Previews are working fine and the video file is also created successfully without any errors. However, the output video is blank and has white flickering lines.

This is the output video I get.

Video output from MediaRecorder

The code for preparing MediaRecorder

val surface = MediaCodec.createPersistentInputSurface()
surface.release()

val outputFile = createFile(applicationContext,"mp4")
val mMediaRecorder = MediaRecorder()
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC)
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE)
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
mMediaRecorder.setOutputFile(outputFile.absolutePath)
mMediaRecorder.setVideoEncodingBitRate(10_000_000)
mMediaRecorder.setVideoFrameRate(30)
mMediaRecorder.setVideoSize(matchedResolution.width, matchedResolution.height)
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
mMediaRecorder.setInputSurface(surface)

Creating the session.

val previewSurface = surfaceView2.holder.surface

val captureCallbackVideo =
    object : CameraCaptureSession.StateCallback() {
        override fun onConfigureFailed(session: CameraCaptureSession) {}
        override fun onConfigured(session: CameraCaptureSession) {
            // session configured
            val previewRequestBuilder =
                cameraDevice.createCaptureRequest(TEMPLATE_RECORD)
                    .apply {
                        addTarget(previewSurface)
                        addTarget(surface)
                        set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, Range(30,30))
                    }
            session.setRepeatingRequest(
                previewRequestBuilder.build(),
                null,
                Handler { true }
            )
        }
    }

cameraDevice.createCaptureSession(mutableListOf(previewSurface, surface), captureCallbackVideo, Handler { true })

For starting the recording

mMediaRecorder.prepare()
mMediaRecorder.start()

For stopping

mMediaRecorder.stop()
mMediaRecorder.release()

Solution

  • I had figured it out. The problem was with the input surface of MediaRecorder. Apparently, the way of Google's code of camera2 at GitHub is not right in setting up the Surface for MediaRecorder correctly.

    The solution is that we should not provide Surface as input to the MediaRecorder, but prepare the MediaRecorder without a Surface and then use its internal Surface for our CaptureRequest

    mMediaRecorder = MediaRecorder()
    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE)
    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
    mMediaRecorder.setVideoEncodingBitRate(10_000_000)
    //Uncomment on physical device, comment on emulator
    //mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
    mMediaRecorder.setVideoSize(matchedResolution.width, matchedResolution.height)
    mMediaRecorder.setVideoFrameRate(30)
    mMediaRecorder.setOutputFile(outputFile.absolutePath)
    try {
        mMediaRecorder.prepare()
    } catch (e: java.lang.Exception) {
        e.printStackTrace()
        return
    }
    

    For creating session

    val previewSurface = surfaceView2.holder.surface
    
    val captureCallbackVideo =
        object : CameraCaptureSession.StateCallback() {
            override fun onConfigureFailed(session: CameraCaptureSession) {}
            override fun onConfigured(session: CameraCaptureSession) {
                // session configured
                val previewRequestBuilder =
                    cameraDevice.createCaptureRequest(TEMPLATE_RECORD)
                        .apply {
                            addTarget(previewSurface)
                            addTarget(mMediaRecorder.surface)
                            set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, Range(30,30))
                        }
                session.setRepeatingRequest(
                    previewRequestBuilder.build(),
                    null,
                    Handler { true }
                )
            }
        }
    
    cameraDevice.createCaptureSession(mutableListOf(previewSurface, mMediaRecorder.surface), captureCallbackVideo, Handler { true })
    

    For starting recording:

    mMediaRecorder.start()
    

    For stopping a recording:

    mMediaRecorder.stop()
    mMediaRecorder.release()