[ImageReader.OnImageAvailableListener()拍照时未调用

问题描述 投票:0回答:2

我有一个为Android 26 SDK开发的Camera App。我曾经在Motorola G5和G6上愉快地使用过它,但是当我移动到Motorola G7时,当我按下按钮在我的应用程序中拍照时,该应用程序崩溃。

G7正在运行Android9。我还有另一部Android 9手机,即Samsung S10 plus。当我按下拍照按钮时,S10 plus不会崩溃。

调试时,我注意到G7不会调用S10时的ImageReader.OnImageAvailableListener。查看代码,这是图像的保存位置,供以后在CameraCaptureSession.CaptureCallback中使用。该回调函数期望填充字节并在不存在该字节时崩溃(我没有包括堆栈跟踪,因为它有点帮助,但是如果您认为自己愿意的话,我可以。)

如果在某些情况下通过调试缓慢运行G7,则可以让G7保存图像。

因此,我有一个按钮,在其中调用函数onImageCaptureClick()会执行很多操作,但其中一项操作是创建ImageReader.OnImageAvailableListener。 OnImageAvailableListener保存图像并从图像缓冲区填充一个可变字节。通过使用reader.setOnImageAvailableListener(readerListener,null)将此onImageAvailableListener附加到我的阅读器,并且从未使用过此侦听器。当我进入CaptureCallBack时,类变量字节未填充,应用程序崩溃。

您是否知道我将在哪里解决这个问题?

protected void onImageCaptureClick() {
    if (null == mCameraDevice) {
        logger.debug("null == mCameraDevice");
        Log.e(TAG, "cameraDevice is null");
        return;
    }
    CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

    try {
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraDevice.getId());
        Size[] jpegSizes = null;
        if (characteristics != null) {
            jpegSizes = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(ImageFormat.JPEG);
        }
        int width = 640;
        int height = 480;
        if (jpegSizes != null && 0 < jpegSizes.length) {
            width = jpegSizes[0].getWidth();
            height = jpegSizes[0].getHeight();
        }
        ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
        List < Surface > outputSurfaces = new ArrayList < > (2);
        outputSurfaces.add(reader.getSurface());
        outputSurfaces.add(new Surface(mTextureView.getSurfaceTexture()));
        final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
        captureBuilder.addTarget(reader.getSurface());

        if (mFlashMode == FLASH_MODE_OFF) {
            captureBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
            logger.debug("FLASH OFF");
        }

        if (mFlashMode == CONTROL_AE_MODE_ON) {
            captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                CaptureRequest.CONTROL_AE_MODE_ON);
            captureBuilder.set(CaptureRequest.FLASH_MODE,
                CaptureRequest.FLASH_MODE_TORCH);
            logger.debug("FLASH ON");
        }

        if (mFlashMode == CONTROL_AE_MODE_ON_AUTO_FLASH) {

            captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
            captureBuilder.set(CaptureRequest.FLASH_MODE,
                CaptureRequest.FLASH_MODE_OFF);
            logger.debug("FLASH AUTO");
        }

        captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);

        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));

        final File file = new File(_pictureUri.getPath());
        logger.debug("OnImageCaptureClick: _pictureUri is: " + _pictureUri.getPath());
        // ************************************
        // this listener is not used on the G7,
        // and so the image isn't saved.
        // ************************************
        ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Image image = null;
                try {
                    image = reader.acquireLatestImage();
                    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                    bytes = new byte[buffer.capacity()];
                    buffer.get(bytes);
                    logger.debug("onImageCaptureClick, the filesize to save is: " + bytes.toString());
                    save();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (image != null) {
                        image.close();
                    }
                }
            }


            private void save() throws IOException {
                OutputStream output = null;
                try {
                    output = new FileOutputStream(file);
                    output.write(bytes);
                } finally {
                    if (null != output) {
                        output.close();
                    }
                }
            }


        };
        // ********************************************************
        // the reader sets the listener here but it is never called
        // and when I get in to the CaptureCallback the BitmapUtils
        // expects bytes to be populated and crashes the app
        // ********************************************************
        reader.setOnImageAvailableListener(readerListener, null);
        final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() {

            @Override
            public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                super.onCaptureCompleted(session, request, result);

                try {
                    BitmapUtils.addTimeStampAndRotate(_pictureUri, bytes);


                    Intent intent = new Intent(CameraActivity.this, CameraReviewPhotoActivity.class);
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, _pictureUri);
                    startActivityForResult(intent, CameraActivity.kRequest_Code_Approve_Image);

                } catch (IOException e) {
                    e.printStackTrace();
                } catch (ImageReadException e) {
                    e.printStackTrace();
                } catch (ImageWriteException e) {
                    e.printStackTrace();
                }

            }
        };

        mCameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(CameraCaptureSession session) {
                try {
                    session.capture(captureBuilder.build(), captureListener, null);

                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onConfigureFailed(CameraCaptureSession session) {
                Log.w(TAG, "Failed to configure camera");
            }
        }, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    } finally {
        takePictureButton.setEnabled(false);
        mTextureView.setEnabled(false);
    }
android android-camera2
2个回答
0
投票

我想我已经找到解决方案了。

我有以下电话:

  • 三星S10 Plus
  • Motorola G7
  • 摩托罗拉G6

我的应用程序可在S10和G6上使用。S10和G6都在OnImageAvailableListener回调之前调用onCaptureCompleted函数。但是,G7在onCaptureCompletedOnImageAvailableListener前后都以相反的方式调用它们。

根据https://proandroiddev.com/understanding-camera2-api-from-callbacks-part-1-5d348de65950,正确的方法是onCaptureCompleted,然后是OnImageAvailableListener

在我的代码中,我假设OnImageAvailableListener保存了图像,然后OnCaptureCompleted尝试对其进行操作,这导致崩溃。

查看每个设备的INFO_SUPPORTED_HARDWARE_LEVEL,我从0级到3级都有以下支持级别。

  • [三星S10 Plus报告设备级别支持级别1
  • Motorola G7报告设备级别支持级别3
  • Motorola G6报告设备级别支持级别2
  • 我现在的假设是,与其他级别相比,当您支持级别3的android-camera2 API时,事件以不同的顺序触发。

希望这会有所帮助


0
投票

API不保证onCaptureCompleted和OnImageAvailableListener的顺序。它们可能以任意顺序到达,具体取决于设备,捕获设置,设备上的负载,甚至您拥有的特定操作系统版本。

© www.soinside.com 2019 - 2024. All rights reserved.