RedApparat / Fotoapparat

Making Camera for Android more friendly. 📸
Apache License 2.0
3.82k stars 405 forks source link

Crash in Fotoapparat.start(): IOException: setPreviewTexture failed #178

Closed jpribble closed 6 years ago

jpribble commented 6 years ago

What are you trying to achieve or the steps to reproduce?

This crash was observed on a Samsung Galaxy Grand Prime running Android 5.1.1. It occurred when the camera activity was destroyed immediately after being resumed. Here is the scenario:

  1. CameraActivity takes a picture and launches an ImageReviewActivity.
  2. If the user accepts the image, ImageReviewActivity closes and returns success to CameraActivity.
  3. CameraActivity resumes, save the image to disk, and exits immediately.

During step 3 above, CameraActivity's onStart() (calls camera.start()), onStop() (call camera.stop()), and onDestroy() are all called in less than 1 second.

What was the result you received?

Fatal crash due to an uncaught IOException: setPreviewTexture failed in Fotoapparat's startRoutine.

Logs:

01-11 14:31:34.480 9246-9246 D/Fotoapparat: Fotoapparat: start 01-11 14:31:34.480 9246-11084 D/Fotoapparat: Device: selectCamera 01-11 14:31:34.480 9246-9246 D/Fotoapparat: Fotoapparat: getCurrentParameters 01-11 14:31:34.480 9246-11084 D/Fotoapparat: CameraDevice: open 01-11 14:31:34.480 9246-9246 D/Fotoapparat: Fotoapparat: getCapabilities 01-11 14:31:34.490 9246-11679 D/Fotoapparat: CameraDevice: getParameters$suspendImpl 01-11 14:31:34.490 9246-11679 D/Fotoapparat: CameraDevice: getCapabilities$suspendImpl 01-11 14:31:34.630 9246-11084 D/Fotoapparat: CameraDevice: getCapabilities$suspendImpl 01-11 14:31:34.630 9246-11084 D/Fotoapparat: CameraDevice: updateParameters$suspendImpl 01-11 14:31:34.630 9246-11084 D/Fotoapparat: New camera parameters are: […] 01-11 14:31:34.640 9246-11084 D/Fotoapparat: CameraDevice: updateFrameProcessor 01-11 14:31:34.640 9246-11084 D/Fotoapparat: CameraDevice: setDisplayOrientation 01-11 14:31:34.640 9246-11084 D/Fotoapparat: Image Rotation is: 270. Display rotation is: 90 01-11 14:31:34.650 9246-11084 D/Fotoapparat: CameraDevice: getPreviewResolution 01-11 14:31:34.650 9246-11084 D/Fotoapparat: Preview resolution is: Resolution(width=720, height=1280) 01-11 14:31:34.650 9246-11084 D/Fotoapparat: CameraDevice: setDisplaySurface 01-11 14:31:34.650 9246-11084 E/BufferQueueProducer: [unnamed-9246-0] connect(P): BufferQueue has been abandoned 01-11 14:31:34.650 9246-11084 E/AndroidRuntime: FATAL EXCEPTION: pool-4-thread-1 Process: [redacted], PID: 9246 java.lang.Error: java.io.IOException: setPreviewTexture failed at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1118) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) at java.lang.Thread.run(Thread.java:818) Caused by: java.io.IOException: setPreviewTexture failed at android.hardware.Camera.setPreviewTexture(Native Method) at io.fotoapparat.hardware.CameraDeviceKt.setDisplaySurface(CameraDevice.kt:402) at io.fotoapparat.hardware.CameraDeviceKt.access$setDisplaySurface(CameraDevice.kt:1) at io.fotoapparat.hardware.CameraDevice.setDisplaySurface(CameraDevice.kt:251) at io.fotoapparat.routine.camera.StartRoutineKt.start(StartRoutine.kt:67) at io.fotoapparat.routine.camera.StartRoutineKt.bootStart(StartRoutine.kt:22) at io.fotoapparat.Fotoapparat$start$1.invoke(Fotoapparat.kt:89) at io.fotoapparat.Fotoapparat$start$1.invoke(Fotoapparat.kt:41) at io.fotoapparat.hardware.ExecutorKt$sam$Runnable$ffbfbfd0.run(Executor.kt) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)  at java.lang.Thread.run(Thread.java:818) 

What did you expect?

If there is an exception inside Fotoapparat's async routines, it should be caught and, if it's fatal, be sent to the cameraErrorCallback. I could work around this by optimizing the code not to call camera.start() when it's about to be destroyed but I still wanted to report this uncaught exception since it may affect others.

Context:

Diolor commented 6 years ago

Correct me if wrong. So ways to reproduce are instantly call: start() & stop()? Or is it because also there is a onDestroy() ?

jpribble commented 6 years ago

Yes, just quickly calling start() then stop() should reproduce it. The crash occurs before onDestroy() so you can ignore that.

Diolor commented 6 years ago

Calling fotoapparat.start(), fotoapparat.stop() one after each other cannot reproduce it (since they run in a single thread). Should be some other reason causing this.

Camera#setPreviewTexture(..) doc:
     * @throws IOException if the method fails (for example, if the surface
     *     texture is unavailable or unsuitable).

Maybe surface becomes/was never unavailable onStop() 🤔

ghost commented 6 years ago

I have the same bug here :

E/AndroidRuntime: FATAL EXCEPTION: pool-2-thread-1
                                                                                     Process: .... PID: 16702
                                                                                     java.lang.Error: java.io.IOException: setPreviewTexture failed
                                                                                         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1119)
                                                                                         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
                                                                                         at java.lang.Thread.run(Thread.java:818)
                                                                                      Caused by: java.io.IOException: setPreviewTexture failed
                                                                                         at android.hardware.Camera.setPreviewTexture(Native Method)
                                                                                         at io.fotoapparat.hardware.CameraDeviceKt.setDisplaySurface(CameraDevice.kt:402)
                                                                                         at io.fotoapparat.hardware.CameraDeviceKt.access$setDisplaySurface(CameraDevice.kt:1)
                                                                                         at io.fotoapparat.hardware.CameraDevice.setDisplaySurface(CameraDevice.kt:251)
                                                                                         at io.fotoapparat.routine.camera.StartRoutineKt.start(StartRoutine.kt:67)
                                                                                         at io.fotoapparat.routine.camera.StartRoutineKt.bootStart(StartRoutine.kt:22)
                                                                                         at io.fotoapparat.Fotoapparat$start$1.invoke(Fotoapparat.kt:89)
                                                                                         at io.fotoapparat.Fotoapparat$start$1.invoke(Fotoapparat.kt:41)
                                                                                         at io.fotoapparat.hardware.ExecutorKt$sam$Runnable$ffbfbfd0.run(Executor.kt)
                                                                                         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
                                                                                         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
                                                                                         at java.lang.Thread.run(Thread.java:818) 

this problem happens when I hit back button in any second activity to return to cameraactivity. Problem happened in Emulator android api level 23

Diolor commented 6 years ago

thanks for the reproduction steps @kim24

Diolor commented 6 years ago

I cannot reproduce it. Can you please provide some more info, code with replicating example etc...?

Things I tried, both work fine (on pixel 2):

1.

2.

jpribble commented 6 years ago

I think this issue only occurs on certain devices. I found it after only a few tests on the Samsung Galaxy Grand Prime but it never occurred in 100+ tests on a Samsung Galaxy S8.

matej-svejda commented 6 years ago

I can also reproduce this error with the following code

public class MainActivity extends AppCompatActivity
{
    private Fotoapparat fotoapparat;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        CameraView cameraView = findViewById(R.id.camera_view);
        fotoapparat = Fotoapparat
            .with(this)
            .into(cameraView)
            .lensPosition(back())
            .focusMode(firstAvailable(
                continuousFocusPicture(),
                autoFocus(),
                fixed()
            ))
            .flash(off())
            .build();
    }

    @Override
    protected void onResume()
    {
        super.onResume();
        fotoapparat.start();
    }

    @Override
    protected void onPause()
    {
        fotoapparat.stop();
        super.onPause();
    }
}

When pausing/resuming the app really fast a couple of times on a Galaxy S6 running Android 6.0.1 I get the following crash:

 Process: com.navvis.fotoapparattest, PID: 24056
  java.lang.Error: java.io.IOException: setPreviewTexture failed
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1119)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
      at java.lang.Thread.run(Thread.java:818)
   Caused by: java.io.IOException: setPreviewTexture failed
      at android.hardware.Camera.setPreviewTexture(Native Method)
      at io.fotoapparat.hardware.CameraDeviceKt.setDisplaySurface(CameraDevice.kt:402)
      at io.fotoapparat.hardware.CameraDeviceKt.access$setDisplaySurface(CameraDevice.kt:1)
      at io.fotoapparat.hardware.CameraDevice.setDisplaySurface(CameraDevice.kt:251)
      at io.fotoapparat.routine.camera.StartRoutineKt.start(StartRoutine.kt:67)
      at io.fotoapparat.routine.camera.StartRoutineKt.bootStart(StartRoutine.kt:22)
      at io.fotoapparat.Fotoapparat$start$1.invoke(Fotoapparat.kt:89)
      at io.fotoapparat.Fotoapparat$start$1.invoke(Fotoapparat.kt:41)
      at io.fotoapparat.hardware.ExecutorKt$sam$Runnable$ffbfbfd0.run(Executor.kt)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
      at java.lang.Thread.run(Thread.java:818) 

.

dmitry-zaitsev commented 6 years ago

Since IOException is a checked exception we should always be ready for it. In this particular case, we could just catch and ignore it.

jpribble commented 6 years ago

Thanks for the fix! It no longer crashes but I'm seeing that this can leave the device in a state where the camera preview is black and no frames are passed to the frame processor. Do you think this should be propagated to the cameraErrorCallback instead of ignoring it? Here are the logs from a Samsung GS7 with the black camera preview:

