Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Camera2API: Session has been closed; further changes are illegal

I'm using Camera2API to build camera activity which sends taken image to another activity. On some devices it works, but on other (slower ones) I get an java.lang.IllegalStateException which says "Session has been closed; further changes are illegal".

I get this exception on this line (in updatePreview()):

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

Here is my full code:

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();
}
}

I get this exception when camera activity is started. When it occurs textureView doesn't get update. I don't have much experience with Camera2API. Big thanks to people ready to help!

like image 589
jelic98 Avatar asked May 09 '17 18:05

jelic98


1 Answers

Check if all the camera configurations have been set with a flagas as you have to give some delay to the repeatingRequest of your mCaptureSession while it setting the configuration for you in parallel in the background. In my case I solved this exception by giving a delay of 500 mS to set the repeatingRequest like this:

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);

All the best Happy coding :-)

like image 120
Utkarsh Srivastava Avatar answered Sep 18 '22 11:09

Utkarsh Srivastava