2

I am building a custom camera with the Camera2 API. So far the camera works very well except for the preview which is distorted sometimes. Let's say I open the camera 7 times in a row. All of the attempts are succesful and the 8th time the camera preview is distorted. It looks like it uses the width as the height and vice versa.

I have based my code on the Google sample implementation of the camera2 which can be found here. the interesting thing is that even the Google sample implementation has this distorted preview sometimes. I have tried to modify the AutoFitTextureView but nothing was successful. I am currently using the AutoFitTextureView.java Google provides again.

A similar post to this one can be found here. However the proposed fixes didn't solve the problem.

I can reproduce the problem by changing the following in the setUpCameraOutputs method:

mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());

to:

mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());

Another weird thing is that whenever the distorted preview occurs and you just press the home button so the app goes in onPause() and open up the app again so onResume() gets called, the preview is perfect every time.

Has anyone here experienced this problem and found a fix for it?

Thanks in advance

Community
  • 1
  • 1
Dennis
  • 818
  • 2
  • 10
  • 23

2 Answers2

5

I am facing the same issue on Sony Xperia Z3 Tablet Compact.

The pull request that Alex pointed out doesn't seem to work for me. It results in camera preview larger than the view's area (preview is cropped).

While I was not able to track the issue specifically, I was able to find a workaround. It seems the distortion happens while there are changes of mTextureView's size while in process of opening camera. Delaying the camera opening procedure fixes the issue.

Modified openCamera method:

/**
 * Opens the camera specified by {@link StepFragmentCamera#mCameraId}.
 */
private void openCamera(int width, int height) {
    startBackgroundThread();

    if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
            != PackageManager.PERMISSION_GRANTED) {
        requestCameraPermission();
        return;
    }
    setUpCameraOutputs(width, height);
    configureTransform(width, height);

    /*
     * Delay the opening of the camera until (hopefully) layout has been fully settled.
     * Otherwise there is a chance the preview will be distorted (tested on Sony Xperia Z3 Tablet Compact).
     */
    mTextureView.post(new Runnable() {
        @Override
        public void run() {
            /*
             * Carry out the camera opening in a background thread so it does not cause lag
             * to any potential running animation.
             */
            mBackgroundHandler.post(new Runnable() {
                @Override
                public void run() {
                    Activity activity = getActivity();
                    CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
                    try {
                        if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
                            throw new RuntimeException("Time out waiting to lock camera opening.");
                        }
                        manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
                    }
                }
            });
        }
    });
}
Miroslav
  • 322
  • 5
  • 13
  • I was having the same issue and this fixed it. After testing, the call to `mTextureView.post` is necessary so that the camera is opened after the TextureView is initialized. I did not need `mBackgroundHandler.post`. I also had to take care that `mPreviewSize` did not come from the TextureView itself; I needed to fix that size by wrapping my TextureView with a RelativeLayout that is controlled by layout. – Bryan W. Wagner Sep 02 '19 at 14:54
  • @BryanW.Wagner wouldn't it be better to use [tree observer](https://stackoverflow.com/a/7735122/192373)? – Alex Cohn Sep 02 '19 at 14:56
  • @AlexCohn yes possibly; for my use case I'm reacting to `TextureView.SurfaceTextureListener` events `onSurfaceTextureAvailable` and `onSurfaceTextureSizeChanged` to change the TextureView size dynamically. I derived my use case from the official sample but deviated, so this issue might not be in the official sample code. – Bryan W. Wagner Sep 02 '19 at 15:04
  • @Miroslav your solution worked after 2 days of struggling. Thank you! – Corneliu Serediuc Feb 27 '20 at 08:54
  • @Miroslav your workaround worked for me as well, thank you so much – Toka A. Amin Jun 23 '22 at 14:40
2

The Google Camera2Basic sample was finally fixed. The original code had a tiny < vs > mistake. It had been wrong for 2 years.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • 1
    Unfortunately, the pull request you linked does not fix the issue, the < vs > is actually correct. The code in the pull request is wrong because it results in a cropped camera preview (see the comments in the link). – Miroslav Feb 01 '19 at 15:46
  • @Miroslav, cropped preview was the intended outcome of that fix. – Alex Cohn Feb 01 '19 at 18:30
  • 1
    From what I remember testing it, the cropped preview was drastically off the actual scene the camera was to capture (~half of it was not visible). Surely this was not intended. Furthermore, to this day, the pull request is not merged. – Miroslav Feb 04 '19 at 12:59
  • 1
    I was having this issue and I am not using the aspect-ratio logic in the sample code (I'm using my own crop). It's a timing issue with respect to TextureView initialization; the answer from @Miroslav is needed. – Bryan W. Wagner Sep 02 '19 at 14:50