RedApparat / Fotoapparat

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

Crash when updating photo resolution after controller is started #288

Open jrmybrault opened 5 years ago

jrmybrault commented 5 years ago

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

I'm trying to defer the photo resolution selection to my activity's presenter. To avoid any strong correlation between the presenter and Fotoapparat I created my own class PhotoResolution (which is more or less identical to Fotoapparat's Resolution). When the camera capabilities are available, I map the Resolution set to a list of PhotoResolution and send it to the presenter. The presenter then picks the wanted resolution (in my case it uses the first resolution that has a width / height superior to a given setting) and send that resolution back to the activity. Whenever I try to update the camera configuration with the selected resolution the following crash immediately occurs :

D/Fotoapparat: CameraDevice: getCapabilities$suspendImpl E/Camera-JNI: Callback buffer was too small! Expected 1382400 bytes, but got 1036800 bytes! Couldn't allocate byte array for JPEG data D/AndroidRuntime: Shutting down VM E/AndroidRuntime: FATAL EXCEPTION: main Process: com.urgo.apollo.dev, PID: 7558 java.lang.IllegalStateException: data must not be null at io.fotoapparat.preview.PreviewStream$start$1.onPreviewFrame(PreviewStream.kt:51) at android.hardware.Camera$EventHandler.handleMessage(Camera.java:1124) at android.os.Handler.dispatchMessage(Handler.java:105) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6944) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)

Here's the code:

private void setupCamera() {
    final FotoapparatBuilder cameraControllerBuilder = Fotoapparat.with(this)
                                                                  .into(mCameraView)
                                                                  .previewScaleType(ScaleType.CenterCrop)
                                                                  .focusMode(SelectorsKt.firstAvailable(
                                                                          FocusModeSelectorsKt.continuousFocusPicture(),
                                                                          FocusModeSelectorsKt.autoFocus(),
                                                                          FocusModeSelectorsKt.fixed()))
                                                                  .logger(LoggersKt.loggers(LoggersKt.logcat()));

    mCameraController = cameraControllerBuilder.build();
    mCameraController.start();

    mCameraController.getCapabilities().whenAvailable(capabilities -> {
        final List<PhotoResolution> availablePhotoResolutions = Stream.of(capabilities.getPictureResolutions())
                                                                      .map(resolution -> new PhotoResolution(resolution.width, resolution.height))
                                                                      .collect(Collectors.toList());

        mPresenter.onCameraSetup(availablePhotoResolutions);

        return Unit.INSTANCE;
    });
}

@Override
public void updatePhotoResolution(final PhotoResolution resolution) {
    mCameraController.updateConfiguration(UpdateConfiguration.builder()
                                                               .photoResolution(resolutions -> FotoapparatUtils.mapToFotoapparatResolution(resolutions, resolution))
                                                               .build());
}

The mapToFotoapparatResolution function just map my PhotoResolution instance to a Fotoapparat's Resolution instance.

Context: