CameraAccessException Сломанный канал при запуске сеанса камеры - PullRequest
0 голосов
/ 28 мая 2020

Я получаю это CameraAccessException при попытке создать сеанс захвата после onOpened() обратного вызова от CameraDevice.StateCallback

Сначала сбой

Fatal Exception: android.hardware.camera2.CameraAccessException: CAMERA_ERROR (3): endConfigure:537: Camera 1: Error configuring streams: Broken pipe (-32)
       at android.hardware.camera2.CameraManager.throwAsPublicException(CameraManager.java:1127)
       at android.hardware.camera2.impl.ICameraDeviceUserWrapper.endConfigure(ICameraDeviceUserWrapper.java:117)
       at android.hardware.camera2.impl.CameraDeviceImpl.configureStreamsChecked(CameraDeviceImpl.java:481)
       at android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSessionInternal(CameraDeviceImpl.java:669)
       at android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSession(CameraDeviceImpl.java:517)
       at a.b.c.d.MainActivity.startCapturingSession(MainActivity.java:364)
       at a.b.c.d.MainActivity.access$startCapturingSession(MainActivity.java:36)
       at a.b.c.d.MainActivity$stateCallback$1.onOpened(MainActivity.java:90)
       at android.hardware.camera2.impl.CameraDeviceImpl$1.run(CameraDeviceImpl.java:150)
       at android.os.Handler.handleCallback(Handler.java:883)
       at android.os.Handler.dispatchMessage(Handler.java:100)
       at android.os.Looper.loop(Looper.java:237)
       at android.os.HandlerThread.run(HandlerThread.java:67)

Caused by android.os.ServiceSpecificException: endConfigure:537: Camera 1: Error configuring streams: Broken pipe (-32)
       at android.os.Parcel.createException(Parcel.java:2102)
       at android.os.Parcel.readException(Parcel.java:2056)
       at android.os.Parcel.readException(Parcel.java:2004)
       at android.hardware.camera2.ICameraDeviceUser$Stub$Proxy.endConfigure(ICameraDeviceUser.java:742)
       at android.hardware.camera2.impl.ICameraDeviceUserWrapper.endConfigure(ICameraDeviceUserWrapper.java:114)
       at android.hardware.camera2.impl.CameraDeviceImpl.configureStreamsChecked(CameraDeviceImpl.java:481)
       at android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSessionInternal(CameraDeviceImpl.java:669)
       at android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSession(CameraDeviceImpl.java:517)
       at a.b.c.d.MainActivity.startCapturingSession(MainActivity.java:364)
       at a.b.c.d.MainActivity.access$startCapturingSession(MainActivity.java:36)
       at a.b.c.d.MainActivity$stateCallback$1.onOpened(MainActivity.java:90)
       at android.hardware.camera2.impl.CameraDeviceImpl$1.run(CameraDeviceImpl.java:150)
       at android.os.Handler.handleCallback(Handler.java:883)
       at android.os.Handler.dispatchMessage(Handler.java:100)
       at android.os.Looper.loop(Looper.java:237)
       at android.os.HandlerThread.run(HandlerThread.java:67)

Ошибка камеры c говорит, что камера в состоянии ошибки. Может ли это быть из-за того, что что-то неправильно реализовано в коде приложения?

Мой код приложения

private val stateCallback = object : CameraDevice.StateCallback() {
        override fun onOpened(camera: CameraDevice) {
            cameraDevice = camera
            startCapturingSession()
        }

        override fun onDisconnected(camera: CameraDevice) {
            cameraDevice = null
            camera.close()
        }

        override fun onError(camera: CameraDevice, error: Int) {
            onDisconnected(camera)
        }
    }

@WorkerThread
    private fun startCapturingSession() {

        val prevSurface = previewSurface ?: kotlin.run {
            throw IllegalStateException("Preview Surface is null while starting capture session")
        }

        reader1 = ImageReader.newInstance(
            PREVIEW_WIDTH, PREVIEW_HEIGHT,
            ImageFormat.YUV_420_888, IMAGE_READER_MAX_IMAGES
        )
        reader1!!.setOnImageAvailableListener(analyser, null)
        val surface1 = reader1!!.surface

        reader2 = ImageReader.newInstance(
            PREVIEW_WIDTH, PREVIEW_HEIGHT,
            ImageFormat.YUV_420_888, IMAGE_READER_MAX_IMAGES
        )
        reader2!!.setOnImageAvailableListener(imageAvailableListener, null)
        val surface2 = reader2!!.surface
        val device = cameraDevice
            ?: throw IllegalStateException("Camera Device null while capturing session")

        device.createCaptureSession( // this is line 364 from the crash log
            listOf(surface1, surface2, prevSurface),
            object : CameraCaptureSession.StateCallback() {
                override fun onConfigureFailed(session: CameraCaptureSession) {
                }

                override fun onConfigured(session: CameraCaptureSession) {
                    // more code here
                }
            }, null
        )
    }

В худшем случае я могу поймать это, закрыть и снова включить камеру?

Это происходит не всегда, но последовательные попытки открытия приложения не дали результатов.

Ответы [ 2 ]

1 голос
/ 02 июня 2020

У меня была такая же проблема, и это было связано с соотношением сторон моего превью. Я реализовал следующий код, чтобы вернуть мне правильный размер предварительного просмотра для моего устройства. Я обнаружил, что проблема заключалась в значении, которое я передавал соотношению сторон (я передавал размер предварительного просмотра), и на некоторых устройствах камера разбивалась при попытке открыть. Я получал исключение CameraError (3) о сломанной трубе.

private static Size ChooseOptimalSize(Size[] choices, int textureViewWidth, int textureViewHeight,
            int maxWidth, int maxHeight)
        {
            var bigEnough = new List<Size>();
            var notBigEnough = new List<Size>();

            //Test different aspect ratios(4:3, 16:9)
            int w = 4;
            int h = 3;

            foreach(Size option in choices)
            {
                if(option.Width <= maxWidth && option.Height <= maxHeight)
                {
                    if(option.Height == option.Width * h                    / w)
                    {
                        if (option.Width >= textureViewWidth && option.Height >= textureViewHeight)
                        {
                            bigEnough.Add(option);
                        }
                        else
                            notBigEnough.Add(option);
                    }
                }
            }
           
            if (bigEnough.Count > 0)
                return (Size)Collections.Min(bigEnough, new ComparesSizeByArea());
            else if (notBigEnough.Count > 0)
                return (Size)Collections.Max(notBigEnough, new ComparesSizeByArea());
            else
            {   
                return choices[0];
            }
        }

ComparesSizesByArea - это вспомогательный класс со следующим методом:

 public int Compare(Java.Lang.Object lhs, Java.Lang.Object rhs)
        {
            var lhsSize = (Size)lhs;
            var rhsSize = (Size)rhs;

            return Long.Signum((long)lhsSize.Width * lhsSize.Height - (long)rhsSize.Width * rhsSize.Height);
        }

В расширенном textureView также ограничьте его шириной:

 protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        base.OnMeasure(widthMeasureSpec, heightMeasureSpec);

        int width = MeasureSpec.GetSize(widthMeasureSpec);
        int height = MeasureSpec.GetSize(heightMeasureSpec);

        if (ratioWidth == 0 || ratioHeight == 0)
            SetMeasuredDimension(width, height);
        else
        {
            if (width > (float)height * ratioWidth / ratioHeight)
                SetMeasuredDimension(width, width * ratioHeight / ratioWidth);
            else
                SetMeasuredDimension(height * ratioWidth / ratioHeight, height);
        }        
    }
0 голосов
/ 11 июля 2020

Похоже, это было вызвано перерисовкой макета, что привело к изменению размера SurfaceView (для предварительного просмотра камеры). При изменении размера будет создан новый Surface. Более старый будет уничтожен, а связанный с ним CameraCaptureSession не может быть создан.

Мне пришлось закрыть текущий сеанс захвата камеры и создать новый после изменения моего макета. Также пришлось добавить противодействие, чтобы предотвратить повторные обновления в течение короткого периода времени. Не самое чистое решение, но, похоже, пока работает.

...