Using android camera2 API to display camera feed

15.8k views Asked by At

I'm trying to create a very simple app which shows the picture received from the camera on the display. I'm using the camera2 API, because I am new to this, so I try to use the most recent technology.

I reach the point where I call createCaptureSession(), but then onConfigureFailed() gets called and I don't know what the problem is or how to find it.

Here is my source:

SurfaceView mSurfaceView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Log.d(TAG, "onCreate() start");

    setContentView(R.layout.activity_main);

    mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);

}

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

    Log.d(TAG, "onResume() start");

    initCamera();
}

private void initCamera() {
    Log.d(TAG, "initCamera() start");
    CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    Log.d(TAG, "acquired cameraManager: " + cameraManager);

    String[] cameraIdList;
    try {
        cameraIdList = cameraManager.getCameraIdList();
    } catch (CameraAccessException e) {
        Log.e(TAG, "couldn't get camera list", e);
        return;
    }
    Log.d(TAG, "acquired cameraIdList: length: " + cameraIdList.length);

    if (cameraIdList.length == 0) {
        Log.w(TAG, "couldn't detect a camera");
        return;
    }

    String camera0Id = cameraIdList[0];

    Log.d(TAG, "chosen camera: " + camera0Id);

    try {
        cameraManager.openCamera(camera0Id, deviceCallback, null);
    } catch (CameraAccessException e) {
        Log.e(TAG, "couldn't open camera", e);
    }
    Log.d(TAG, "called cameraManager.openCamera()");
}

CameraDevice.StateCallback deviceCallback = new CameraDevice.StateCallback() {

    @Override
    public void onOpened(CameraDevice camera) {
        Log.d(TAG, "deviceCallback.onOpened() start");

        Surface surface = mSurfaceView.getHolder().getSurface();
        Log.d(TAG, "surface: " + surface);

        List<Surface> surfaceList = Collections.singletonList(surface);

        try {
            camera.createCaptureSession(surfaceList, sessionCallback, null);
        } catch (CameraAccessException e) {
            Log.e(TAG, "couldn't create capture session for camera: " + camera.getId(), e);
            return;
        }

    }

    @Override
    public void onDisconnected(CameraDevice camera) {
        Log.d(TAG, "deviceCallback.onDisconnected() start");
    }

    @Override
    public void onError(CameraDevice camera, int error) {
        Log.d(TAG, "deviceCallback.onError() start");
    }

};

CameraCaptureSession.StateCallback sessionCallback = new CameraCaptureSession.StateCallback() {
    @Override
    public void onConfigured(CameraCaptureSession session) {
        Log.i(TAG, "capture session configured: " + session);
    }

    @Override
    public void onConfigureFailed(CameraCaptureSession session) {
        Log.e(TAG, "capture session configure failed: " + session);
    }
};

And the output is:

...﹕ onCreate() start
...﹕ onResume() start
...﹕ initCamera() start
...﹕ acquired cameraManager: android.hardware.camera2.CameraManager@5d68786
...﹕ acquired cameraIdList: length: 2
...﹕ chosen camera: 0
...﹕ called cameraManager.openCamera()
...﹕ deviceCallback.onOpened() start
...﹕ surface: Surface(name=null)/@0x52c91e3
...﹕ capture session configure failed: android.hardware.camera2.impl.CameraCaptureSessionImpl@1a8c7a99

The following is in my AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera2" />
3

There are 3 answers

1
Dhruv On

Set Custom camera code:

import java.io.IOException;
import java.util.List;

import android.annotation.SuppressLint;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;

@SuppressWarnings("deprecation")
public class Preview extends ViewGroup implements SurfaceHolder.Callback {
    private final String TAG = "Preview";

    SurfaceView mSurfaceView;
    SurfaceHolder mHolder;
    Size mPreviewSize;
    List<Size> mSupportedPreviewSizes;
    Camera mCamera;

