Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 The resolution of the PhotoFile is different than the format photo resolution #3031

Closed
4 of 5 tasks
Dingenis opened this issue Jun 27, 2024 · 35 comments · Fixed by #3063
Closed
4 of 5 tasks

🐛 The resolution of the PhotoFile is different than the format photo resolution #3031

Dingenis opened this issue Jun 27, 2024 · 35 comments · Fixed by #3063
Labels
🐛 bug Something isn't working

Comments

@Dingenis
Copy link

What's happening?

When taking a photo the PhotoFile has a different resolution than the format.

Reproduceable Code

let device = useCameraDevice(cameraPosition)
const format = useCameraFormat(device, [{ photoResolution: 'max' }])

useEffect(() => {
    const f =
      format != null
        ? `(${format.photoWidth}x${format.photoHeight} photo / ${format.videoWidth}x${format.videoHeight}@${format.maxFps} video @ ${fps}fps)`
        : undefined
    console.log(`Camera: ${device?.name} | Format: ${f}`)
  }, [device?.name, format, fps])

return   
 <ReanimatedCamera
  style={StyleSheet.absoluteFill}
  device={device}
  isActive={isActive}
  ref={camera}
  format={format}
  photo={true}
  video={true}
 />

Relevant log output

Metro:

 LOG  Camera: 1 (FRONT) androidx.camera.camera2 | Format: (3264x2448 photo / 1920x1080@30 video @ 30fps)
 LOG  Media captured! {"isMirrored":true,"path":"/data/user/0/com.mrousavy.camera.example/cache/mrousavy-5497026049884411527.jpg","isRawPhoto":false,"height":1080,"orientation":"portrait","width":1440}
 LOG  Image loaded. Size: 1072x1440

Logcat:

2024-06-27 12:55:12.082 16956-18577 CameraView.takePhoto    com.mrousavy.camera.example          I  Successfully captured 1440 x 1080 photo!

Camera Device

{
  "formats": [],
  "sensorOrientation": "landscape-right",
  "hardwareLevel": "limited",
  "maxZoom": 4,
  "minZoom": 1,
  "maxExposure": 20,
  "supportsLowLightBoost": false,
  "neutralZoom": 1,
  "physicalDevices": [
    "wide-angle-camera"
  ],
  "supportsFocus": true,
  "supportsRawCapture": false,
  "isMultiCam": false,
  "minFocusDistance": 0,
  "minExposure": -20,
  "name": "1 (FRONT) androidx.camera.camera2",
  "hasFlash": false,
  "hasTorch": false,
  "position": "front",
  "id": "1"
}

Device

Samsung Galaxy A53 (Android 14)

VisionCamera Version

4.3.1

Can you reproduce this issue in the VisionCamera Example app?

Yes, I can reproduce the same issue in the Example app here

Additional information

@Dingenis Dingenis added the 🐛 bug Something isn't working label Jun 27, 2024
Copy link

Guten Tag, Hans here.

Note

New features, bugfixes, updates and other improvements are all handled mostly by @mrousavy in his free time.
To support @mrousavy, please consider 💖 sponsoring him on GitHub 💖.
Sponsored issues will be prioritized.

@Dingenis
Copy link
Author

Hi @mrousavy, our company (Spaces Experiences) has been a sponsor for around four months and we are very happy that this library exists, so thank you! This issue I posted here has priority for us, if you have any time taking a look would be much appreciated.
Cheers, Dingenis

@mrousavy
Copy link
Owner

Hi!
Thanks for the sponsorship - does this also happen if you disable video?

@Dingenis
Copy link
Author

Dingenis commented Jun 27, 2024

Hi, thanks for your quick reply. Yes, with only video={false} it still happens. However with video={false} preview={false} the photo does have the same resolution (as the format):

 LOG  Media captured! {"isMirrored":true,"path":"/data/user/0/com.mrousavy.camera.example/cache/mrousavy-7410530320673441399.jpg","isRawPhoto":false,"height":2448,"orientation":"portrait","width":3264}

@JustinHaut
Copy link

fwiw visionCamera version 4.3.2 with my Galaxy S22 doesn't have this issue. I tried disabling/enabling video and preview, and verified both front and back camera format and photo resolutions match.

@Dingenis
Copy link
Author

Yes good point @JustinHaut, I also tested on a Galaxy A41 and there it does indeed also match.

@mrousavy
Copy link
Owner

Okay thanks, good research on preview={false} that is actually quite helpful - I just merged a PR a few days ago that changes the Preview resolution selector to now use the format's video resolution, just like on iOS. (#3026)

