Closed grevtsovna closed 2 months ago
We've also noticed this when running on Android emulator. On the emulator the preview is stretched and also the co-ordinates (when using the barcode scanner) is off in x. Only have one real Android device and there the preview is not stretched.
Same here. I have a stretched and zoomed preview on Android that makes it unusable.
"react-native-vision-camera": "3.9.0",
"expo": "~50.0.6",
const device = useCameraDevice("back");
const format = useCameraFormat(device, [{ photoResolution: "max" }]);
const focus = useCallback((point) => {
const c = cameraRef.current;
if (c == null) return;
c.focus(point);
}, []);
const gesture = Gesture.Tap().onEnd(({ x, y }) => {
runOnJS(setFocusPoint)({ x, y });
runOnJS(focus)({ x, y });
});
const { props: cameraProps } = useBarcodeScanner({
barcodeTypes: ["ean-13", "ean-8"],
onBarcodeScanned: (barcodes) => {
"worklet";
updateBarcodesStateWorklet(barcodes);
},
});
<GestureDetector gesture={gesture}>
<Camera
ref={cameraRef}
photo={true}
style={StyleSheet.absoluteFill}
device={device}
isActive={cameraIsActive}
format={format}
fps={30}
{... cameraProps }
/>
</GestureDetector>
On Android:
On iOS
My situation is that there is no stretching in the simulator, but it will stretch in the actual machine (landscape mode).
I'm experiencing the same issue. I upgraded to version 3.9.0 hoping to address this problem: https://github.com/mrousavy/react-native-vision-camera/pull/2519
Previously, the stretching was vertical, but now it is horizontal.
PreviewView.kt
that would of course be a huge help.
- This is weird, on all my test devices (Samsung, Huawei, Google Pixel) the preview looks good. I can take another look but if the issue cannot be reproduced on my devices then I don't know what to do. If anyone of you guys wants to play around with the code in
PreviewView.kt
that would of course be a huge help.- As mentioned in the first pinned issue here in this repo, orientation is not yet supported. It's quite a large effort to implement it, so I'm crowdfunding the effort. ✨ Implement Orientation ($8,000) #1891
Sorry, I would like to help more with Kotlin, but I don't have any idea.
I've tried changing the resizeMode prop from 'cover' to 'contain,' and this is the result (picture). I don't know why I have these measurements (the container has 600 height x 300 width). Are these the default preview measurements? Anyway, the preview still shows a stretched capture.
Samsung Galaxy J6+
EDIT: Ok, my sensorOrientation is always 'landscape-left' and I can't change it...
So @mrousavy this is not supported in this release?
Hello. I've looked into the PreviewView.kt
.
One thing I do not understand is why the CameraDeviceDetails.getMaximumPreviewSize()
is used for the aspect ratio in getSize
.
Using the contentAspectRatio
instead of containerAspectRatio
fixes the stretching issue on my galaxy S22, but I might be missing something and might not work on all phones, please let me know what you think Marc
@bruno-centanaro can you submit a PR? I can take a look and others can test this.
One thing I do not understand is why the
CameraDeviceDetails.getMaximumPreviewSize()
is used for the aspect ratio ingetSize
.
This is just the default value. It will be updated later by the surfaceChanged
event where the actual Surface
size gets assigned to PreviewView.size
, causing onMeasure
to re-calculate the aspect ratio (at least that's the plan).
Sorry, I messed up, as for my first message, it was containerAspectRatio instead of contentAspectRatio and not vice-versa. Nevertheless now that i get the intended use I think that's correct As for the surfaceChanged, you are correct it modifies the size variable but this is not causing the onMeasure to run again, at least on my phone. The PR I can try to do it after work today, I'll link it here
As for the surfaceChanged, you are correct it modifies the size variable but this is not causing the onMeasure to run again, at least on my phone.
Interesting - I think this PR fixes that: https://github.com/mrousavy/react-native-vision-camera/pull/2588
I believe that the aspect ratio calculation is correct, are you sure that swapping the aspect ratios is right? Can you just quickly post your diff or what exactly you changed so I can take a look now?
Otherwise a PR after work is also much appreciated. Before and after screenshots are all I need
That PR does cause the onMeasure to re-run, but it is strange because the preview is still stretched. This are the logs without my aspect ratio change
2024-02-19 10:19:08.709 26373-26373 PreviewView BUNDLE_ID I Creating PreviewView... 2024-02-19 10:19:08.710 26373-26373 PreviewView BUNDLE_ID I PreviewView is 0x0, rendering 1080x1920 content (LANDSCAPE_LEFT). Resizing to: 0x0 (COVER) 2024-02-19 10:19:08.878 26373-26373 SurfaceView@8057685 BUNDLE_ID I onWindowVisibilityChanged(0) true com.mrousavy.camera.core.PreviewView{8057685 V.E...... ......I. 0,0-0,0} of ViewRootImpl@a077172[MainActivity] 2024-02-19 10:19:08.883 26373-26373 PreviewView BUNDLE_ID I PreviewView is 1080x1440, rendering 1080x1920 content (LANDSCAPE_LEFT). Resizing to: 1080x1920 (COVER) 2024-02-19 10:19:08.931 26373-26373 SurfaceView@8057685 BUNDLE_ID I surfaceCreated 2 #8 com.mrousavy.camera.core.PreviewView{8057685 V.E...... ......ID 0,-240-1080,1680} 2024-02-19 10:19:08.931 26373-26373 CameraSession BUNDLE_ID I PreviewView Surface created! Surface(name=null)/@0xf56b2aa 2024-02-19 10:19:08.932 26373-26373 SurfaceView@8057685 BUNDLE_ID I surfaceChanged (1920,1080) 2 #8 com.mrousavy.camera.core.PreviewView{8057685 V.E...... ......ID 0,-240-1080,1680} 2024-02-19 10:19:08.932 26373-26373 PreviewView BUNDLE_ID I Surface changed: 1920 x 1080 2024-02-19 10:19:08.932 26373-26373 CameraSession BUNDLE_ID I PreviewView Surface updated! Surface(name=null)/@0xf56b2aa 1920 x 1080 2024-02-19 10:19:09.101 26373-26373 PreviewView BUNDLE_ID I Input Orientation changed: LANDSCAPE_LEFT -> LANDSCAPE_RIGHT 2024-02-19 10:19:09.120 26373-26373 PreviewView BUNDLE_ID I PreviewView is 1080x1920, rendering 1080x1920 content (LANDSCAPE_RIGHT). Resizing to: 1080x1920 (COVER) 2024-02-19 10:19:09.311 26373-26373 PreviewView BUNDLE_ID I PreviewView is 1080x1920, rendering 1080x1920 content (LANDSCAPE_RIGHT). Resizing to: 1080x1920 (COVER) 2024-02-19 10:19:09.311 26373-26373 BLASTBufferQueue_Java BUNDLE_ID I update, w= 640 h= 480 mName = null mNativeObject= 0xb400007d19803210 sc.mNativeObject= 0xb400007c4962f9d0 format= 4 caller= android.view.SurfaceView.setBufferSize:1432 android.view.SurfaceView.performSurfaceTransaction:988 android.view.SurfaceView.updateSurface:1204 android.view.SurfaceView.setFrame:559 android.view.View.layout:25731 com.mrousavy.camera.core.PreviewView.requestLayout$lambda$1:100 2024-02-19 10:19:09.313 26373-26373 SurfaceView@8057685 BUNDLE_ID I surfaceChanged (640,480) 3 #5 com.mrousavy.camera.core.PreviewView{8057685 V.E...... ......I. 0,-240-1080,1680} 2024-02-19 10:19:09.313 26373-26373 PreviewView BUNDLE_ID I Surface changed: 640 x 480 2024-02-19 10:19:09.313 26373-26373 PreviewView BUNDLE_ID I Surface Size changed: 1920x1080 -> 640x480 2024-02-19 10:19:09.313 26373-26373 CameraSession BUNDLE_ID I PreviewView Surface updated! Surface(name=null)/@0xf56b2aa 640 x 480 2024-02-19 10:19:09.324 26373-26373 PreviewView BUNDLE_ID I PreviewView is 1080x1920, rendering 480x640 content (LANDSCAPE_RIGHT). Resizing to: 1440x1920 (COVER)
I'm not sure if swapping the aspect ratio's is the correct way to do it, it works on my phone. This is the code
private fun getSize(contentSize: Size, containerSize: Size, resizeMode: ResizeMode): Size {
val contentAspectRatio = contentSize.width.toDouble() / contentSize.height
val containerAspectRatio = containerSize.width.toDouble() / containerSize.height
val widthOverHeight = when (resizeMode) {
ResizeMode.COVER -> contentAspectRatio > containerAspectRatio
ResizeMode.CONTAIN -> contentAspectRatio < containerAspectRatio
}
Log.i(TAG, "Content Aspect Ratio: $contentAspectRatio, Container Aspect Ratio: $containerAspectRatio widthOverHeight: $widthOverHeight ($resizeMode)")
return if (widthOverHeight) {
// Scale by width to cover height
val scaledWidth = containerSize.height * containerAspectRatio
Size(scaledWidth.roundToInt(), containerSize.height)
} else {
val scaledHeight = if (containerAspectRatio.isNaN()) containerSize.width / contentAspectRatio else containerSize.width / containerAspectRatio
Log.i(TAG, "Scaled Height: $scaledHeight, containerSize: $containerSize, contentAspectRatio: $contentAspectRatio")
Size(containerSize.width, scaledHeight.roundToInt())
}
}
Uhm well this code:
val scaledWidth = containerSize.height * contentAspectRatio
val scaledWidth = containerSize.height * containerAspectRatio
Size(scaledWidth.roundToInt(), containerSize.height)
Effectively does nothing, it's the same as
containerSize
(1080 / 1920 * 1080 = 1920, so both width and height always stay the same values as before)
And then the second part:
val scaledHeight = if (containerAspectRatio.isNaN()) containerSize.width / contentAspectRatio else containerSize.width / containerAspectRatio
containerAspectRatio
could be NaN on the first render, true, in that case I think an early return makes more sense though.
Yes, to be honest the widthOverHeight I did not test it as its false for me
I tried this PR https://github.com/mrousavy/react-native-vision-camera/pull/2588, but the preview is still stretched.
For me, we were using two aspect ratio, 4:3 and 16:9, 16:9 doesn't show any stretching issue, but I do notice issue in case of 4:3. Tested on oneplus Nord, back camera
there is update for this, i am still get this issue, sometime when i reload after save, the preview is normally, when go back to previous page and open camera again, this happen again
this is normal preview
@bruno-centanaro @mrousavy
I have the same issue with landscape stretching, and this is what helped me (since 3.7.1). The issue still persists on 3.9.0.
diff --git a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt b/node_modules/react- native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt index ba3c94b..f2b140b 100644
--- a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt
+++ b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt
@@ -105,7 +105,7 @@ class PreviewView(context: Context, callback: SurfaceHolder.Callback) :
Size(scaledWidth.roundToInt(), containerSize.height)
} else {
// Scale by height to cover width
- val scaledHeight = containerSize.width / contentAspectRatio
+ val scaledHeight = containerSize.width * contentAspectRatio
Size(containerSize.width, scaledHeight.roundToInt())
}
}
I think division operator here is not correct, it should be multiplication.
The operators there are correct I think. For anyone who has issues and is sure the aspect ratio of the camera container is the same as the resolution's one this works for me
index ba3c94b..cc19356 100644
--- a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt
+++ b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt
@@ -94,6 +94,10 @@ class PreviewView(context: Context, callback: SurfaceHolder.Callback) :
val contentAspectRatio = contentSize.width.toDouble() / contentSize.height
val containerAspectRatio = containerSize.width.toDouble() / containerSize.height
+ if (!(contentAspectRatio > 0 && containerAspectRatio > 0)) {
+ return contentSize
+ }
+
val widthOverHeight = when (resizeMode) {
ResizeMode.COVER -> contentAspectRatio > containerAspectRatio
ResizeMode.CONTAIN -> contentAspectRatio < containerAspectRatio
@@ -101,11 +105,11 @@ class PreviewView(context: Context, callback: SurfaceHolder.Callback) :
return if (widthOverHeight) {
// Scale by width to cover height
- val scaledWidth = containerSize.height * contentAspectRatio
+ val scaledWidth = containerSize.height * containerAspectRatio
Size(scaledWidth.roundToInt(), containerSize.height)
} else {
// Scale by height to cover width
- val scaledHeight = containerSize.width / contentAspectRatio
+ val scaledHeight = containerSize.width / containerAspectRatio
Size(containerSize.width, scaledHeight.roundToInt())
}
}
Please make sure the aspect ratio's are the same otherwise I don't think that will work
The operators there are correct I think. For anyone who has issues and is sure the aspect ratio of the camera container is the same as the resolution's one this works for me
index ba3c94b..cc19356 100644 --- a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt +++ b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt @@ -94,6 +94,10 @@ class PreviewView(context: Context, callback: SurfaceHolder.Callback) : val contentAspectRatio = contentSize.width.toDouble() / contentSize.height val containerAspectRatio = containerSize.width.toDouble() / containerSize.height + if (!(contentAspectRatio > 0 && containerAspectRatio > 0)) { + return contentSize + } + val widthOverHeight = when (resizeMode) { ResizeMode.COVER -> contentAspectRatio > containerAspectRatio ResizeMode.CONTAIN -> contentAspectRatio < containerAspectRatio @@ -101,11 +105,11 @@ class PreviewView(context: Context, callback: SurfaceHolder.Callback) : return if (widthOverHeight) { // Scale by width to cover height - val scaledWidth = containerSize.height * contentAspectRatio + val scaledWidth = containerSize.height * containerAspectRatio Size(scaledWidth.roundToInt(), containerSize.height) } else { // Scale by height to cover width - val scaledHeight = containerSize.width / contentAspectRatio + val scaledHeight = containerSize.width / containerAspectRatio Size(containerSize.width, scaledHeight.roundToInt()) } }
Please make sure the aspect ratio's are the same otherwise I don't think that will work
Tested this patch, this resolves the issue we were having with 4:3 aspect ratio.
The operators there are correct I think. For anyone who has issues and is sure the aspect ratio of the camera container is the same as the resolution's one this works for me
index ba3c94b..cc19356 100644 --- a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt +++ b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt @@ -94,6 +94,10 @@ class PreviewView(context: Context, callback: SurfaceHolder.Callback) : val contentAspectRatio = contentSize.width.toDouble() / contentSize.height val containerAspectRatio = containerSize.width.toDouble() / containerSize.height + if (!(contentAspectRatio > 0 && containerAspectRatio > 0)) { + return contentSize + } + val widthOverHeight = when (resizeMode) { ResizeMode.COVER -> contentAspectRatio > containerAspectRatio ResizeMode.CONTAIN -> contentAspectRatio < containerAspectRatio @@ -101,11 +105,11 @@ class PreviewView(context: Context, callback: SurfaceHolder.Callback) : return if (widthOverHeight) { // Scale by width to cover height - val scaledWidth = containerSize.height * contentAspectRatio + val scaledWidth = containerSize.height * containerAspectRatio Size(scaledWidth.roundToInt(), containerSize.height) } else { // Scale by height to cover width - val scaledHeight = containerSize.width / contentAspectRatio + val scaledHeight = containerSize.width / containerAspectRatio Size(containerSize.width, scaledHeight.roundToInt()) } }
Please make sure the aspect ratio's are the same otherwise I don't think that will work
Tried this on a Samsung Galaxy (A04e) but the preview remains stretched and zoomed.
Samsung Galaxy (A04e)
iPhone 14 Pro
It seems as your container aspect ratio might not be the same as the video preview (set on the format prop)
It seems as your container aspect ratio might not be the same as the video preview (set on the format prop)
I believe it is the same. Here is how I set it up:
const device = useCameraDevice("back");
const screen = Dimensions.get("screen");
const format = useCameraFormat(device, [
{ targetPhotoAspectRatio: screen.height / screen.width },
{ autoFocusSystem: "phase-detection" },
{ photoResolution: "max" },
{ fps: 30 },
]);
<Camera
ref={cameraRef}
photo={true}
style={StyleSheet.absoluteFill}
device={device}
isActive={cameraIsActive}
format={format}
/>
The aspect ratio of the preview is the same as video, not the picture one, also the fact that you use an aspect ratio in the format does not necessarily mean it is supported by the camera, maybe try console logging the format to check
containerAspectRatio
This seems to have sorted the issue for me, was occurring on a Fairphone 4 when my camera preview was not the full size of my screen
The aspect ratio of the preview is the same as video, not the picture one, also the fact that you use an aspect ratio in the format does not necessarily mean it is supported by the camera, maybe try console logging the format to check
Here is what I've tried:
const screen = Dimensions.get("screen");
const device = useCameraDevice("back");
const format = useCameraFormat(device, [
{ photoWidth: screen.width },
{ photoHeight: screen.height },
{ videoHeight: screen.height },
{ videoWidth: screen.width },
{ autoFocusSystem: "phase-detection" },
{ photoResolution: "max" },
{ fps: 30 },
]);
Is this the correct/ideal way to set the format wrt to width and height?
Logging the format to the console :
{
autoFocusSystem: "contrast-detection",
fieldOfView: 81.19526233755766,
maxFps: 30,
maxISO: 1600,
maxZoom: 10,
minFps: 1,
minISO: 100,
photoHeight: 3120,
photoWidth: 4160,
pixelFormats: ["yuv", "native"],
supportsDepthCapture: false,
supportsPhotoHdr: false,
supportsVideoHdr: false,
videoHeight: 1440,
videoStabilizationModes: ["off", "standard", "off"],
videoWidth: 1920,
}
No idea why the photo and video dimensions are not the same.
When I manually reset the width or height on the camera once it's mounted, the preview renders correctly:
<Camera
ref={cameraRef}
photo={true}
style={StyleSheet.absoluteFill} // <-- commenting this out to manually reset
// style={{ width: screen.width, height: screen.height }} <-- uncommenting this to manually reset
device={device}
isActive={cameraIsActive}
format={format}
/>
But it never renders correctly on the initial mount. Tried it with @swamywiz's batch and without (same behaviour).
Hi @mrousavy
I have an issue where the preview image is slightly more zoomed in than the captured image, in one axis only. For instance if I am taking a portrait image, the preview is , lets say, 20% less field of view in the horizontal axis than the resultant image. And if I rotate to landscape, then the field of view of the preview is less in the vertical axis.
This seems consistent across versions that I have tried from 3.6.4 -> 3.8.2
The mode is set to 'contain' and I have tried various styles such as absoluteFill, and an absolute top, left, width, height to ensure there is a large margin around the preview area to ensure the view is not running off the edge of the screen somehow
This appears to be device specific. Some android devices have the issue, but my iphone SE does not
Is there a trick I am missing?
@stevenmathers I have the same issue but it happens in both axes. Tried on 3 android devices (2 Xiaomi, 1 Pixel), this does not happen on any iOS device.
@jhau are you sure its both axis? thats weird if so
update, this appears to be android only, and I really think its only in one axis - horizontal, regardless of what orientation you are holding the device. And it does not affect the android emulator, presuambly some implemntation issue of the emulator as opposed to a physical camera. the 1st image of the pair shows the preview window, and the second is the actual image returned from the camera. NOTE: as I said, the image is not being drawn off the screen. even if I reduce the window size so that it is less than the screen dimenstions, the same thing occurs
Well, after conducting multiple tests, this is the trick I propose:
Play with re-renders. I mean, if you set a negligible size initially for the camera component, and after 1 second, you update it to the full size of the container, it seems that the re-render corrects the issue.
const [sizeWithDelay, setSizeWithDelay] = useState<ViewStyle>({
width: 1,
height: 1,
});
useLayoutEffect(() => {
!photo &&
setTimeout(() => {
setSizeWithDelay({
width: '100%',
height: '100%',
});
}, 1000);
return () => {
photo &&
setSizeWithDelay({
width: 1,
height: 1,
});
};
}, [photo]);
And in the component:
<Camera
ref={camera}
photo={true}
style={[
styles.camera,
{ height: sizeWithDelay.height, width: sizeWithDelay.width },
]}
.../>
Note that if you leave the sizes at 0, it doesn't work well, which is why I had to set it to 1.
What I do is, if there's no photo, I use the preview and give it a 100% width and height, but it reverts to 1 if we already have the photo.
Depending on the delay set by the developer, it might be better to include some type of activity indicator to enhance the user experience or not.
I'm sure there's a more correct way to implement it, and I'm open to improvements until the error is fixed. I hope it also helps identify why the problem might be occurring.
Samsung Galaxy J6+:
the sizeWithDelay helps me for stretch issue on redmi but I had also to modify the native code to have a correct preview (on samsung device)
see patch below
diff --git a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/CameraDeviceDetails.kt b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/CameraDeviceDetails.kt
index a589c26..91f5b8c 100644
--- a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/CameraDeviceDetails.kt
+++ b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/CameraDeviceDetails.kt
@@ -47,6 +47,13 @@ class CameraDeviceDetails(private val cameraManager: CameraManager, val cameraId
return if (isHighResScreen) display1080p else displaySize
}
+
+ fun getActualPreviewSize(): Size {
+ return Size(
+ Resources.getSystem().displayMetrics.widthPixels,
+ Resources.getSystem().displayMetrics.heightPixels
+ )
+ }
}
val characteristics by lazy { cameraManager.getCameraCharacteristics(cameraId) }
diff --git a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt
index ba3c94b..c2b7335 100644
--- a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt
+++ b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt
@@ -20,7 +20,7 @@ import kotlinx.coroutines.withContext
class PreviewView(context: Context, callback: SurfaceHolder.Callback) :
SurfaceView(context),
SurfaceHolder.Callback {
- var size: Size = CameraDeviceDetails.getMaximumPreviewSize()
+ var size: Size = CameraDeviceDetails.getActualPreviewSize()
set(value) {
if (field != value) {
Log.i(TAG, "Surface Size changed: $field -> $value")
@@ -92,11 +92,11 @@ class PreviewView(context: Context, callback: SurfaceHolder.Callback) :
private fun getSize(contentSize: Size, containerSize: Size, resizeMode: ResizeMode): Size {
val contentAspectRatio = contentSize.width.toDouble() / contentSize.height
- val containerAspectRatio = containerSize.width.toDouble() / containerSize.height
+ val containerAspectRatio = if(containerSize.height > 0) containerSize.width.toDouble() / containerSize.height else 0
val widthOverHeight = when (resizeMode) {
- ResizeMode.COVER -> contentAspectRatio > containerAspectRatio
- ResizeMode.CONTAIN -> contentAspectRatio < containerAspectRatio
+ ResizeMode.COVER -> contentAspectRatio > containerAspectRatio.toDouble()
+ ResizeMode.CONTAIN -> contentAspectRatio < containerAspectRatio.toDouble()
}
return if (widthOverHeight) {
@mrousavy Facing the same issue preview stretching with the following version details:
"react": "18.2.0",
"react-native": "0.73.5",
"react-native-reanimated": "^3.6.1",
"react-native-vision-camera": "^3.9.0",
the sizeWithDelay helps me for stretch issue on redmi but I had also to modify the native code to have a correct preview (on samsung device)
see patch below
diff --git a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/CameraDeviceDetails.kt b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/CameraDeviceDetails.kt index a589c26..91f5b8c 100644 --- a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/CameraDeviceDetails.kt +++ b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/CameraDeviceDetails.kt @@ -47,6 +47,13 @@ class CameraDeviceDetails(private val cameraManager: CameraManager, val cameraId return if (isHighResScreen) display1080p else displaySize } + + fun getActualPreviewSize(): Size { + return Size( + Resources.getSystem().displayMetrics.widthPixels, + Resources.getSystem().displayMetrics.heightPixels + ) + } } val characteristics by lazy { cameraManager.getCameraCharacteristics(cameraId) } diff --git a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt index ba3c94b..c2b7335 100644 --- a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt +++ b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt @@ -20,7 +20,7 @@ import kotlinx.coroutines.withContext class PreviewView(context: Context, callback: SurfaceHolder.Callback) : SurfaceView(context), SurfaceHolder.Callback { - var size: Size = CameraDeviceDetails.getMaximumPreviewSize() + var size: Size = CameraDeviceDetails.getActualPreviewSize() set(value) { if (field != value) { Log.i(TAG, "Surface Size changed: $field -> $value") @@ -92,11 +92,11 @@ class PreviewView(context: Context, callback: SurfaceHolder.Callback) : private fun getSize(contentSize: Size, containerSize: Size, resizeMode: ResizeMode): Size { val contentAspectRatio = contentSize.width.toDouble() / contentSize.height - val containerAspectRatio = containerSize.width.toDouble() / containerSize.height + val containerAspectRatio = if(containerSize.height > 0) containerSize.width.toDouble() / containerSize.height else 0 val widthOverHeight = when (resizeMode) { - ResizeMode.COVER -> contentAspectRatio > containerAspectRatio - ResizeMode.CONTAIN -> contentAspectRatio < containerAspectRatio + ResizeMode.COVER -> contentAspectRatio > containerAspectRatio.toDouble() + ResizeMode.CONTAIN -> contentAspectRatio < containerAspectRatio.toDouble() } return if (widthOverHeight) {
I am using 3.8.2 because 3.9 seems buggy in different ways (timing out trying to take photos on android device) I refactored this patch for 3.8.2 and it doesnt work, but some weird new behaviour occurs -> for a moment, the preview looks like it is the proper width (in terms of field of view) but possibly it is stretched (wrong aspect ratio?), then it snaps to the same aspect ratio/field of view which is the same as my original issue -> the field of view of the preview is less horizontally than the captured image. any thoughts?
You're right, I thought it was fixing my issue (did you set photoAspectRatio in useFormat hook too) but it was worse in fact
Getting a similar issue to whats being described above on a moto e22i. I tried the patch in https://github.com/mrousavy/react-native-vision-camera/pull/2588 which didnt resolve the issue. The preview is stretched but only in landscape, portrait seems fine. Replicatable on the latest ShadowLense app version. Actual image capture is fine. In 3.8.2 this isn't an issue. Not sure if related to #1891 given it seems fine in previous versions.
Same issue persists
On 3.9.1 still have the issue on some android devices, tried the hack with adjusting the style using the onInitialized hook and that is a usable work around.
logs below from when using the hack.
16:52:55.788 I Creating PreviewView...
16:52:55.788 I PreviewView is 0x0, rendering 1415x720 content (LANDSCAPE_LEFT). Resizing to: 1415x720 (COVER)
16:52:55.790 I Updating CameraSession...
16:52:55.791 I configure { ... }: Waiting for lock...
16:52:55.792 I configure { ... }: Updating CameraSession Configuration... Difference(deviceChanged=true, outputsChanged=true, sidePropsChanged=true, isActiveChanged=true)
16:52:55.792 I Configuring inputs for CameraSession...
16:52:55.792 D --> setInput(0)
16:52:55.792 I Destroying previous outputs...
16:52:55.793 I Creating outputs for Camera #0...
16:52:55.797 I Adding 4032x1908 Photo Output in JPEG...
16:52:55.799 I Adding 1920x912 Video Output in YUV_420_888...
16:52:55.799 I Initializing 1920 x 912 Video Pipeline (format: YUV)
16:52:55.799 I Using ImageReader round-trip (format: #35)
16:52:55.799 I Creating ImageReader with default usage flags...
16:52:55.799 I Creating ImageWriter with format #35...
16:52:55.800 D --> setOutputs([PHOTO (4032x1908 in JPEG), VIDEO (1920x912 in YUV)])
16:52:55.800 I Successfully configured Session with 2 outputs for Camera #0!
16:52:55.800 I Updating Video Outputs...
16:52:55.800 I Removing RecordingSession Output...
16:52:55.800 D --> setRepeatingRequest(...)
16:52:55.800 D --> setIsActive(false)
16:52:55.800 D Configure() with isActive: false, ID: 0, device: null, session: null
16:52:55.800 I Creating new device...
16:52:55.800 I Camera #0: Opening...
16:52:55.812 I postSingleUpdate device: camera id 0 status STATUS_NOT_AVAILABLE
16:52:55.813 I Camera 0 facing CAMERA_FACING_BACK state now CAMERA_STATE_OPEN for client se.fnx.powerup.acce API Level 2
16:52:55.813 I Camera #0 is now unavailable.
16:52:55.826 I Camera #0: Opened!
16:52:55.826 I Updating CameraSession...
16:52:55.826 I Creating new session...
16:52:55.827 I onWindowVisibilityChanged(0) true com.mrousavy.camera.core.PreviewView{f168b99 V.E...... ......ID -707,-360-708,360} of ViewRootImpl@543e7a3[MainActivity]
16:52:55.827 I Camera #0: Creating Capture Session #35... (Hardware Level: 1 | Outputs: [PHOTO (4032x1908 in JPEG), VIDEO (1920x912 in YUV)])
16:52:55.828 I Using new API (>=28)
16:52:55.833 I PreviewView is 720x1490, rendering 1415x720 content (LANDSCAPE_LEFT). Resizing to: 2928x1490 (COVER)
16:52:55.844 I Relayout returned: old=(0,0,720,1520) new=(0,0,720,1520) req=(720,1520)0 dur=8 res=0x1 s={true 479177400320} ch=false
16:52:55.855 I surfaceCreated 2 #8 com.mrousavy.camera.core.PreviewView{f168b99 V.E...... ......ID -1104,0-1824,1490}
16:52:55.855 I PreviewView Surface created! Surface(name=null)/@0x8643be
16:52:55.855 I Setting Preview Output...
16:52:55.855 I surfaceChanged (720,1415) 2 #8 com.mrousavy.camera.core.PreviewView{f168b99 V.E...... ......ID -1104,0-1824,1490}
16:52:55.855 I PreviewView Surface updated! Surface(name=null)/@0x8643be 720 x 1415
16:52:55.860 I doUpdatePositionAsync is called and callVoidMethod
16:52:55.892 I doUpdatePositionAsync is called and callVoidMethod
16:52:55.924 D Sending lifecycle 2 to service
16:52:55.925 D Activity backgrounding at 12606912
16:52:55.930 D unregisterListener ::
16:52:55.937 W A resource failed to call release.
16:52:55.938 I configure { ... }: Waiting for lock...
16:52:55.938 I Camera #0: Successfully created CameraCaptureSession #35!
16:52:55.938 I configure { ... }: Waiting for lock...
16:52:55.939 D Stopping repeating request...
16:52:55.939 D Configure() done! isActive: false, ID: 0, device: android.hardware.camera2.impl.CameraDeviceImpl@5851b0f, session: android.hardware.camera2.impl.CameraCaptureSessionImpl@485479c
16:52:55.939 I configure { ... }: Completed CameraSession Configuration! (isActive: false, isRunning: false)
16:52:55.939 I invokeOnInitialized()
16:52:55.939 D Finding view 7553...
16:52:55.940 D Found view 7553!
16:52:55.940 I configure { ... }: Updating CameraSession Configuration... Difference(deviceChanged=false, outputsChanged=false, sidePropsChanged=false, isActiveChanged=true)
16:52:55.940 D --> setIsActive(false)
16:52:55.940 D Configure() with isActive: false, ID: 0, device: android.hardware.camera2.impl.CameraDeviceImpl@5851b0f, session: android.hardware.camera2.impl.CameraCaptureSessionImpl@485479c
16:52:55.942 I doUpdatePositionAsync is called and callVoidMethod
16:52:55.942 D Stopping repeating request...
16:52:55.942 D Configure() done! isActive: false, ID: 0, device: android.hardware.camera2.impl.CameraDeviceImpl@5851b0f, session: android.hardware.camera2.impl.CameraCaptureSessionImpl@485479c
16:52:55.942 I configure { ... }: Completed CameraSession Configuration! (isActive: true, isRunning: false)
16:52:55.943 I configure { ... }: Updating CameraSession Configuration... Difference(deviceChanged=false, outputsChanged=true, sidePropsChanged=true, isActiveChanged=true)
16:52:55.943 I Destroying previous outputs...
16:52:55.943 I Closing 4032x1908 PHOTO ImageReader..
16:52:55.943 I Closing 1920x912 Video Pipeline..
16:52:55.947 I Creating outputs for Camera #0...
16:52:55.951 I Adding 4032x1908 Photo Output in JPEG...
16:52:55.953 I Adding 1920x912 Video Output in YUV_420_888...
16:52:55.953 I Initializing 1920 x 912 Video Pipeline (format: YUV)
16:52:55.954 I Using ImageReader round-trip (format: #35)
16:52:55.954 I Creating ImageReader with default usage flags...
16:52:55.954 I Creating ImageWriter with format #35...
16:52:55.954 I doUpdatePositionAsync is called and callVoidMethod
16:52:55.958 I Adding 1280x720 Preview Output...
16:52:55.959 I Resizing SurfaceHolder to 1280 x 720...
16:52:55.969 I surfaceChanged (1280,720) 3 #8 com.mrousavy.camera.core.PreviewView{f168b99 V.E...... ......I. -1104,0-1824,1490}
16:52:55.969 I Surface Size changed: 720x1415 -> 1280x720
16:52:55.969 I PreviewView Surface updated! Surface(name=null)/@0x8643be 1280 x 720
16:52:55.969 I Resized SurfaceHolder to 1280 x 720!
16:52:55.986 I doUpdatePositionAsync is called and callVoidMethod
16:52:55.987 I PreviewView is 2928x1490, rendering 720x1280 content (LANDSCAPE_LEFT). Resizing to: 2928x5205 (COVER)
16:52:55.991 D --> setOutputs([PHOTO (4032x1908 in JPEG), VIDEO (1920x912 in YUV), PREVIEW (1280 x 720)])
16:52:55.991 I Successfully configured Session with 3 outputs for Camera #0!
16:52:55.991 I Updating Video Outputs...
16:52:55.991 I Removing RecordingSession Output...
16:52:55.991 D --> setRepeatingRequest(...)
16:52:55.991 D --> setIsActive(true)
16:52:55.991 D Configure() with isActive: true, ID: 0, device: android.hardware.camera2.impl.CameraDeviceImpl@5851b0f, session: null
16:52:55.997 I Creating new session...
16:52:55.998 I doUpdatePositionAsync is called and callVoidMethod
16:52:55.998 I PreviewView is 2928x1490, rendering 720x1280 content (LANDSCAPE_LEFT). Resizing to: 2928x5205 (COVER)
16:52:55.999 I Camera #0: Creating Capture Session #36... (Hardware Level: 1 | Outputs: [PHOTO (4032x1908 in JPEG), VIDEO (1920x912 in YUV), PREVIEW (1280 x 720)])
16:52:56.002 I Using new API (>=28)
16:52:56.002 I Camera #0: CameraCaptureSession #35 has been closed.
16:52:56.002 I Session android.hardware.camera2.impl.CameraCaptureSessionImpl@485479c closed!
16:52:56.010 I doUpdatePositionAsync is called and callVoidMethod
16:52:56.010 I MSG_WINDOW_FOCUS_CHANGED 0 1
16:52:56.011 D prepareNavigationBarInfo() DecorView@374ecbe[MainActivity]
16:52:56.011 D getNavigationBarColor() -855310
16:52:56.020 I doUpdatePositionAsync is called and callVoidMethod
16:52:56.053 I doUpdatePositionAsync is called and callVoidMethod
16:52:56.054 I Updating CameraSession...
16:52:56.054 I PreviewView is 722x1490, rendering 720x1280 content (LANDSCAPE_LEFT). Resizing to: 838x1490 (COVER)
16:52:56.061 I Camera #0: Successfully created CameraCaptureSession #36!
16:52:56.061 I configure { ... }: Waiting for lock...
16:52:56.061 D Updating repeating request...
16:52:56.070 D Configure() done! isActive: true, ID: 0, device: android.hardware.camera2.impl.CameraDeviceImpl@5851b0f, session: android.hardware.camera2.impl.CameraCaptureSessionImpl@ba9745d
16:52:56.070 I doUpdatePositionAsync is called and callVoidMethod
16:52:56.071 I configure { ... }: Completed CameraSession Configuration! (isActive: true, isRunning: true)
16:52:56.071 I invokeOnStarted()
16:52:56.072 I Nothing changed, aborting configure { ... }
16:52:56.086 I doUpdatePositionAsync is called and callVoidMethod
16:52:56.107 I stopped(false) old=false
16:52:56.111 D Sending lifecycle 1 to service
16:52:56.111 D Activity foregrounding at 12607099.
16:52:56.116 D registerListener :: 1, LSM6DSO Acceleration Sensor, 66667, 0,
16:52:56.119 I MSG_WINDOW_FOCUS_CHANGED 1 1
16:52:56.119 D prepareNavigationBarInfo() DecorView@374ecbe[MainActivity]
16:52:56.119 D getNavigationBarColor() -855310
16:52:56.121 W Couldn't connect to "ws://localhost:8081/message?device=SM-G973F%20-%2010%20-%20API%2029&app=se.fnx.powerup.acce&clientid=BridgeDevSupportManager", will silently retry
16:52:56.124 I Relayout returned: old=(0,0,720,1520) new=(0,0,720,1520) req=(720,1520)0 dur=2 res=0x1 s={true 479177400320} ch=false
16:52:56.172 I Relayout returned: old=(0,0,720,1520) new=(0,0,720,1520) req=(720,1520)0 dur=3 res=0x1 s={true 479177400320} ch=false
16:52:56.372 I Camera 0 facing CAMERA_FACING_BACK state now CAMERA_STATE_ACTIVE for client se.fnx.powerup.acce API Level 2
16:52:56.391 I ImageReader::onImageAvailable!
16:52:56.426 I ImageReader::onImageAvailable!
Example of the code in react-native-camera, it takes into account the current screen orientation. When the screen is in landscape it flips the divide and multiply calculation. In RN Vision Camera its static I think(assumes you're always portrait) no matter the rotation passed via the prop. Flipping the calculation if your camera is in landscape fixes the issue, at least on my test device.
return if(inputOrientation == Orientation.LANDSCAPE_LEFT || inputOrientation == Orientation.LANDSCAPE_RIGHT) {
if (widthOverHeight) {
// Scale by width to cover height
val scaledWidth = containerSize.height / contentAspectRatio
Size(scaledWidth.roundToInt(), containerSize.height)
} else {
// Scale by height to cover width
val scaledHeight = containerSize.width * contentAspectRatio
Size(containerSize.width, scaledHeight.roundToInt())
}
} else {
if (widthOverHeight) {
// Scale by width to cover height
val scaledWidth = containerSize.height * contentAspectRatio
Size(scaledWidth.roundToInt(), containerSize.height)
} else {
// Scale by height to cover width
val scaledHeight = containerSize.width / contentAspectRatio
Size(containerSize.width, scaledHeight.roundToInt())
}
}
I'm using class component Tired of trying and trying workarounds, version changes, debugging.. Sure that after the first rendering all works well.. Switch on caveman mode:
added this style in the Camera component:
style={{
...StyleSheet.absoluteFill,
width: Dimensions.get('screen').width,
height: Dimensions.get('screen').height + this.state.update,
}}
and this in componentDidMount:
let context = this;
setTimeout(function () {
context.setState({update: 1});
}, 500);
In a functional component:
const [update, doUpdate] = useState(0); return ( <Camera ref={cameraRef} onInitialized={() => doUpdate(1)} style={[StyleSheet.absoluteFill, { width: width + update }]}
Hey folks, I have been keeping up with the past few updates but i cant seem to get the preview to be the same as the capture result (works fine on IOS, testing with Samsung A14 device)
My preview is either 1:1 Square or 4:3 so it is dynamic but no matter what the zoom is always slightly zoomed in compared to the result. I am using v 3.9.1 currently to test.
Hi, guys, are you going to fix this issue with stretched preview in this version? without that this version is basically useless. I believe that the JS hacks are not the solution and the problem should be solved on the native side. Thanks for your commitment.
I downgraded the version to 2.16.8, which solved all the stretch issues and also supported landscape mode.
I am seeing this since updating from version "3.6.4"
Guys, I found out something insteresting. You can test if you want.
You simply use Dimensions.get instead of 'absoluteFill': const {width, height} = Dimensions.get('screen');
<Camera //style={StyleSheet.absoluteFill} style={{width: width, height: height }} device={device} isActive={true} ref={camera} photo={true} />
Now I reload the screen and the error persist. But when I change some value for 'width' or 'height'. For example, I change to style={{width: width, height: height-1 }} and save. Now it fix the problem:
Before: Later:
Does someone knows the solution? Also, how can I diff the node modules as said before?
diff --git a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/CameraDeviceDetails.kt b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/CameraDeviceDetails.kt index a589c26..91f5b8c 100644 --- a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/CameraDeviceDetails.kt +++ b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/CameraDeviceDetails.kt
2.16.8
I tried, got the error: FAILURE: Build failed with an exception.
Where: Build file 'D:\Personal Data\Code\Mobile\node_modules\react-native-vision-camera\android\build.gradle' line: 354
What went wrong: A problem occurred evaluating project ':react-native-vision-camera'.
Cannot invoke method replace() on null object
After hours testing, the version that worked for me is: "react-native-vision-camera": "3.8", If you are stuck, please downgrade the version to 3.8 before anything more complex.
The only workaround I am able to accomplish right now is store the started state and apply the width and height once it has started.
const CameraContainer = () => {
const { width, height } = useWindowDimensions();
const device = useCameraDevice('back');
const [started, setStarted] = useState(fales);
// you'll need this if you're rendering Camera based on a state
useEffect(() => {
return () => {
if (!open) setStarted(false);
}
}, [open]); // could be a global state
return <Camera
isActive
device={device}
style={{ width: started ? width : 0, height: started ? height : 0 }}
onStarted={() => setStarted(true)}
/>
}
If you're keeping the CameraContainer
around instead of conditionally rendering it, add a useEffect
that returns a function to set started to false.
What's happening?
The preview looks stretched on Android.
I figured out that it becomes ok when I change the height of the container in which the Camera renders. Here are a screenshot and logs after changing the height in debug mode.
It reproduces in the example app as well. To reproduce that, I needed to tap the QR-icon. Here is a screenshot.
Investigating the problem I found out that
onMeasure
inPrevieView.kt
method doesn't call after changing the surface size (you can see this in logs). Maybe this is a problem but I'm not sure as I don't have great experience in android development.Reproduceable Code
Relevant log output
Camera Device
Device
Android Emulator (Pixel 7 Pro API 33), OnePlus 10R (CPH2411 Android 13))
VisionCamera Version
3.9.0
Can you reproduce this issue in the VisionCamera Example app?
Yes, I can reproduce the same issue in the Example app here
Additional information