Camera2API:会话已关闭;进一步的变化是非法的

时间:2017-05-09 18:53:18

标签: java android android-camera android-camera2 textureview

我使用Camera2API构建相机活动,将拍摄的图像发送到另一个活动。在某些设备上它可以工作,但在其他设备(较慢的设备)上,我得到一个java.lang.IllegalStateException,它表示"会话已经关闭;进一步的变化是非法的"

我在这一行得到了这个例外(在updatePreview()中):

cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, mBackgroundHandler);

这是我的完整代码:

public class NewCamera extends CustomActivity implements TimingActivity {

private static final String TAG = "NewCamera";
private ImageView takePictureButton;
private TextureView textureView;
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();

static {
    ORIENTATIONS.append(Surface.ROTATION_0, 90);
    ORIENTATIONS.append(Surface.ROTATION_90, 0);
    ORIENTATIONS.append(Surface.ROTATION_180, 270);
    ORIENTATIONS.append(Surface.ROTATION_270, 180);
}

private String cameraId;
protected CameraDevice cameraDevice;
protected CameraCaptureSession cameraCaptureSessions;
protected CaptureRequest.Builder captureRequestBuilder;
private Semaphore mCameraOpenCloseLock = new Semaphore(1);
private Size imageDimension;
private ImageReader imageReader;
private static final int REQUEST_CAMERA_PERMISSION = 200;
private Handler mBackgroundHandler;
private HandlerThread mBackgroundThread;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.new_camera);

    textureView = (TextureView) findViewById(R.id.textureView);
    textureView.setSurfaceTextureListener(textureListener);
    takePictureButton = (ImageView) findViewById(R.id.capture);
    takePictureButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            takePicture();
            captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
            finish();
        }
    });

    changeFlashImage();
}

TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        //open your camera here

        //transfromImage(width, height);
        openCamera();
    }
    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        // Transform you image captured size according to the surface width and height
    }
    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        return false;
    }
    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {

    }
};
private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(CameraDevice camera) {
        mCameraOpenCloseLock.release();
        cameraDevice = camera;
        createCameraPreview();
    }

    @Override
    public void onDisconnected(CameraDevice camera) {
        mCameraOpenCloseLock.release();
        camera.close();
        cameraDevice = null;
    }

    @Override
    public void onError(CameraDevice camera, int error) {
        mCameraOpenCloseLock.release();
        camera.close();
        cameraDevice = null;

        safeDestroy();
    }
};
final CameraCaptureSession.CaptureCallback captureCallbackListener = new CameraCaptureSession.CaptureCallback() {
    @Override
    public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
        super.onCaptureCompleted(session, request, result);
        createCameraPreview();
    }
};

private void safeDestroy() {
    Intent intent = getIntent();
    finish();
    startActivity(intent);
}

protected void startBackgroundThread() {
    mBackgroundThread = new HandlerThread("Camera Background");
    mBackgroundThread.start();
    mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}

protected void stopBackgroundThread() {
    mBackgroundThread.quitSafely();
    try {
        mBackgroundThread.join();
        mBackgroundThread = null;
        mBackgroundHandler = null;
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

protected void takePicture() {
    if(null == cameraDevice) {
        Log.e(TAG, "cameraDevice is null");
        return;
    }
    try {
        ImageReader reader = ImageReader.newInstance(imageDimension.getWidth(), imageDimension.getHeight(), ImageFormat.JPEG, 1);
        List<Surface> outputSurfaces = new ArrayList<Surface>(2);
        outputSurfaces.add(reader.getSurface());
        outputSurfaces.add(new Surface(textureView.getSurfaceTexture()));
        captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
        captureRequestBuilder.addTarget(reader.getSurface());
        captureRequestBuilder.set(CaptureRequest.JPEG_QUALITY, (byte) 100);

        switch(Preferences.getInstance().flashMode % 2) {
            case 0:
                //off
                captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
                break;
            case 1:
                //on
                captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
                break;
        }

        // Orientation
        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
        ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                ByteBuffer buffer = reader.acquireLatestImage().getPlanes()[0].getBuffer();
                byte[] bytes = new byte[buffer.capacity()];
                buffer.get(bytes);

                //handle bytes - pass to another activity
            }
        };

        reader.setOnImageAvailableListener(readerListener, mBackgroundHandler);
        final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() {
            @Override
            public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                super.onCaptureCompleted(session, request, result);
            }
        };
        cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(CameraCaptureSession session) {
                try {
                    session.capture(captureRequestBuilder.build(), captureListener, mBackgroundHandler);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onConfigureFailed(CameraCaptureSession session) {
                safeDestroy();
            }
        }, mBackgroundHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

protected void createCameraPreview() {
    try {
        SurfaceTexture texture = textureView.getSurfaceTexture();

        if(texture == null) {
            return;
        }

        texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight());
        Surface surface = new Surface(texture);
        captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        captureRequestBuilder.addTarget(surface);
        captureRequestBuilder.set(CaptureRequest.JPEG_QUALITY, (byte) 100);
        cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback(){
            @Override
            public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                //The camera is already closed
                if (null == cameraDevice) {
                    return;
                }
                // When the session is ready, we start displaying the preview.
                cameraCaptureSessions = cameraCaptureSession;
                updatePreview();
            }
            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {

            }
        }, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}
private void openCamera() {
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED
            || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(NewCamera.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CAMERA_PERMISSION);
        return;
    }
    CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    Log.e(TAG, "is camera open");
    try {
        cameraId = manager.getCameraIdList()[0];
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
        StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        assert map != null;
        imageDimension = map.getOutputSizes(SurfaceTexture.class)[0];
        transfromImage(textureView.getWidth(), textureView.getHeight());
        manager.openCamera(cameraId, stateCallback, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
    Log.e(TAG, "openCamera X");
}
protected void updatePreview() {
    if(null == cameraDevice) {
        Log.e(TAG, "updatePreview error, return");
    }
    captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
    try {
        cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, mBackgroundHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

@Override
protected void onRestart() {
    super.onRestart();

    changeFlashImage();
}

@Override
protected void onResume() {
    super.onResume();

    changeFlashImage();
    startBackgroundThread();

    if (textureView.isAvailable()) {
        openCamera();
    } else {
        textureView.setSurfaceTextureListener(textureListener);
    }
}
@Override
protected void onPause() {
    closeCamera();
    stopBackgroundThread();
    super.onPause();
}

private void closeCamera() {
    try {
        mCameraOpenCloseLock.acquire();
        if (cameraCaptureSessions != null) {
            cameraCaptureSessions.close();
            cameraCaptureSessions = null;
        }

        if (cameraDevice != null) {
            cameraDevice.close();
            cameraDevice = null;
        }

        if (null != imageReader) {
            imageReader.close();
            imageReader = null;
        }
    } catch (InterruptedException e) {
        throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
    } finally {
        mCameraOpenCloseLock.release();
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();

    if(cameraCaptureSessions!=null){
        cameraCaptureSessions.close();
        cameraCaptureSessions=null;
    }
    if (cameraDevice!=null){
        cameraDevice.close();
        cameraDevice=null;

        if(imageReader!=null){
            imageReader.close();
            imageReader=null;

        }
    }
}

private void transfromImage (int width, int height) {
    Matrix matrix = new Matrix();
    int rotation = getWindowManager().getDefaultDisplay().getRotation();
    RectF textureRectF = new RectF(0, 0, width, height);
    RectF previewRectF = new RectF(0, 0, imageDimension.getHeight(), imageDimension.getWidth());
    float centerX = textureRectF.centerX();
    float centerY = textureRectF.centerY();

    if ((rotation == Surface.ROTATION_90) || (rotation == Surface.ROTATION_270)) {
        previewRectF.offset(centerX - previewRectF.centerX(), centerY - previewRectF.centerY());
        matrix.setRectToRect(textureRectF, previewRectF, Matrix.ScaleToFit.FILL);
        float scale = Math.max ((float) width / imageDimension.getWidth(), (float)height / imageDimension.getHeight());
        matrix.postScale(scale, scale, centerX, centerY);
        matrix.postRotate(90 * (rotation - 2), centerX, centerY);
    }

    textureView.setTransform(matrix);
}

public void changeFlashImage() {
    ImageView ivFlash = (ImageView) findViewById(R.id.flash);

    switch(Preferences.getInstance().flashMode % 2) {
        case 0:
            ivFlash.setImageResource(R.drawable.flash_off);
            break;
        case 1:
            ivFlash.setImageResource(R.drawable.flash_on);
            break;
    }
}

public void changeFlash(View view) {
    Preferences.getInstance().flashMode++;
    changeFlashImage();
}
}

启动相机活动时出现此异常。当它发生时,textureView没有得到更新。我对Camera2API没有多少经验。非常感谢准备帮助的人们!

1 个答案:

答案 0 :(得分:0)

检查所有摄像机配置是否都设置了标志,因为在后台并行设置配置时,您必须给mCaptureSession的repeatingRequest带来一些延迟。在我的情况下,我通过提供500 ms的延迟来设置repeatingRequest来解决此异常:

new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            try {
                mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
                        mCaptureCallback, null);
            } catch (CameraAccessException e) {
                Log.e(TAG, "Failed to start camera preview because it couldn't access camera", e);
            } catch (IllegalStateException e) {
                Log.e(TAG, "Failed to start camera preview.", e);
            }
        }
    }, 500);

最好的快乐编码:-)