This might fix this issue.

@Dingenis
Copy link
Author

Dingenis commented Jun 28, 2024

@mrousavy I can reproduce it with the example app with video={false} on the following devices (via Android Studio Device Streaming):

Pixel Fold

 LOG  Camera: 0 (BACK) androidx.camera.camera2 | Format: (4000x3000 photo / 3840x2160@60 video @ 60fps)
 LOG  Media captured! {"isMirrored":false,"path":"/data/user/0/com.mrousavy.camera.example/cache/mrousavy-2125636817995574257.jpg","isRawPhoto":false,"height":2160,"orientation":"portrait","width":3840}

Pixel 8 Pro

 LOG  Camera: 0 (BACK) androidx.camera.camera2 | Format: (4000x2000 photo / 3840x2160@60 video @ 60fps)
 LOG  Media captured! {"isMirrored":false,"path":"/data/user/0/com.mrousavy.camera.example/cache/mrousavy-7321524609184097620.jpg","isRawPhoto":false,"height":2160,"orientation":"landscape-right","width":3840}

Pixel 7a

 LOG  Camera: 0 (BACK) androidx.camera.camera2 | Format: (4000x2000 photo / 3840x2160@60 video @ 60fps)
 LOG  Media captured! {"isMirrored":false,"path":"/data/user/0/com.mrousavy.camera.example/cache/mrousavy-2799048483279202367.jpg","isRawPhoto":false,"height":1080,"orientation":"landscape-right","width":1920}

Samsung Galaxy A53

LOG  Camera: 1 (FRONT) androidx.camera.camera2 | Format: (3264x2448 photo / 1920x1080@30 video @ 30fps)
LOG  Media captured! {"isMirrored":true,"path":"/data/user/0/com.mrousavy.camera.example/cache/mrousavy-5497026049884411527.jpg","isRawPhoto":false,"height":1080,"orientation":"portrait","width":1440}

And I cannot reproduce it on these devices:

Samsung Galaxy S23 Ultra

 LOG  Camera: 0 (BACK) androidx.camera.camera2 | Format: (4000x1868 photo / 3840x2160@30 video @ 30fps)
 LOG  Media captured! {"isMirrored":false,"path":"/data/user/0/com.mrousavy.camera.example/cache/mrousavy-9181039376298899107.jpg","isRawPhoto":false,"height":1868,"orientation":"landscape-right","width":4000}

SHARP AQUOS sense2 SH-01L

 LOG  Camera: 0 (BACK) androidx.camera.camera2 | Format: (864x480 photo / 1920x1080@30 video @ 30fps)
 LOG  Media captured! {"isMirrored":false,"path":"/data/user/0/com.mrousavy.camera.example/cache/mrousavy-1548835093455550811.jpg","isRawPhoto":false,"height":480,"orientation":"landscape-right","width":864}

@Dingenis
Copy link
Author

Dingenis commented Jun 28, 2024

Okay thanks, good research on preview={false} that is actually quite helpful - I just merged a PR a few days ago that changes the Preview resolution selector to now use the format's video resolution, just like on iOS. (#3026)

This might fix this issue.

Good to hear! Let me know if can help in any way once you get to it (like testing or something). To add I used the main branch to test on the devices shown above, so I don't think (#3026) fixes the issue.

@mrousavy
Copy link
Owner

The problem here is that the Formats API on Android is not 100% accurate, which sucks - I know.

On Android, there is no API to figure out which combination of output resolutions is supported. On iOS there is, which is why the format always determines exactly what resolution the photo and video will have.

But on Android, it's always only an estimate, the OS might not be able to support 4k Photo and 4k Video at the same time, only individually.
So in this case, CameraX is smart enough to fall back to a lower resolution, either for photo or for video.

I try to get an accurate list of supported video/photo resolutions here;

val qualities = videoCapabilities.getSupportedQualities(dynamicRange)
val videoSizes = qualities.map { it as ConstantQuality }.flatMap { it.typicalSizes }
val photoSizes = cameraInfoInternal.getSupportedResolutions(ImageFormat.JPEG)
val fpsRanges = cameraInfo.supportedFrameRateRanges

..but again, CameraX might choose a different resolution if it feels like it. I raised this concern with the Google/CameraX team, and after many months they came back to me and said they are going to implement an API that actually gives you accurate resolution (and FPS) combinations - which is what VisionCamera's format API is about.

So we might see this sometime soon, but for now this is all we have on Android unfortunately.

