Cameraview: Error while starting/stopping the camera engine - Camera is being used after Camera.release() was called

Created on 26 Oct 2017  路  12Comments  路  Source: natario1/CameraView

As requested in #26, I'm opening a new issue:

Your latest changes seem to have solved the very most of the Camera is being used after Camera.release() was called issues. However, I'm afraid it's still not solved 100%. I got one more error:

Fatal Exception: com.otaliastudios.cameraview.CameraUnavailableException: Error while starting the camera engine.
       at com.otaliastudios.cameraview.CameraController$2.run(CameraController.java:107)
       at android.os.Handler.handleCallback(Handler.java:743)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:150)
       at android.os.HandlerThread.run(HandlerThread.java:61)
Caused by java.lang.RuntimeException: Camera is being used after Camera.release() was called
       at android.hardware.Camera.native_setParameters(Camera.java)
       at android.hardware.Camera.setParameters(Camera.java:2260)
       at com.otaliastudios.cameraview.Camera1.onStart(Camera1.java:224)
       at com.otaliastudios.cameraview.CameraController$2.run(CameraController.java:101)
       at android.os.Handler.handleCallback(Handler.java:743)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:150)
       at android.os.HandlerThread.run(HandlerThread.java:61)

Code lines are refering to the master branch of my fork using the extended error handling, but that shouldn't be a problem.

It's very seldom though and seems to be the case when the camera is stopped before it finished starting.

bug

All 12 comments

The same situation produces the following stacktrace inside of the stopping method (in contrast to starting it as above):

Fatal Exception: com.otaliastudios.cameraview.CameraUnavailableException: Error while stopping the camera engine.
       at com.otaliastudios.cameraview.CameraController$3.run(CameraController.java:134)
       at android.os.Handler.handleCallback(Handler.java:739)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:145)
       at android.os.HandlerThread.run(HandlerThread.java:61)
Caused by java.lang.RuntimeException: Camera is being used after Camera.release() was called
       at android.hardware.Camera._stopPreview(Camera.java)
       at android.hardware.Camera.stopPreview(Camera.java:803)
       at com.otaliastudios.cameraview.Camera1.onStop(Camera1.java:246)
       at com.otaliastudios.cameraview.CameraController$3.run(CameraController.java:128)
       at android.os.Handler.handleCallback(Handler.java:739)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:145)
       at android.os.HandlerThread.run(HandlerThread.java:61)

And another related one in Camera1.computePreviewSize:

Fatal Exception: java.lang.RuntimeException: Method called after release()
       at android.hardware.Camera.native_getParameters(Camera.java)
       at android.hardware.Camera.getParameters(Camera.java:1844)
       at com.otaliastudios.cameraview.Camera1.computePreviewSize(Camera1.java:696)
       at com.otaliastudios.cameraview.Camera1.onSurfaceChanged(Camera1.java:108)
       at com.otaliastudios.cameraview.CameraPreview.getOutput(CameraPreview.java:99)
       at com.otaliastudios.cameraview.TextureCameraPreview$1.onSurfaceTextureSizeChanged(TextureCameraPreview.java:36)
       at android.view.TextureView.onSizeChanged(TextureView.java:328)
       at android.view.View.sizeChange(View.java:15027)
       at android.view.View.setFrame(View.java:15000)
       at android.view.View.layout(View.java:14904)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
       at android.view.View.layout(View.java:14909)
       at android.view.ViewGroup.layout(ViewGroup.java:4646)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1673)
       at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1662)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1438)
       at android.view.View.layout(View.java:14909)
       at android.view.ViewGroup.layout(ViewGroup.java:4646)
       at android.support.constraint.ConstraintLayout.onLayout(ConstraintLayout.java:1197)
       at android.view.View.layout(View.java:14909)
       at android.view.ViewGroup.layout(ViewGroup.java:4646)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1673)
       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1527)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1436)
       at android.view.View.layout(View.java:14909)
       at android.view.ViewGroup.layout(ViewGroup.java:4646)
       at android.support.constraint.ConstraintLayout.onLayout(ConstraintLayout.java:1197)
       at android.view.View.layout(View.java:14909)
       at android.view.ViewGroup.layout(ViewGroup.java:4646)
       at android.support.design.widget.HeaderScrollingViewBehavior.layoutChild(HeaderScrollingViewBehavior.java:132)
       at android.support.design.widget.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:42)
       at android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onMeasureChild(AppBarLayout.java:1361)
       at android.support.design.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:869)
       at android.view.View.layout(View.java:14909)
       at android.view.ViewGroup.layout(ViewGroup.java:4646)
       at android.support.v4.widget.DrawerLayout.onLayout(DrawerLayout.java:1172)
       at android.view.View.layout(View.java:14909)
       at android.view.ViewGroup.layout(ViewGroup.java:4646)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
       at android.view.View.layout(View.java:14909)
       at android.view.ViewGroup.layout(ViewGroup.java:4646)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1673)
       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1527)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1436)
       at android.view.View.layout(View.java:14909)
       at android.view.ViewGroup.layout(ViewGroup.java:4646)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
       at android.view.View.layout(View.java:14909)
       at android.view.ViewGroup.layout(ViewGroup.java:4646)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1673)
       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1527)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1436)
       at android.view.View.layout(View.java:14909)
       at android.view.ViewGroup.layout(ViewGroup.java:4646)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
       at android.view.View.layout(View.java:14909)
       at android.view.ViewGroup.layout(ViewGroup.java:4646)
       at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2041)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1798)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1051)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5945)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
       at android.view.Choreographer.doCallbacks(Choreographer.java:574)
       at android.view.Choreographer.doFrame(Choreographer.java:544)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
       at android.os.Handler.handleCallback(Handler.java:733)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:136)
       at android.app.ActivityThread.main(ActivityThread.java:5118)
       at java.lang.reflect.Method.invokeNative(Method.java)
       at java.lang.reflect.Method.invoke(Method.java:515)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:606)
       at dalvik.system.NativeStart.main(NativeStart.java)

