Open RishabhK12 opened 3 months ago
@RishabhK12
I suspect the issue might have something to do with async
initialization and update
I cannot test it myself, but maybe you can try with the code sample below?
It seems like your code sample doesn't image stream at all. I am facing an issue with the image streaming. When I comment out startImageStream, I don't have any errors.
Hi @RishabhK12 I have waited 5 minutes but didn't see the exception as yours. I see this code on your sample code, is there any action on the device to fall into one of these cases?
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
switch (state) {
case AppLifecycleState.inactive:
_cameraController?.stopImageStream();
break;
case AppLifecycleState.resumed:
_initStateAsync();
break;
default:
}
}
It would be better if you could provide clearer and concrete steps to reproduce this. Thanks!
@huycozy
I can reproduce the issue exactly as @RishabhK12 described, (just start remember to "move the phone around" to make the camera do some work)
please find below a very minimal code sample below (reproduces with the code above as well)
you can find the device details in the doctor
#
note that the issue does not occur w/o startImageStream
as the reporter pointed out
@huycozy My device isn't falling into any of the cases in the didChangeAppLifecycleState
method.
Thanks
(just start remember to "move the phone around" to make the camera do some work)
I did but still couldn't reproduce this on my device Pixel 7, Android 14 using the latest packages:
camera:
dependency: "direct main"
description:
name: camera
sha256: "2170a943dcb67be2af2c6bcda8775e74b41d4c02d6a4eb10bdc832ee185c4eea"
url: "https://pub.dev"
source: hosted
version: "0.11.0+1"
camera_android_camerax:
dependency: transitive
description:
name: camera_android_camerax
sha256: "8bd9cab67551642eb33ceb33ece7acc0890014fc90ddfae637c7e2b683657e65"
url: "https://pub.dev"
source: hosted
version: "0.6.7+2"
Can you both share the fetched camera_android_camerax
version from pubspec.lock
file?
AndroidCameraCameraX._configureImageAnalysis.analyze (package:camera_android_camerax/src/android_camera_camerax.dart:1236:7)
Checking the error trace, it seems to fall analyze
method here but doesn't match the line of code. So it's worth checking on the latest version of package.
Can you both share the fetched
camera_android_camerax
version frompubspec.lock
file?
@huycozy sure
@huycozy Here's mine, it has some older versions of some packages compared to @iapicca. When I update I still get the issue.
Thank you. I can see the same camera_android_camerax
version as mine (0.6.7+2
) on @iapicca comment. I still can't reproduce this issue but will label this for further input from others.
I've experienced the same issue on my Samsung S24 Ultra. Unfortunately, there aren't any specific steps to reproduce the problem, which I understand makes it challenging to address.
I've experienced the same issue on my Samsung S24 Ultra. Unfortunately, there aren't any specific steps to reproduce the problem, which I understand makes it challenging to address.
@PerLycke what version? (both flutter and camera)
I've experienced the same issue on my Samsung S24 Ultra. Unfortunately, there aren't any specific steps to reproduce the problem, which I understand makes it challenging to address.
@PerLycke what version? (both flutter and camera)
Latest version for both:
camera: ^0.11.0+2
Flutter 3.24.0 stable channel
Just ran this on a Pixel 3A for 35 minutes and didn't see a crash. Wondering if this device specific and also if it has to do with https://github.com/flutter/flutter/issues/145893
Just ran this on a Pixel 3A for 35 minutes and didn't see a crash. Wondering if this device specific and also if it has to do with #145893
@camsim99 I also didn't see crashes while "leaving the phone alone" but as mentioned with the same device the crash occurs quite quickly when "moving around" the phone, it's strange but it happens
(just start remember to "move the phone around" to make the camera do some work)
@iapicca Ah okay thanks for the clarification here. Next steps are to try that out and investigate if this is a memory leak.
I tried running this on the same device (Pixel 3A) for 15 minutes while moving it, but the stream doesn't stop. May have to try another device.
It happens to me on two devices i have available. Even when the device is stationary but the image in camera is moving. Both devices have same cpu MediaTek Helio G99 but different cameras.
I am on the latest version of camera lib.
Tried running this on a Samsung Galaxy S7 for 30ish minutes and still didn't see the issue.
We previously experienced a memory leak with image streaming, see https://github.com/flutter/flutter/issues/145893 for details. The fix for that can be found in https://github.com/flutter/packages/pull/6493. To explain the fix briefly, we have an InstanceManager
that tracks native Camera-related objects and so, we increased the rate at which this InstanceManager
removes instances it tracks and no longer needs when image streaming is performed.
For anyone willing to take a deeper look and see if it helps, you can increase the interval by lowering the CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL_FOR_IMAGE_ANALYSIS
value. We would definitely appreciate anyone trying this out and would be open to landing a PR if this helps your case!
I can confirm this is happening when after upgrading from camera: ^0.10.6
to ^0.11.0+2
, both on Android emulator (Windows 10, Pixel 4 API 29 x86 AVD) and on a real Samsung A54 5G.
It's been happening a lot to me on tablets from the Samsung A9 family.
Non-fatal Exception: io.flutter.plugins.firebase.crashlytics.FlutterError
PlatformException(java.lang.NullPointerException, NullPointerException, Cause: null, Stacktrace: java.lang.NullPointerException at java.util.Objects.requireNonNull(Objects.java:207) at qb.z5.d(Unknown Source:12) at qb.z5.a(Unknown Source:0) at qb.g3.c(Unknown Source:27) at qb.f3.a(Unknown Source:2) at mb.a$b.a(Unknown Source:17) at ab.c.l(Unknown Source:18) at ab.c.m(Unknown Source:41) at ab.c.i(Unknown Source:0) at ab.b.run(Unknown Source:12) at android.os.Handler.handleCallback(Handler.java:958) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:230) at android.os.Looper.loop(Looper.java:319) at android.app.ActivityThread.main(ActivityThread.java:8934) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103) , null)
ImageProxyHostApi.close (camerax_library.g.dart:2764) AndroidCameraCameraX._configureImageAnalysis.analyze (android_camera_camerax.dart:1236)
@camsim99 Superficial analysis:
I managed to simulate this error in the emulator by decreasing DEFAULT_CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL
to 500 and kept observing. In some cases, after 1 minute the error occurred and in others it took around 10 to 15 minutes for the error to occur.
One point is that for some reason the error reference always occurred with ImageProxy when trying to search the method:
void analyze(
int identifier,
int imageProxyIdentifier,
) {
final Analyzer instance = _instanceManager.getInstanceWithWeakReference(identifier)!;
final ImageProxy imageProxy = _instanceManager.getInstanceWithWeakReference(imageProxyIdentifier)!;
instance.analyze(
imageProxy,
);
}
I still need to check other points, but we have something superficial to start with. And must pass a function in startImageStream:
await cameraController.startImageStream((CameraImage c){
print('Image Stream');
});
@Mairramer thank you for the analysis! This gives me some clarity.
@bparrishMines Is it possible that a tradeoff of lowering the interval of how often an InstanceManager
removes garbage collected weak references to native Android objects (which we did before, see this comment for context) is that they get garbage collected prematurely? In this case, I'm specifically wondering if the ImageProxy
s that are not being found by the InstanceManager
have been garbage collected before the plugin has the chance to close
them, causing the error:
E/flutter ( 8806): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(java.lang.NullPointerException, NullPointerException, Cause: null, Stacktrace: java.lang.NullPointerException
E/flutter ( 8806): at java.util.Objects.requireNonNull(Objects.java:207)
E/flutter ( 8806): at io.flutter.plugins.camerax.ImageProxyHostApiImpl.getImageProxyInstance(ImageProxyHostApiImpl.java:80)
E/flutter ( 8806): at io.flutter.plugins.camerax.ImageProxyHostApiImpl.close(ImageProxyHostApiImpl.java:73)
@Mairramer thank you for the analysis! This gives me some clarity.
@bparrishMines Is it possible that a tradeoff of lowering the interval of how often an
InstanceManager
removes garbage collected weak references to native Android objects (which we did before, see this comment for context) is that they get garbage collected prematurely? In this case, I'm specifically wondering if theImageProxy
s that are not being found by theInstanceManager
have been garbage collected before the plugin has the chance toclose
them, causing the error:E/flutter ( 8806): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(java.lang.NullPointerException, NullPointerException, Cause: null, Stacktrace: java.lang.NullPointerException E/flutter ( 8806): at java.util.Objects.requireNonNull(Objects.java:207) E/flutter ( 8806): at io.flutter.plugins.camerax.ImageProxyHostApiImpl.getImageProxyInstance(ImageProxyHostApiImpl.java:80) E/flutter ( 8806): at io.flutter.plugins.camerax.ImageProxyHostApiImpl.close(ImageProxyHostApiImpl.java:73)
The FinalizationListener
shouldn't be able to remove it prematurely. Since the Dart ImageProxy
should have priority that prevents the Java one from being removed. But I would have to check the specific implementation of the InstanceManager
in the camera_android_camerax
plugin.
However, since all plugins are now using Java 11 from https://github.com/flutter/packages/pull/7795#event-1452762262, we can switch the implementation to use Cleaners instead anyways.
@Mairramer thank you for the analysis! This gives me some clarity. @bparrishMines Is it possible that a tradeoff of lowering the interval of how often an
InstanceManager
removes garbage collected weak references to native Android objects (which we did before, see this comment for context) is that they get garbage collected prematurely? In this case, I'm specifically wondering if theImageProxy
s that are not being found by theInstanceManager
have been garbage collected before the plugin has the chance toclose
them, causing the error:E/flutter ( 8806): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(java.lang.NullPointerException, NullPointerException, Cause: null, Stacktrace: java.lang.NullPointerException E/flutter ( 8806): at java.util.Objects.requireNonNull(Objects.java:207) E/flutter ( 8806): at io.flutter.plugins.camerax.ImageProxyHostApiImpl.getImageProxyInstance(ImageProxyHostApiImpl.java:80) E/flutter ( 8806): at io.flutter.plugins.camerax.ImageProxyHostApiImpl.close(ImageProxyHostApiImpl.java:73)
The
FinalizationListener
shouldn't be able to remove it prematurely. Since the DartImageProxy
should have priority that prevents the Java one from being removed. But I would have to check the specific implementation of theInstanceManager
in thecamera_android_camerax
plugin.However, since all plugins are now using Java 11 from flutter/packages#7795 (comment), we can switch the implementation to use Cleaners instead anyways.
@camsim99 @bparrishMines
Although I'm not a Java expert, when performing local tests, I noticed that by replacing the use of HashMap
with ConcurrentHashMap
, the problem was apparently resolved. There was no significant increase in memory consumption, and the NullPointerException
didn't occur anymore. I have a naive and perhaps silly question: could there have been some synchronization issue between the Dart and Java threads (even though they might not have anything to do with each other, I think), and by switching to ConcurrentHashMap
, which is thread-safe, was this problem eliminated?
I still need to conduct more tests, but so far, testing the same scenario with just this change, I haven't encountered the problem again.
Update:
Putting everything as ConcurrentHashMap
the error still occurred, just with a lower frequency. Putting only the strongInstances
as ConcurrentHashMap
didn't really give an error, but it could just be a coincidence.
@Mairramer Based on my understanding of the code, threading shouldn't be the problem. Essentially the issue is that the ImageProxy
can't be found in the InstanceManager
with the identifier provided. So the ImageProxy
was never added, added incorrectly, or removed prematurely. Using the InstanceManager
on a background thread could cause another error, but it probably shouldn't cause a NullPointerException
.
My guess is that the problem could be a bug in the implementation. @camsim99 Pigeon ProxyApi support for Kotlin has landed, so we should update the plugin to use it. Ideally this should fix this problem and/or provide better debugging errors.
I just attempted to update Android InstanceManager
s to use Cleaner
s, but it looks like it actually requires Android 33. Despite only requiring Java 9:
https://developer.android.com/reference/java/lang/ref/Cleaner
What package does this bug report belong to?
camera
What target platforms are you seeing this bug on?
Android
Have you already upgraded your packages?
Yes
Steps to reproduce
flutter run
on the code below. This is using the latest version 0.11.0+1.Expected results
No exception is output and the image stream never stops.
Actual results
The image stream stops and the platform exception shown below in the log is output. This happens randomly, sometimes after running it for a few minutes, other times after running it for longer like 8ish minutes. This has been run on an s23 ultra. It happens quicker if I do something with the image that the stream is giving me. It doesn't happen if I comment out the startImageStream line.
Code sample
Code sample
```dart import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) => MaterialApp( title: 'Stream Test', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: const CameraWidget(), ); } class CameraWidget extends StatefulWidget { const CameraWidget({super.key}); @override StateScreenshots or Videos
Screenshots / Video demonstration
[Upload media here]Logs
Logs
```console I/CameraManagerGlobal( 8806): Camera 0 facing CAMERA_FACING_BACK state now CAMERA_STATE_CLOSED for client com.example.lamp API Level 2 User Id 0 I/CameraManagerGlobal( 8806): Camera 1 facing CAMERA_FACING_FRONT state now CAMERA_STATE_CLOSED for client com.snapchat.android API Level 2 User Id 0 I/CameraManagerGlobal( 8806): Camera 2 facing CAMERA_FACING_BACK state now CAMERA_STATE_CLOSED for client android.system API Level 2 User Id 0 I/CameraManagerGlobal( 8806): Camera 20 facing CAMERA_FACING_BACK state now CAMERA_STATE_CLOSED for client com.sec.android.app.camera API Level 2 User Id 0 I/CameraManagerGlobal( 8806): Camera 21 facing CAMERA_FACING_BACK state now CAMERA_STATE_CLOSED for client android.system API Level 2 User Id 0 I/CameraManagerGlobal( 8806): Camera 23 facing CAMERA_FACING_BACK state now CAMERA_STATE_CLOSED for client android.system API Level 2 User Id 0 I/CameraManagerGlobal( 8806): Camera 3 facing CAMERA_FACING_FRONT state now CAMERA_STATE_CLOSED for client android.system API Level 2 User Id 0 I/CameraManagerGlobal( 8806): Camera 4 facing CAMERA_FACING_FRONT state now CAMERA_STATE_CLOSED for client android.system API Level 2 User Id 0 I/CameraManagerGlobal( 8806): Camera 52 facing CAMERA_FACING_BACK state now CAMERA_STATE_CLOSED for client android.system API Level 2 User Id 0 I/CameraManagerGlobal( 8806): Camera 54 facing CAMERA_FACING_BACK state now CAMERA_STATE_CLOSED for client android.system API Level 2 User Id 0 I/CameraManagerGlobal( 8806): Camera 56 facing CAMERA_FACING_BACK state now CAMERA_STATE_CLOSED for client com.sec.android.app.camera API Level 2 User Id 0 I/CameraManagerGlobal( 8806): Camera 58 facing CAMERA_FACING_BACK state now CAMERA_STATE_CLOSED for client android.system API Level 2 User Id 0 I/CameraManagerGlobal( 8806): Camera 90 facing CAMERA_FACING_FRONT state now CAMERA_STATE_CLOSED for client android.system API Level 2 User Id 0 D/CameraRepository( 8806): Added camera: 0 I/Camera2CameraInfo( 8806): Device Level: INFO_SUPPORTED_HARDWARE_LEVEL_3 I/CameraManager( 8806): registerAvailabilityCallback: Is device callback = false I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 0 status STATUS_PRESENT I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 1 status STATUS_PRESENT I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 2 status STATUS_PRESENT I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 3 status STATUS_PRESENT D/CameraRepository( 8806): Added camera: 1 I/Camera2CameraInfo( 8806): Device Level: INFO_SUPPORTED_HARDWARE_LEVEL_FULL I/CameraManager( 8806): registerAvailabilityCallback: Is device callback = false I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 0 status STATUS_PRESENT I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 1 status STATUS_PRESENT I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 2 status STATUS_PRESENT I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 3 status STATUS_PRESENT D/CameraRepository( 8806): Added camera: 2 I/Camera2CameraInfo( 8806): Device Level: INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED I/CameraManager( 8806): registerAvailabilityCallback: Is device callback = false I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 0 status STATUS_PRESENT I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 1 status STATUS_PRESENT I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 2 status STATUS_PRESENT I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 3 status STATUS_PRESENT D/CameraRepository( 8806): Added camera: 3 I/Camera2CameraInfo( 8806): Device Level: INFO_SUPPORTED_HARDWARE_LEVEL_FULL I/CameraManager( 8806): registerAvailabilityCallback: Is device callback = false I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 0 status STATUS_PRESENT I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 1 status STATUS_PRESENT I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 2 status STATUS_PRESENT I/CameraManagerGlobal( 8806): postSingleUpdate device: camera id 3 status STATUS_PRESENT D/CameraValidator( 8806): Verifying camera lens facing on dm3q, lensFacingInteger: null I/BLASTBufferQueue( 8806): [SurfaceView[com.example.lamp/com.example.live_object_detection_ssd_mobilenet.MainActivity]@0#1](f:0,a:0,s:0) onFrameAvailable the first frame is available D/SurfaceView@98475c0( 8806): 159675840 setAlpha: alpha=1.0 I/SurfaceView( 8806): 159675840 Changes: creating=false format=false size=false visible=false alpha=true hint=false visible=false left=false top=false z=false attached=true lifecycleStrategy=false I/SurfaceView@98475c0( 8806): 159675840 Cur surface: Surface(name=null)/@0xafedb05 I/SurfaceView@98475c0( 8806): updateSurface: mVisible = true mSurface.isValid() = true I/SurfaceView@98475c0( 8806): updateSurface: mSurfaceCreated = true surfaceChanged = false visibleChanged = false I/SurfaceView( 8806): 159675840 surfaceRedrawNeeded I/SurfaceView( 8806): 159675840 finishedDrawing V/SurfaceView@98475c0( 8806): Layout: x=0 y=0 w=1440 h=3032, frame=Rect(0, 0 - 1440, 3032) I/ViewRootImpl@70d56ee[MainActivity]( 8806): Setup new sync=wmsSync-ViewRootImpl@70d56ee[MainActivity]#2 I/ViewRootImpl@70d56ee[MainActivity]( 8806): Creating new active sync group ViewRootImpl@70d56ee[MainActivity]#3 I/ViewRootImpl@70d56ee[MainActivity]( 8806): registerCallbacksForSync syncBuffer=false D/SurfaceView( 8806): 159675840 updateSurfacePosition RenderWorker, frameNr = 1, position = [0, 0, 1440, 3032] surfaceSize = 1440x3032 I/SurfaceView@98475c0( 8806): uSP: rtp = Rect(0, 0 - 1440, 3032) rtsw = 1440 rtsh = 3032 I/SurfaceView@98475c0( 8806): onSSPAndSRT: pl = 0 pt = 0 sx = 1.0 sy = 1.0 I/SurfaceView@98475c0( 8806): aOrMT: ViewRootImpl@70d56ee[MainActivity] t = android.view.SurfaceControl$Transaction@809abf8 fN = 1 android.view.SurfaceView.-$$Nest$mapplyOrMergeTransaction:0 android.view.SurfaceView$SurfaceViewPositionUpdateListener.positionChanged:1666 android.graphics.RenderNode$CompositePositionUpdateListener.positionChanged:369 I/ViewRootImpl@70d56ee[MainActivity]( 8806): mWNT: t=0xb4000079b4d5a880 mBlastBufferQueue=0xb4000079b5b31d00 fn= 1 mRenderHdrSdrRatio=1.0 caller= android.view.SurfaceView.applyOrMergeTransaction:1598 android.view.SurfaceView.-$$Nest$mapplyOrMergeTransaction:0 android.view.SurfaceView$SurfaceViewPositionUpdateListener.positionChanged:1666 I/ViewRootImpl@70d56ee[MainActivity]( 8806): Received frameDrawingCallback syncResult=0 frameNum=1. I/ViewRootImpl@70d56ee[MainActivity]( 8806): mWNT: t=0xb4000079b4d9b680 mBlastBufferQueue=0xb4000079b5b31d00 fn= 1 mRenderHdrSdrRatio=1.0 caller= android.view.ViewRootImpl$8.onFrameDraw:13841 android.view.ThreadedRenderer$1.onFrameDraw:792Flutter Doctor output
Doctor output
```console [√] Flutter (Channel stable, 3.22.2, on Microsoft Windows [Version 10.0.19045.4651], locale en-US) • Flutter version 3.22.2 on channel stable at C:\dev\flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision 761747bfc5 (8 weeks ago), 2024-06-05 22:15:13 +0200 • Engine revision edd8546116 • Dart version 3.4.3 • DevTools version 2.34.3 [√] Windows Version (Installed version of Windows is version 10 or higher) [√] Android toolchain - develop for Android devices (Android SDK version 34.0.0) • Android SDK at C:\Users\Administrator\AppData\Local\Android\sdk • Platform android-34, build-tools 34.0.0 • Java binary at: C:\Program Files\Android\Android Studio\jbr\bin\java • Java version OpenJDK Runtime Environment (build 17.0.10+0--11572160) • All Android licenses accepted. [√] Chrome - develop for the web • Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe [!] Visual Studio - develop Windows apps (Visual Studio Build Tools 2019 16.11.36) • Visual Studio at C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools • Visual Studio Build Tools 2019 version 16.11.34902.97 X The current Visual Studio installation is incomplete. Please use Visual Studio Installer to complete the installation or reinstall Visual Studio. [√] Android Studio (version 2023.3) • Android Studio at C:\Program Files\Android\Android Studio • Flutter plugin can be installed from: https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 17.0.10+0--11572160) [√] VS Code (version 1.91.1) • VS Code at C:\Users\Administrator\AppData\Local\Programs\Microsoft VS Code • Flutter extension version 3.92.0 [√] Connected device (4 available) • SM S918U1 (mobile) • R3CW50M09SD • android-arm64 • Android 14 (API 34) • Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.19045.4651] • Chrome (web) • chrome • web-javascript • Google Chrome 127.0.6533.73 • Edge (web) • edge • web-javascript • Microsoft Edge 127.0.2651.74 [√] Network resources • All expected network resources are available. ```