@Dingenis
Copy link
Author

Dingenis commented Jun 28, 2024

Hey, thanks for your explanation and your time! That's a bummer.... seems kind of stupid for Android to not have such a API, but I don't know too much of the Android camera world so idk 🤷 (maybe it's really hard to implement with all the different vendors who might not fully comply)

It does make it really hard to manually select a format, since the format you select can be one that is not compatible thus resulting in the photo resolution (and sometimes aspect ratio) being different than expected. When looking at the Pixel 7a it's a massive difference instead of 4000x2000 photo you get a 1920x1080 photo. So you think you are getting the highest resolution picture while instead you might have a format which actually has a lower photo resolution than maybe a other one where the video resolution is lower (which is compatible). At least if I understand it correctly? 🤔

I was thinking is there a running issue for this in the CameraX issue tracker, maybe we as community could upvote it so that they might prioritize this? Furthermore, I was wondering do you have any advice on how to deal with this? Like should formats with a high resolution and and high video ouput be avoided (because they might be invalid)?

Yet again, thanks for your time.
Cheers,
Dingenis.

@mrousavy
Copy link
Owner

Formats with high resolutions should not be avoided, no.

But the useCameraFormat/getAvailableCameraDevices should just be accurate in telling us which video/photo resolution combinations are supported.

@Dingenis
Copy link
Author

Dingenis commented Jul 1, 2024

Thanks for your reply @mrousavy! Hopefully, the Android team will pick this up somewhere in the near future.

I misunderstood some things on how this works and have since thought about it some more.
So I have one more question, if you would indulge me, I see that the aspect ratio of the preview is set to the format's photo aspect ratio (if using photo={true}) here. Let's say the format is a "invalid" combination thus CameraX falls back on another resolution, then the aspect ratio of the preview should also change to that new aspect ratio. Currently it doesn't, is that because we only know the aspect ratio of the fallback once the user captures a picture?

@mrousavy
Copy link
Owner

mrousavy commented Jul 2, 2024

This has been changed recently and now works exactly like on iOS - preview will try to match the format's video resolution and aspect ratio, with the priority being the aspect ratio.

@mrousavy
Copy link
Owner

mrousavy commented Jul 2, 2024

Hey can you check if release 4.4.0 changed anything?

@Dingenis
Copy link
Author

Dingenis commented Jul 4, 2024

Hey, thanks again for you effort! I just tested it on my device, and indeed the preview confirms to the format's video resolution and aspect ratio. The issue with the resolution being lower for the ImageCapture then format still happens.
Interestingly, I tried the CameraXBasic sample project (after upgrading it to the latest deps) and there the resolution is high (like the format in react-native-vision-camera).

@Dingenis
Copy link
Author

Dingenis commented Jul 4, 2024

The code from the sample project boils down to this:

val rotation = fragmentCameraBinding.viewFinder.display.rotation

// CameraProvider
val cameraProvider = cameraProvider ?: throw IllegalStateException("Camera initialization failed.")

// CameraSelector
val cameraSelector =
    CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()

// Preview
val previewResolutionSelector =
    ResolutionSelector.Builder()
        .setAllowedResolutionMode(ResolutionSelector.PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION)
        .build()

preview =
    Preview.Builder()
        .setResolutionSelector(previewResolutionSelector)
        .setTargetRotation(rotation)
        .build()

// ImageCapture
val imageCaptureResolutionSelector =
    ResolutionSelector.Builder()
        .setAllowedResolutionMode(ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE)
        .build()

imageCapture =
    ImageCapture.Builder()
        .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
        .setResolutionSelector(imageCaptureResolutionSelector)
        .setTargetRotation(rotation)
        .build()

// Must unbind the use-cases before rebinding them
cameraProvider.unbindAll()

try {
    // A variable number of use-cases can be passed here -
    // camera provides access to CameraControl & CameraInfo
    camera = cameraProvider.bindToLifecycle(this, cameraSelector, imageCapture, preview)

    val previewResolution = preview?.resolutionInfo?.resolution

    if (previewResolution != null) {
        Log.i("DingenisCamera", "${previewResolution.width}x${previewResolution.height}")
    }

    val imageCaptureResolution = imageCapture?.resolutionInfo?.resolution

    if (imageCaptureResolution != null) {
        Log.i("DingenisCamera", "${imageCaptureResolution.width}x${imageCaptureResolution.height}")
    }

    // Attach the viewfinder's surface provider to preview use case
    preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)
    observeCameraState(camera?.cameraInfo!!)
} catch (exc: Exception) {
    Log.e(TAG, "Use case binding failed", exc)
}

