camera2's setVideoFramerate() doesn't have any effect

1k Views Asked by At

I'm developing my camera app for my LG G4 and I can't find a way to record at a constant framerate. I took the google sample Camera2 app to add my features.

When I want to record in UHD :

mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);

mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
if (mNextVideoAbsolutePath == null || mNextVideoAbsolutePath.isEmpty()) {
    mNextVideoAbsolutePath = getVideoFilePath(getActivity());
}
mMediaRecorder.setOutputFile(mNextVideoAbsolutePath);

mMediaRecorder.setVideoEncodingBitRate(35 * 1000 * 1000);
mMediaRecorder.setVideoSize(3840, 2160);
mMediaRecorder.setVideoFrameRate(30);

mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);

The framerate of 3 differents output videos is 27.61, 28.39, 26.24 etc .. I can't get a constant framterate at 29.97 of 30fps. I tried to increase bitrate to 50Mbps, decrease it to 30Mbps but it doesn't change anything.

The weirdest part is that I can't even record above 30fps in 1080p :

mMediaRecorder.setVideoSize(1920, 1080);
mMediaRecorder.setVideoFrameRate(60);

It records a FHD footage at 29.69 last time I tried but it is as random as UHD footage. What am I doing wrong ?

I've checked Recording 60fps video with Camera2(on Android version 21) API out but it doesn't work either. I also found some answers but they were using the old camera API (with Camera.Parameters) which is deprecated now.

Is there another parameter ?

2

There are 2 best solutions below

1
hyand On

NOTE: On some devices that have auto-frame rate, this sets the maximum frame rate, not a constant frame rate. Actual frame rate will vary according to lighting conditions.

try this:

mediaRecorder.setCaptureRate(30);
0
Matheus Fortunato Alves On

For anyone who is still facing this issue, here is my solution (note that FIXED_FRAME_RATE is private constant of customizable value within your device capabilities):

private void startRecording() {
    if (cameraDevice == null || !previewView.isAvailable() || videoSize == null) {
        return;
    }

    try {
        closePreviewSession();
        setUpMediaRecorder();

        SurfaceTexture texture = previewView.getSurfaceTexture();
        texture.setDefaultBufferSize(videoSize.getWidth(), videoSize.getHeight());
        Surface previewSurface = new Surface(texture);

        List<Surface> surfaces = new ArrayList<>();
        surfaces.add(previewSurface);
        surfaces.add(mediaRecorder.getSurface());

        captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
        for (Surface surface : surfaces) {
            captureRequestBuilder.addTarget(surface);
        }

        // Set the frame rate in the CaptureRequest
        captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
        captureRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, new Range<>(FIXED_FRAME_RATE, FIXED_FRAME_RATE));

        cameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(@NonNull CameraCaptureSession session) {
                if (cameraDevice == null) {
                    return;
                }

                cameraCaptureSession = session;
                updatePreview();

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mediaRecorder.start();
                        isRecording = true;
                    }
                });
            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                // You can Handle configuration failure if desired
            }
        }, null);

    } catch (CameraAccessException | IOException e) {
        e.printStackTrace();
    }
}

Just call startRecording() after pressing a button (for example) and all should be good.

Please note that you should also setup mediaRecorder video frame rate with same value of FIXED_FRAME_RATE as the original question did. setupMediaRecorder() method.