01-25 10:55:50.141 22825-22825/ D/TakePictureActivity: onStart() 01-25 10:55:50.161 22825-22825/ D/Fotoapparat: Fotoapparat: start 01-25 10:55:50.161 22825-22825/ D/Fotoapparat: Fotoapparat: getCurrentParameters 01-25 10:55:50.161 22825-26832/ D/Fotoapparat: Device: selectCamera 01-25 10:55:50.161 22825-22825/ D/Fotoapparat: Fotoapparat: getCapabilities 01-25 10:55:50.161 22825-26832/ D/Fotoapparat: CameraDevice: open 01-25 10:55:50.211 22825-26832/ D/Fotoapparat: CameraDevice: getCapabilities$suspendImpl 01-25 10:55:50.211 22825-26832/ D/Fotoapparat: CameraDevice: updateParameters$suspendImpl 01-25 10:55:50.211 22825-26832/ D/Fotoapparat: New camera parameters are: CameraParameters flashMode: io.fotoapparat.parameter.Flash$Off@c59cc10 focusMode: io.fotoapparat.parameter.FocusMode$ContinuousFocusPicture@7430d28 jpegQuality: 90 previewFpsRange: FpsRange(min=15000, max=30000) antiBandingMode: io.fotoapparat.parameter.AntiBandingMode$Auto@a7efa41 sensorSensitivity: null pictureResolution: Resolution(width=1280, height=720) previewResolution: Resolution(width=1280, height=720) 01-25 10:55:50.211 22825-26832/ D/Fotoapparat: CameraDevice: updateFrameProcessor 01-25 10:55:50.211 22825-26832/ D/Fotoapparat: CameraDevice: setDisplayOrientation 01-25 10:55:50.211 22825-26832/ D/Fotoapparat: Image orientation is: io.fotoapparat.hardware.orientation.Orientation$Horizontal$ReverseLandscape@44fd3d4. Display orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$Portrait@27ff97d. Preview orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$Portrait@27ff97d. 01-25 10:55:50.211 22825-26832/ D/Fotoapparat: CameraDevice: getPreviewResolution 01-25 10:55:50.211 22825-26832/ D/Fotoapparat: Preview resolution is: Resolution(width=1280, height=720) 01-25 10:55:50.211 22825-26832/ D/Fotoapparat: CameraDevice: setDisplaySurface 01-25 10:55:50.211 22825-26832/ E/BufferQueueProducer: [SurfaceTexture-0-22825-2] connect(P): BufferQueue has been abandoned 01-25 10:55:50.211 22825-26832/ D/Fotoapparat: Can't start preview because of the exception: java.io.IOException: setPreviewTexture failed 01-25 10:55:50.221 22825-26832/ D/Fotoapparat: CameraDevice: getParameters$suspendImpl 01-25 10:55:50.221 22825-26832/ D/Fotoapparat: CameraDevice: getCapabilities$suspendImpl 01-25 10:55:50.221 22825-23138/ D/mali_winsys: new_window_surface returns 0x3000, [2560x1440]-format:1 01-25 10:55:50.231 22825-22825/ D/ViewRootImpl: MSG_RESIZED_REPORT: ci=Rect(0, 0 - 0, 0) vi=Rect(0, 0 - 0, 0) or=2 01-25 10:55:51.751 22825-26832/ D/Fotoapparat: CameraDevice: setDisplayOrientation 01-25 10:55:51.751 22825-26832/ D/Fotoapparat: Image orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$Portrait@27ff97d. Display orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$Portrait@27ff97d. Preview orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$Portrait@27ff97d. 01-25 10:55:51.931 22825-26832/ D/Fotoapparat: CameraDevice: setDisplayOrientation 01-25 10:55:51.931 22825-26832/ D/Fotoapparat: Image orientation is: io.fotoapparat.hardware.orientation.Orientation$Horizontal$ReverseLandscape@44fd3d4. Display orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$Portrait@27ff97d. Preview orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$Portrait@27ff97d. 01-25 10:55:55.121 22825-26832/ D/Fotoapparat: CameraDevice: setDisplayOrientation 01-25 10:55:55.121 22825-26832/ D/Fotoapparat: Image orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$ReversePortrait@f00ccf4. Display orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$Portrait@27ff97d. Preview orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$Portrait@27ff97d. 01-25 10:55:55.721 22825-26832/ D/Fotoapparat: CameraDevice: setDisplayOrientation 01-25 10:55:55.721 22825-26832/ D/Fotoapparat: Image orientation is: io.fotoapparat.hardware.orientation.Orientation$Horizontal$ReverseLandscape@44fd3d4. Display orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$Portrait@27ff97d. Preview orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$Portrait@27ff97d. 01-25 10:55:55.781 22825-26832/ D/Fotoapparat: CameraDevice: setDisplayOrientation 01-25 10:55:55.781 22825-26832/ D/Fotoapparat: Image orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$ReversePortrait@f00ccf4. Display orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$Portrait@27ff97d. Preview orientation is: io.fotoapparat.hardware.orientation.Orientation$Vertical$Portrait@27ff97d. 01-25 10:55:55.831 22825-26832/ D/Fotoapparat: CameraDevice: setDisplayOrientation … (continues repeating ^)

thuypt commented 5 years ago

@jpribble Agree with you. All off exceptions if the lib cannot handle should be propagated to error callback instead of ignore and close the issue.

I also got this after open any camera app then get back to my app.

@dmitry-zaitsev Pls consider to return known exceptions in the error callback, and let us handle by our self, at least we can call recreate screen or show a error message.