Which gives me the following preview and image capture resolution, this matches the highest resolution format from react-native-vision-camera (but there CameraX choses a different resolution):

2024-07-04 14:58:24.154 31948-31948 DingenisCamera                    com.android.example.cameraxbasic     I  1440x1080
2024-07-04 14:58:24.154 31948-31948 DingenisCamera                    com.android.example.cameraxbasic     I  4624x3468

@Dingenis
Copy link
Author

Dingenis commented Jul 4, 2024

Alright @mrousavy, I think I found the culprit! I was playing around with the configuration of the Preview in the CameraXBasic sample project using Preview.Builder and that's when I noticed when I used setTargetFrameRate with a min of 21 or higher the ImageCapture resolution would change to a low value (1440x1080). Whereas if 20 was used as min (setTargetFrameRate(Range(20, 30))) the higher resolution would be used (4624x3468).

In react-native-vison-camera the format says it has a maxFps of 30, but it seems that is not accurate because when changing the fps camera prop to 20 the format now does work!

 LOG  Camera: 0 (BACK) androidx.camera.camera2 | Format: (4624x3468 photo / 1920x1080@30 video @ 30fps)
 LOG  Media captured! {"isMirrored":false,"path":"/data/user/0/com.mrousavy.camera.example/cache/mrousavy-1755692348160762567.jpg","isRawPhoto":false,"height":3468,"orientation":"portrait","width":4624}

@mrousavy
Copy link
Owner

mrousavy commented Jul 4, 2024

Oh wow interesting, great research!

I'm thinking what a good solution to this would be, maybe I'll just always allow minFps of 20...

@Dingenis
Copy link
Author

Dingenis commented Jul 4, 2024

Thanks! Good question, well to set it to a min of 20 seems like a simple solution, so i like it 😁😂 . I don't know if there are any other formats which have an even lower maxFps, so that could be a issue.

Another solution could be to change the fps camera prop to a range, in that way the responsibility is with the app maker to select a suitable range. That might be less confusing, since if you set the fps camera prop to 30 but you get 20 one might be confused. But that could also just be solved by added some documentation like: "on android the fps is target and is therefore not guaranteed" so I am not sure what is best either.

@Dingenis
Copy link
Author

Dingenis commented Jul 4, 2024

I can also confirm that lowering the fps also fixes the format on these devices (prob the rest mentioned early too):

Pixel 7a

 LOG  Camera: 0 (BACK) androidx.camera.camera2 | Format: (4624x3472 photo / 3840x2160@60 video @ 30fps)
 LOG  Media captured! {"isMirrored":false,"path":"/data/user/0/com.mrousavy.camera.example/cache/mrousavy-4166822674804579466.jpg","isRawPhoto":false,"height":3472,"orientation":"landscape-right","width":4624}

Pixel Fold

 LOG  Camera: 0 (BACK) androidx.camera.camera2 | Format: (4000x3000 photo / 3840x2160@60 video @ 30fps)
 LOG  Media captured! {"isMirrored":false,"path":"/data/user/0/com.mrousavy.camera.example/cache/mrousavy-128395477126160525.jpg","isRawPhoto":false,"height":3000,"orientation":"portrait","width":4000}

@mrousavy
Copy link
Owner

@Dingenis let's do this;

  • you create an issue in the Google CameraX issue tracker about this, because I believe this is an issue
  • I create a PR to VisionCamera to lower the min FPS to 20 so it is fixed here

cool?

@mrousavy
Copy link
Owner

Created a PR to automatically fix this: #3063

@Dingenis
Copy link
Author

@mrousavy Nice! 🔥

I think this is intended behaviour, looking at the description of setTargetFrameRate it says the following:

Sets the target frame rate range in frames per second for the associated Preview use case.

Device will try to get as close as possible to the target frame rate. This may affect the selected resolutions of the surfaces, resulting in better frame rates at the potential reduction of resolution.

Achieving target frame rate is dependent on device capabilities, as well as other concurrently attached use cases and their target frame rates. Because of this, the frame rate that is ultimately selected is not guaranteed to be a perfect match to the requested target.

The line Device will try to get as close as possible to the target frame rate. This may affect the selected resolutions of the surfaces, resulting in better frame rates at the potential reduction of resolution. does explain this behaviour.

In combination with how react-native-vision-camera determines the max fps here using supportedFrameRateRanges which seems to be not fully accurate. A situation arises where the max FPS is higher than what is supported for that combination and thus the setTargetFrameRate will possibly lower the resolution in order to get closer to the FPS.

@mrousavy
Copy link
Owner

Ahh okay - so I think we shouldn't hardcode it to 20 then, but instead use the minFps value here.... wdyt?

@Dingenis
Copy link
Author

Yes, I think that could be nice! I do wonder how that affects the device, for example that high resolution format for my Galaxy A53 has a min fps of 8. Will the device chose to output the highest fps for that resolution if we have a range of [8, 30]? Or will it act "lazy" and chose a lower fps while a higher fps is possible? I mean that sounds insane but it's unclear from the API documentation on how that works.

@Dingenis
Copy link
Author

It seems that it choses the highest value possible (link)

@mrousavy
Copy link
Owner

One thing I'm worried about is if devices have multiple overlapping FPS Ranges.

For example, a device could report

  • [8, 30]
  • [12, 30]
  • [30, 60]

I'm not sure if devices do this (could you confirm by logging fps ranges in the native kotlin code for CameraDevice?), but if it does, what would the difference between [8, 30] and [12, 30] be? Should I choose 8 or 12 for min FPS if the user wants 30 FPS?

@Dingenis
Copy link
Author

Dingenis commented Jul 11, 2024

Sure thing @mrousavy, here you go!

2024-07-11 14:03:25.544  1257-1583  BRAM                    com.mrousavy.camera.example          I  --- START CAMERA: 0 ---
2024-07-11 14:03:25.544  1257-1583  BRAM                    com.mrousavy.camera.example          I  [20, 20]
2024-07-11 14:03:25.544  1257-1583  BRAM                    com.mrousavy.camera.example          I  [24, 24]
2024-07-11 14:03:25.545  1257-1583  BRAM                    com.mrousavy.camera.example          I  [30, 30]
2024-07-11 14:03:25.545  1257-1583  BRAM                    com.mrousavy.camera.example          I  [15, 20]
2024-07-11 14:03:25.545  1257-1583  BRAM                    com.mrousavy.camera.example          I  [10, 30]
2024-07-11 14:03:25.545  1257-1583  BRAM                    com.mrousavy.camera.example          I  [8, 30]
2024-07-11 14:03:25.545  1257-1583  BRAM                    com.mrousavy.camera.example          I  [15, 30]
2024-07-11 14:03:25.545  1257-1583  BRAM                    com.mrousavy.camera.example          I  [15, 15]
2024-07-11 14:03:25.545  1257-1583  BRAM                    com.mrousavy.camera.example          I  --- END CAMERA: 0 ---
2024-07-11 14:03:25.585  1257-1583  BRAM                    com.mrousavy.camera.example          I  --- START CAMERA: 1 ---
2024-07-11 14:03:25.585  1257-1583  BRAM                    com.mrousavy.camera.example          I  [20, 20]
2024-07-11 14:03:25.585  1257-1583  BRAM                    com.mrousavy.camera.example          I  [24, 24]
2024-07-11 14:03:25.585  1257-1583  BRAM                    com.mrousavy.camera.example          I  [30, 30]
2024-07-11 14:03:25.585  1257-1583  BRAM                    com.mrousavy.camera.example          I  [15, 20]
2024-07-11 14:03:25.585  1257-1583  BRAM                    com.mrousavy.camera.example          I  [10, 30]
2024-07-11 14:03:25.585  1257-1583  BRAM                    com.mrousavy.camera.example          I  [8, 30]
2024-07-11 14:03:25.585  1257-1583  BRAM                    com.mrousavy.camera.example          I  [15, 30]
2024-07-11 14:03:25.585  1257-1583  BRAM                    com.mrousavy.camera.example          I  [15, 15]
2024-07-11 14:03:25.585  1257-1583  BRAM                    com.mrousavy.camera.example          I  --- END CAMERA: 1 ---
2024-07-11 14:03:25.617  1257-1583  BRAM                    com.mrousavy.camera.example          I  --- START CAMERA: 2 ---
2024-07-11 14:03:25.617  1257-1583  BRAM                    com.mrousavy.camera.example          I  [20, 20]
2024-07-11 14:03:25.617  1257-1583  BRAM                    com.mrousavy.camera.example          I  [24, 24]
2024-07-11 14:03:25.617  1257-1583  BRAM                    com.mrousavy.camera.example          I  [30, 30]
2024-07-11 14:03:25.617  1257-1583  BRAM                    com.mrousavy.camera.example          I  [15, 20]
2024-07-11 14:03:25.617  1257-1583  BRAM                    com.mrousavy.camera.example          I  [10, 30]
2024-07-11 14:03:25.617  1257-1583  BRAM                    com.mrousavy.camera.example          I  [8, 30]
2024-07-11 14:03:25.617  1257-1583  BRAM                    com.mrousavy.camera.example          I  [15, 30]
2024-07-11 14:03:25.617  1257-1583  BRAM                    com.mrousavy.camera.example          I  [15, 15]
2024-07-11 14:03:25.617  1257-1583  BRAM                    com.mrousavy.camera.example          I  --- END CAMERA: 2 ---
2024-07-11 14:03:25.649  1257-1583  BRAM                    com.mrousavy.camera.example          I  --- START CAMERA: 3 ---
2024-07-11 14:03:25.649  1257-1583  BRAM                    com.mrousavy.camera.example          I  [20, 20]
2024-07-11 14:03:25.649  1257-1583  BRAM                    com.mrousavy.camera.example          I  [24, 24]
2024-07-11 14:03:25.650  1257-1583  BRAM                    com.mrousavy.camera.example          I  [30, 30]
2024-07-11 14:03:25.650  1257-1583  BRAM                    com.mrousavy.camera.example          I  [15, 20]
2024-07-11 14:03:25.650  1257-1583  BRAM                    com.mrousavy.camera.example          I  [10, 30]
2024-07-11 14:03:25.650  1257-1583  BRAM                    com.mrousavy.camera.example          I  [8, 30]
2024-07-11 14:03:25.650  1257-1583  BRAM                    com.mrousavy.camera.example          I  [15, 30]
2024-07-11 14:03:25.650  1257-1583  BRAM                    com.mrousavy.camera.example          I  [15, 15]
2024-07-11 14:03:25.650  1257-1583  BRAM                    com.mrousavy.camera.example          I  --- END CAMERA: 3 ---