    public Preview(Context context, SurfaceView sv) {
        super(context);

        mSurfaceView = sv;
        // addView(mSurfaceView);

        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void setCamera(Camera camera) {
        mCamera = camera;
        if (mCamera != null) {
            mSupportedPreviewSizes = mCamera.getParameters()
                    .getSupportedPreviewSizes();
            requestLayout();

            mCamera.setDisplayOrientation(90);
            setCamFocusMode();

            // // get Camera parameters
            // Camera.Parameters params = mCamera.getParameters();
            //
            // List<String> focusModes = params.getSupportedFocusModes();
            // if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
            // // set the focus mode
            // params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            // // set Camera parameters
            // mCamera.setParameters(params);
            //
            // }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // We purposely disregard child measurements because act as a
        // wrapper to a SurfaceView that centers the camera preview instead
        // of stretching it.
        final int width = resolveSize(getSuggestedMinimumWidth(),
                widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(),
                heightMeasureSpec);
        setMeasuredDimension(width, height);

        if (mSupportedPreviewSizes != null) {
            mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width,
                    height);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed && getChildCount() > 0) {
            final View child = getChildAt(0);

            final int width = r - l;
            final int height = b - t;

            int previewWidth = width;
            int previewHeight = height;
            if (mPreviewSize != null) {
                previewWidth = mPreviewSize.width;
                previewHeight = mPreviewSize.height;
            }

            // Center the child SurfaceView within the parent.
            if (width * previewHeight > height * previewWidth) {
                final int scaledChildWidth = previewWidth * height
                        / previewHeight;
                child.layout((width - scaledChildWidth) / 2, 0,
                        (width + scaledChildWidth) / 2, height);
            } else {
                final int scaledChildHeight = previewHeight * width
                        / previewWidth;
                child.layout(0, (height - scaledChildHeight) / 2, width,
                        (height + scaledChildHeight) / 2);
            }
        }
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, acquire the camera and tell it where
        // to draw.
        try {
            if (mCamera != null) {
                mCamera.setPreviewDisplay(holder);
            }
        } catch (IOException exception) {
            Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // Surface will be destroyed when we return, so stop the preview.
        if (mCamera != null) {
            mCamera.stopPreview();
        }
    }

    private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) w / h;
        if (sizes == null)
            return null;

        Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        // Try to find an size match aspect ratio and size
        for (Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        // Cannot find the one match the aspect ratio, ignore the requirement
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        if (mCamera != null) {

            Camera.Parameters parameters = mCamera.getParameters();
            List<Camera.Size> previewSizes = parameters
                    .getSupportedPreviewSizes();

            // You need to choose the most appropriate previewSize for your app
            Camera.Size previewSize;
            try {
                previewSize = previewSizes.get(1);
                parameters.setPreviewSize(previewSize.width, previewSize.height);
                mCamera.setParameters(parameters);
                mCamera.startPreview();
            } catch (Exception e) {
                previewSize = previewSizes.get(0);
                parameters.setPreviewSize(previewSize.width, previewSize.height);
                mCamera.setParameters(parameters);
                mCamera.startPreview();
            }
        }
    }

    @SuppressLint("InlinedApi")
    private void setCamFocusMode() {

        if (null == mCamera) {
            return;
        }
        /* Set Auto focus */
        Parameters parameters = mCamera.getParameters();
        List<String> focusModes = parameters.getSupportedFocusModes();
        if (focusModes
                .contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
            parameters
                    .setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
        } else if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
        }
        mCamera.setParameters(parameters);
    }

    // Check Flash in Camera or not
    public boolean hasFlash() {
        if (mCamera == null) {
            return false;
        }

        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters.getFlashMode() == null) {
            return false;
        }

        List<String> supportedFlashModes = parameters.getSupportedFlashModes();
        if (supportedFlashModes == null
                || supportedFlashModes.isEmpty()
                || supportedFlashModes.size() == 1
                && supportedFlashModes.get(0).equals(
                        Camera.Parameters.FLASH_MODE_OFF)) {
            return false;
        }
        return true;
    }

    public boolean setFlash(int flashMode) {
        if (mCamera == null) {
            return false;
        }

        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters.getFlashMode() == null) {
            return false;
        }

        List<String> supportedFlashModes = parameters.getSupportedFlashModes();
        if (flashMode == 1 && supportedFlashModes.get(2).equals(
                        Camera.Parameters.FLASH_MODE_AUTO)) {
            parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
        } else if (flashMode == 2 && supportedFlashModes.get(1).equals(
                        Camera.Parameters.FLASH_MODE_ON)) {
            parameters.setFlashMode(Camera.Parameters.FLASH_MODE_ON);
        } else if (flashMode == 3 && supportedFlashModes.get(0).equals(
                        Camera.Parameters.FLASH_MODE_OFF)) {
            parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
        }
        mCamera.setParameters(parameters);
        return true;
    }
}

You can set camera preview size in "surfaceChanged()" method.

This code is work for me in Android 5.0.1 and 5.1.1 also.

0
yydcdut On

you should set the size of preview

mSurfaceView.getHolder().setFixedSize(previewSize.getWidth(), previewSize.getHeight());
0
Rene Juuse On

Since the asker mentioned that he was testing on the Galaxy S4, maybe it had something to do with the fact that some of the Samsung's Lollipop phones haven't implemented the Camera2 API. DPReview