I'm afraid I got one more. All in all, they look pretty much the same as in #26.

Non-fatal Exception: com.otaliastudios.cameraview.CameraUnavailableException: Error while starting the camera engine.
       at com.otaliastudios.cameraview.CameraController$2.run(CameraController.java:107)
       at android.os.Handler.handleCallback(Handler.java:739)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:135)
       at android.os.HandlerThread.run(HandlerThread.java:61)
Caused by java.lang.RuntimeException: startPreview failed
       at android.hardware.Camera.startPreview(Camera.java)
       at com.otaliastudios.cameraview.Camera1.setup(Camera1.java:165)
       at com.otaliastudios.cameraview.Camera1.onStart(Camera1.java:229)
       at com.otaliastudios.cameraview.CameraController$2.run(CameraController.java:101)
       at android.os.Handler.handleCallback(Handler.java:739)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:135)
       at android.os.HandlerThread.run(HandlerThread.java:61)

It's always the same: cameraView.start() was called, but the onOpened event wasn't triggered yet. Then there is an Activity's onPause calling cameraView.stop().

I'll take a look at this, thanks! So the event chain is,

onResume()
start()
[onCameraOpen not called]
onPause()
stop()
crash ?

Are you logging onDestroy as well? I think it might be the real issue here, because stop() will do nothing if start() has not finished.

If destroy is the cause, this might not be a real bug, and just got to the surface because you are logging these exceptions. If onStart() / onStop() throws an exception that won't crash the app, it just crashes a secondary thread which we recycle soon after.

And I expect onStart() / onStop() to crash if destroy() is called at the same time (but we get the result - camera is destroyed). So you should be aware of this before displaying "could not open" or "could not stop" to the user.

This is one of the reasons why the error handling is somewhat delicate and I left it aside for the moment, but we'll get to it!

Still, this trace should not happen and must be fixed (because it runs in the UI thread so it's not caught)

You seem to be right for the Error while starting/stopping the camera engine - Camera is being used after Camera.release() was called part: there is a following onDestroy call.

As you pointed out correctly, too: there is _no such call_ for computePreviewSize. There isn't one for the last case of startPreview failed either.

I'll work on the internal logger so people can attach a custom logger (e.g. Crashlytics) and access our internal logs, it would be helpful I think.

If destroy is the cause, this might not be a real bug, and just got to the surface because you are logging these exceptions. If onStart() / onStop() throws an exception that won't crash the app, it just crashes a secondary thread which we recycle soon after.

And I expect onStart() / onStop() to crash if destroy() is called at the same time (but we get the result - camera is destroyed). So you should be aware of this before displaying "could not open" or "could not stop" to the user.

This is one of the reasons why the error handling is somewhat delicate and I left it aside for the moment, but we'll get to it!

What about making all code parts that rely on the mState mutually exclusive? As you are already validating that state, it would prevent raising those errors in the first place.

You mean not calling them at the same time?

start() and stop() are executed on another thread so we don't block the UI thread, while destroy() is executed on the UI thread because of nasty things that might happen when you do configuration changes (e.g. stop() from old activity not finished when start() from new activity is called). That's why stop and destroy are executed concurrently.

Later on I have introduced thread caching in the WorkerHandler class so this might not be a problem anymore. I would have to check how it does behave now.

Calling them may be alright, but the critical code parts should be mutually exclusive. So I would extend the CameraController class by a new private final Object mStateLock = new Object();. Then wrapping all code parts that depend on mState(especially the ones changing it) with synchronized (mStateLock) { .. }. Just as you already did for setParameters related parts in Camera1.

Yes this can solve a couple of issues. I will do this 馃憤

Was this page helpful?
0 / 5 - 0 ratings