From what I can find, is that on Android the camera itself manages it's frame rate internally, so using setTargetFrameRate only gives the camera a target range which acts kinda like a filter (if possible). Outside that it will choose it's own frame rate depending on light conditions and other things, so I think in the case you are describing I would do setTargetFrameRate to [8,30]. Since all formats of the device would qualify for that range, so the camera can choose which one to pick based on other things like quality. Which actually might be better for taking pictures, for example of darker scenes because then the device has the freedom to increase the exposure time or other settings to better adjust.

Say the user really wants 30 fps for like video capture, then I think this is a argument for changing the fps camera prop to a range, if the user really wants 30 fps then they can set a range with that as a min accepting the possibility that the resolution might be lower because of that. Otherwise, the user could set the fps prop to [format.minFps, format.maxFps] giving the camera control over the fps so that it can chose what is best for the other settings like mentioned before.

It would be a lot nicer if each format would have a accurate minFps and maxFps so that one could reliably filter on that and always set the fps range to that, however like you mentioned there is no API on Android to get which combination of settings and outputs are supported so I don't think that can be made.

@mrousavy
Copy link
Owner

Awesome thank you so much. Created a PR to use minFps from format: #3073

@mrousavy
Copy link
Owner

I now wonder if this is a good idea to always have a low minFps. So now with this change, all existing apps will throttle their frame rates in dark lighting conditions.

@mrousavy
Copy link
Owner

So I have two ideas:

  1. Have fps={30} as a prop like now, and then a separate prop like fpsMode="variable" | "fixed", which allows throttling to minFps in variable, and always keeps it constant at fixed.
  2. Have minFps={format.minFps} and maxFps={format.maxFps} as props, and then fps as a prop which just sets both min and max fps props on the JS layer as a wrapper. This is non-breaking, but you can opt into the new API choosing min and max FPS to your likings (probably the one from format)

@mrousavy
Copy link
Owner

With option 2 you have full control, with option 1 you cannot select the min FPS - it will always be the minimum from the format. Which might not be a good idea since we don't always want to drop as low as 8, sometimes we just want a smaller range like [20, 30].

I mean yea we can also select a format with that range tbh. But maybe on some devices there are no multiple FPS ranges, only one that says like [1, 30]...

@mrousavy
Copy link
Owner

See #3074

@Dingenis
Copy link
Author

Dingenis commented Jul 12, 2024

Good ideas @mrousavy! 🔥
I think option two is the best option too, giving full control to the user thus enabling them to make the decision and that it is non-breaking is a nice bonus! Looking at the PR, I think your latest changes where fps prop can be a number or a tuple is a nice implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants