Previewing simultaneously two rear cameras with Android Camera2

Viewed 82

As title says, I am trying to preview the two rear cameras on my phone on two separate textureviews. What I can do is preview the front camera and any of the rear cameras, but when I try to preview both rear cameras I get an "ERROR_MAX_CAMERAS_IN_USE" from the onError state callback method for the last-opened camera. By searching on the Internet and on here I couldn't really find any pointers on what I'm doing wrong.

For completeness here's the code of my main activity java class, in which I setup the cameras and obtain the previews:

public class MainActivity extends AppCompatActivity {
private final TextureView[] textureViews = new TextureView[2];
protected CameraDevice[] cameraDevices = new CameraDevice[2];
protected CameraCaptureSession[] cameraCaptureSessions = new CameraCaptureSession[2];
protected CaptureRequest.Builder[] captureRequestBuilders = new CaptureRequest.Builder[2];
private Size imageDimension;
private static final int REQUEST_CAMERA_PERMISSION = 200;
private final Handler[] mBackgroundHandlers = new Handler[2];
private final HandlerThread[] mBackgroundThreads = new HandlerThread[2];
private final int[] cameraIDs = {0, 2};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    textureViews[0] = findViewById(R.id.left);
    textureViews[1] = findViewById(R.id.right);
    assert textureViews[0] != null;
    assert textureViews[1] != null;
    textureViews[0].setSurfaceTextureListener(textureListener0);
    textureViews[1].setSurfaceTextureListener(textureListener1);
}

TextureView.SurfaceTextureListener textureListener0 = new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        openCamera(0);
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) { }
};

TextureView.SurfaceTextureListener textureListener1 = new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        openCamera(1);
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) { }
};

private final CameraDevice.StateCallback stateCallback0 = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(CameraDevice camera) {
        if(Debug.LOG) {
            Log.e(Debug.TAG, camera.getId()+ " onOpened.");
        }
        cameraDevices[0] = camera;
        createCameraPreview(0);
    }

    @Override
    public void onDisconnected(CameraDevice camera) {
        cameraDevices[0].close();
    }

    @Override
    public void onError(CameraDevice camera, int error) {
        cameraDevices[0].close();
        cameraDevices[0] = null;
    }
};

private final CameraDevice.StateCallback stateCallback1 = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(CameraDevice camera) {
        if(Debug.LOG) {
            Log.e(Debug.TAG, camera.getId() + " onOpened.");
        }
        cameraDevices[1] = camera;
        createCameraPreview(1);
    }

    @Override
    public void onDisconnected(CameraDevice camera) {
        cameraDevices[1].close();
    }

    @Override
    public void onError(CameraDevice camera, int error) {
        cameraDevices[1].close();
        cameraDevices[1] = null;
    }
};

protected void startBackgroundThreads() {
    mBackgroundThreads[0] = new HandlerThread("Left camera Background");
    mBackgroundThreads[0].start();
    mBackgroundHandlers[0] = new Handler(mBackgroundThreads[0].getLooper());
    mBackgroundThreads[1] = new HandlerThread("Right camera Background");
    mBackgroundThreads[1].start();
    mBackgroundHandlers[1] = new Handler(mBackgroundThreads[1].getLooper());
}

protected void stopBackgroundThreads() {
    mBackgroundThreads[0].quitSafely();
    mBackgroundThreads[1].quitSafely();
    try {
        mBackgroundThreads[0].join();
        mBackgroundThreads[0] = null;
        mBackgroundHandlers[0] = null;
        mBackgroundThreads[1].join();
        mBackgroundThreads[1] = null;
        mBackgroundHandlers[1] = null;
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

protected void createCameraPreview(int cameraID) {
    try {
        SurfaceTexture texture = textureViews[cameraID].getSurfaceTexture();
        assert texture != null;
        texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight());
        Surface surface = new Surface(texture);
        captureRequestBuilders[cameraID] = cameraDevices[cameraID].createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        captureRequestBuilders[cameraID].addTarget(surface);
        cameraDevices[cameraID].createCaptureSession(Collections.singletonList(surface), new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                if (null == cameraDevices[cameraID]) {
                    return;
                }
                cameraCaptureSessions[cameraID] = cameraCaptureSession;
                updatePreview(cameraID);
            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                Toast.makeText(MainActivity.this, "Configuration change", Toast.LENGTH_SHORT).show();
            }
        }, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

private void openCamera(int cameraID) {
    CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    if(Debug.LOG) {
        Log.e(Debug.TAG, "openCamera " + cameraIDs[cameraID]);
    }
    try {
        String cameraId = manager.getCameraIdList()[cameraIDs[cameraID]];
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
        StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        assert map != null;
        imageDimension = map.getOutputSizes(SurfaceTexture.class)[0];
        // Add permission for camera and let user grant the permission
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CAMERA_PERMISSION);
            return;
        }
        manager.openCamera(cameraId, (cameraID == 0) ? stateCallback0 : stateCallback1, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }

    if(Debug.LOG) {
        Log.e(Debug.TAG, "openCamera exited");
    }
}

protected void updatePreview(int cameraID) {
    if (null == cameraDevices[cameraID]) {
        if(Debug.LOG) {
            Log.e(Debug.TAG, "updatePreview error, return");
        }
    }
    captureRequestBuilders[cameraID].set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
    try {
        cameraCaptureSessions[cameraID].setRepeatingRequest(captureRequestBuilders[cameraID].build(), null, mBackgroundHandlers[cameraID]);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

private void closeCamera(int cameraID) {
    if (null != cameraDevices[cameraID]) {
        cameraDevices[cameraID].close();
        cameraDevices[cameraID] = null;
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_CAMERA_PERMISSION) {
        if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
            finish();
        }
    }
}

@Override
protected void onResume() {
    super.onResume();
    startBackgroundThreads();
    if(textureViews[0].isAvailable()) {
        openCamera(0);
    } else {
        textureViews[0].setSurfaceTextureListener(textureListener0);
    }
    if(textureViews[1].isAvailable()) {
        openCamera(1);
    } else {
        textureViews[1].setSurfaceTextureListener(textureListener1);
    }
}

@Override
protected void onPause() {
    closeCamera(0);
    closeCamera(1);
    stopBackgroundThreads();
    super.onPause();
}
0 Answers
Related