JCutting8
JCutting8

Reputation: 774

Camera2 MediaRecorder | Audio and Video are recording asynchronously

I am building an app that records video - quite simple. I am using the Camera2 API along with MediaRecorder to achieve this. I have been able to successfully record video, but the sound and video are recording asynchronously.

Let's say I record a 5 second video - the output will be a video file that is 10 seconds in length. The first 5 seconds is a still frame playing the audio, the last 5 seconds will be the video frames without any sound. It is as if the MediaRecorder is recording the audio before it even begins to receive video frames.

Device I am using for testing is a Samsung Galaxy 7. I managed to find one person online facing a similar issue with no answers/response: Android Audio and Video track playing subsequently

How I set up my MediaRecorder:

private void setUpMediaRecorder() throws IOException {
    final Activity activity = this;
    if (null == activity) {
        print("Null activity!");
        return;
    }

    mediaRecorder = new MediaRecorder();
    mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
    if (nextVideoAbsolutePath == null || nextVideoAbsolutePath.isEmpty()) {
        nextVideoAbsolutePath = getVideoFilePath(this);
    }
    mediaRecorder.setOutputFile(nextVideoAbsolutePath);
    mediaRecorder.setVideoEncodingBitRate(10000000);
    mediaRecorder.setVideoFrameRate(30);
    mediaRecorder.setVideoSize(videoSize.getWidth(), videoSize.getHeight());
    mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
    mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
    int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
    switch (sensorOrientation) {
        case SENSOR_ORIENTATION_DEFAULT_DEGREES:
            mediaRecorder.setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation));
            break;
        case SENSOR_ORIENTATION_INVERSE_DEGREES:
            mediaRecorder.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation));
            break;
    }
    mediaRecorder.prepare();
}

My StartRecording() function:

void startRecording()
{
    //Check the camera and TextureView are ready
    if (null == cameraDevice || !textureView.isAvailable() || null == previewSize) {
        return;
    }

    try {

        //Close the existing capture session used for preview
        closePreviewSession();

        //Set up the MediaRecorder
        setUpMediaRecorder();

        //Compile list of surfaces
        List<Surface> surfaces = new ArrayList<>();
        surfaces.add(previewSurface);
        final Surface recorderSurface = mediaRecorder.getSurface();
        surfaces.add(recorderSurface);

        // Start the capture session
        // Once the session starts, we can update the UI and start recording
        cameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                //Store a reference to the session
                currentCaptureSession = cameraCaptureSession;

               //Flag that recording has started
               isRecording = true;
               toggleRecordingButton();

                try {
                    //Initiate requestBuilder
                    captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);

                    //Add the surfaces to the request
                    captureRequestBuilder.addTarget(previewSurface); 
                    captureRequestBuilder.addTarget(recorderSurface);

                    //Build
                    captureRequest = captureRequestBuilder.build();
                    print("CameraCaptureSession stateCallback onConfigured() - Starting media recorder");

                    //Start recording
                    mediaRecorder.start();

                    //Set repeating request for VIDEO
                    currentCaptureSession.setRepeatingRequest(captureRequest, new CameraCaptureSession.CaptureCallback() {
                        @Override
                        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {

                        }
                    }, null);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                print("onConfigureFailed");
                Activity activity = MainActivity.this;
                if (null != activity) {
                    Toast.makeText(activity, "Failed", Toast.LENGTH_SHORT).show();
                }
                isRecording = false;
            }
        }, backgroundHandler);
    } catch (Exception e) {
        e.printStackTrace();
    }


}

My StopRecording() function:

private void stopRecording() {
    // UI
    isRecording = false;
    toggleButtonsWhileRecording();

    // Stop recording
    mediaRecorder.stop();
    mediaRecorder.reset();

    //Reset path
    nextVideoAbsolutePath = null;
}

Upvotes: 1

Views: 1616

Answers (1)

JCutting8
JCutting8

Reputation: 774

Turns out this is a bug limited to certain Samsung Galaxy devices only. See more about this issue and a workaround for detecting the long frame and correcting it after recording: Saved video file only has one video frame

In summary, the bug is related to these devices coming out of Deep Sleep. After resetting the phone, problem is solved (but comes back if I let the phone go into Deep Sleep).

Upvotes: 2

Related Questions