react-native-vision-camera: 🐛 `CameraDevice` contains formats that are not supported by `CamcorderProfile`

What’s happening?

When the video resolution is too high, recording does not work unless I use the H.265 codec.

Is it possible to know when to use the H.265 codec?

For example:

  • working with h264/h265: {“autoFocusSystem”: “contrast-detection”, “fieldOfView”: 78.63484214515682, “maxExposure”: 8, “maxFps”: 30, “maxISO”: 6400, “maxZoom”: 10, “minExposure”: -8, “minFps”: 1, “minISO”: 50, “photoHeight”: 2736, “photoWidth”: 3648, “pixelFormats”: [“yuv”, “native”], “supportsDepthCapture”: true, “supportsPhotoHdr”: false, “supportsVideoHdr”: false, “videoHeight”: 2056, “videoStabilizationModes”: [“off”, “standard”, “cinematic-extended”], “videoWidth”: 3648}
  • not working with h264 (/ working with h265): {“autoFocusSystem”: “contrast-detection”, “fieldOfView”: 78.63484214515682, “maxExposure”: 8, “maxFps”: 30, “maxISO”: 6400, “maxZoom”: 10, “minExposure”: -8, “minFps”: 1, “minISO”: 50, “photoHeight”: 2736, “photoWidth”: 3648, “pixelFormats”: [“yuv”, “native”], “supportsDepthCapture”: true, “supportsPhotoHdr”: false, “supportsVideoHdr”: false, “videoHeight”: 2736, “videoStabilizationModes”: [“off”, “standard”, “cinematic-extended”], “videoWidth”: 3648}

Reproduceable Code

const devices = Camera.getAvailableCameraDevices();
const device = devices.find((d) => d.position === 'back');
if (!device) {
    return;
}
const format = getCameraFormat(device, [
    { videoResolution: 'max' },
]);

// ...

return (
    <View style={s.container}>
        <Camera
            ref={this.cameraRef}
            style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}
            device={device}
            format={format}
            isActive={true}
            video={true}
            audio={true}
            resizeMode='contain'
        />
    </View>
);

Relevant log output

{"cause": {"message": "prepare failed.", "stacktrace": "java.io.IOException: prepare failed.
	at android.media.MediaRecorder._prepare(Native Method)
	at android.media.MediaRecorder.prepare(MediaRecorder.java:1107)
	at com.mrousavy.camera.core.RecordingSession.start(RecordingSession.kt:91)
	at com.mrousavy.camera.core.CameraSession.startRecording(CameraSession.kt:546)
	at com.mrousavy.camera.CameraView_RecordVideoKt.startRecording(CameraView+RecordVideo.kt:56)
	at com.mrousavy.camera.CameraViewModule$startRecording$1.invokeSuspend(CameraViewModule.kt:90)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
"}, "code": "capture/unknown", "message": "An unknown error occurred while trying to start a video recording! prepare failed.", "userInfo": null}

Camera Device

{
  "sensorOrientation": "landscape-right",
  "hardwareLevel": "limited",
  "maxZoom": 10,
  "minZoom": 1,
  "supportsLowLightBoost": false,
  "neutralZoom": 1,
  "physicalDevices": [
    "wide-angle-camera",
    "ultra-wide-angle-camera",
    "telephoto-camera"
  ],
  "supportsFocus": true,
  "supportsRawCapture": false,
  "isMultiCam": true,
  "name": "BACK (0)",
  "hasFlash": true,
  "hasTorch": true,
  "position": "back",
  "id": "0"
}

Device

HUAWEI P30 (ELE-L29) with Android 10

VisionCamera Version

3.6.10

Can you reproduce this issue in the VisionCamera Example app?

I didn’t try (⚠️ your issue might get ignored & closed if you don’t try this)

Additional information

About this issue

  • Original URL
  • State: closed
  • Created 7 months ago
  • Comments: 31 (10 by maintainers)

Most upvoted comments

I have other priorities that are higher than working on contributing back to this project. I did plan on coming back to do a PR once my app issues were resolved. As a common courtesy I added a comment because I found something that seemed to work for me that wasn’t thoroughly tested (and I needed to get my production app fixed that had issues with this and other libraries after IOS/Android updates). I’m also not familiar with the API this library was using, I just know how to run a debugger to set a break point on the error and to try some real-time changes to see if I can resolve the issue.

If you want to flame me for not creating a PR, feel free if that makes you feel better. Just know that responding to people in that way will make them less inclined to suggest a solution going forward.

wtf? lol

It’s always the same - someone creates an issue, wants it fixed “asap” because it hurts their business, then someone posts a local patch in the comments, and the issue goes stale. If you use open-source you are using something someone in the community has put hundreds or thousands of hours blood and sweat into, and then offered it to the public/you for free. To keep that project alive and working for the future you can either sponsor/support the devs/maintainers, or contribute back in some way shape or form - this would’ve been an ideal example.

I have other priorities that are higher than working on contributing back to this project.

I have other priorities too than to help other people fix issues for their business.

If you want to flame me for not creating a PR, feel free if that makes you feel better. Just know that responding to people in that way will make them less inclined to suggest a solution going forward.

lmao, I simply said “Would’ve been great …”. If you get hurt over such a suggestion that’s not my fault.

Doesn’t matter anyways, but I think the PR would’ve taken 2 minutes longer than writing that comment. I understand that time is valuable, and I understand that you are not required to create a PR, but still - it would’ve been great.

Anyways, issue is now resolved since that PR is merged, that’s all that matters.

Wow, you’re so fast! 😊

I just tested. Unfortunately, I encountered another error.

{"cause": {"message": "Error retrieving camcorder profile params", "stacktrace": "java.lang.RuntimeException: Error retrieving camcorder profile params
	at android.media.CamcorderProfile.native_get_camcorder_profile(Native Method)
	at android.media.CamcorderProfile.get(CamcorderProfile.java:467)
	at com.mrousavy.camera.extensions.RecordingSession_getRecommendedBitRateKt.getRecommendedBitRate(RecordingSession+getRecommendedBitRate.kt:48)
	at com.mrousavy.camera.core.RecordingSession.getBitRate(RecordingSession.kt:132)
	at com.mrousavy.camera.core.RecordingSession.<init>(RecordingSession.kt:37)
	at com.mrousavy.camera.core.CameraSession.startRecording(CameraSession.kt:545)
	at com.mrousavy.camera.CameraView_RecordVideoKt.startRecording(CameraView+RecordVideo.kt:44)
	at com.mrousavy.camera.CameraViewModule$startRecording$1.invokeSuspend(CameraViewModule.kt:91)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)

This is due to the ‘quality’ used (returned by findClosestCamcorderProfileQuality). In my case, it returns CamcorderProfile.QUALITY_4KDCI, and my phone only supports up to CamcorderProfile.QUALITY_2160P.

If I set it to CamcorderProfile.QUALITY_2160P, it works 😃 and the bitrate is correct! (“Created 3648 x 2736 @ 30 FPS H264 MOV LANDSCAPE_RIGHT 40.0 Mbps RecordingSession (with audio)!”)

Ohh, interesting! I’ll try to fix the getDefaultBitRate() method by using CamcorderProfile instead of computing it on my own then!