Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Custom Camera with Camera2 API

Android Custom Camera with Camera2 API

Create your custom camera on Android without dying in the attempt

Marcos Ambrosi

December 17, 2015
Tweet

Other Decks in Technology

Transcript

  1. ANDROID CUSTOM CAMERA WITH CAMERA2 API BUILT IN CAMERA -

    LO BUENO ▸ Fácil de implementar (MediaStore.ACTION_IMAGE_CAPTURE) ▸ Delegamos el trabajo a una app de terceros.
  2. ANDROID CUSTOM CAMERA WITH CAMERA2 API BUILT IN CAMERA -

    LO NO TAN BUENO ▸ Delegamos el trabajo a una app de terceros ▸ No hay control sobre la UI ▸ No hay manejo de errores
  3. ANDROID CUSTOM CAMERA WITH CAMERA2 API CUSTOM CAMERA - LO

    BUENO ▸ Mejor integración con la UI de nuestra app ▸ Podemos manejar errores, modos, etc ▸ Custom features (grabar solo 10 segundos)
  4. ANDROID CUSTOM CAMERA WITH CAMERA2 API CUSTOM CAMERA - LO

    NO TAN BUENO ▸ Implementación compleja ▸ Lidiar con fragmentación ▸ Errores pueden costar caro
  5. ANDROID CUSTOM CAMERA WITH CAMERA2 API CAMERA1 ▸ Basic Modes:

    Preview, Capture & Record ▸ Auto Focus, Flash… ▸ No RAW support ▸ Difícil de implementar custom features (HDR, Burst, etc) ▸ No hay controles manuales (aperture, shutter speed)
  6. ANDROID CUSTOM CAMERA WITH CAMERA2 API CAMERA2 ▸ A partir

    de API Level 21 ▸ DSLR ▸ Total re-work (Camera1 es deprecada en API 21) ▸ On-device post processing (HDR, Focus Stacking) ▸ RAW support ▸ Manual exposure and shutter speed ▸ Mejor manejo de Multi-core CPU & GPU ▸ HAL update
  7. ANDROID CUSTOM CAMERA WITH CAMERA2 API CAMERA2 - CLASES ▸

    CameraDevice: cámara conectada al dispositivo ▸ CameraManager: servicio para obtener un CameraDevice ▸ CameraCaptureSession: ▸ configura los outputs para CameraDevice ▸ se encarga de el manejo de requests ▸ CaptureResult: resultado de una ImageCapture request
  8. ANDROID CUSTOM CAMERA WITH CAMERA2 API THE ISSUE SURFACEVIEW Camera

    Device SurfaceView.getSurfaceTexture().setDefaultBufferSize(width, height);
  9. 768px 1184px Nexus 4 Ratio: 1.54 CAMERA1 Camera.getSupportedPreviewSizes() 1280 X

    96O 1.33 1280 X 720 1.77 800 X 480 1.66 720 X 480 1.5 640 X 480 1.33 576 X 432 1.33 480 X 320 1.5 384 X 288 1.33 352 X 288 1.22 320 X 240 1.33 CAMERA2 StreamConfigurationMap.getOutputSizes(SurfaceTexture.class)
  10. ANDROID CUSTOM CAMERA WITH CAMERA2 API <?xml version="1.0" encoding="utf-8"?>
 <FrameLayout

    xmlns:android="http://schemas.android.com/apk/res/ android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/container"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="#000"
 tools:context="com.marcosambrosi.camera2sample.MainActivity">
 
 <TextureView
 android:id="@+id/texture"
 android:layout_width="match_parent"
 android:layout_height="match_parent" />
 </FrameLayout>
 1) DEFINIR MI SURFACEVIEW
  11. ANDROID CUSTOM CAMERA WITH CAMERA2 API private TextureView.SurfaceTextureListener mSurfaceTextureListener =


    new TextureView.SurfaceTextureListener() {
 @Override
 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
 //SETEO OUTPUTS
 //INICIO LA CAMARA
 }
 
 @Override
 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
 
 }
 
 @Override
 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
 return false;
 }
 
 @Override
 public void onSurfaceTextureUpdated(SurfaceTexture surface) {
 
 }
 }; 2) DECLARO UN LISTENER PARA EL SURFACEVIEW
  12. ANDROID CUSTOM CAMERA WITH CAMERA2 API CameraManager cameraManager = (CameraManager)getSystemService(Context.CAMERA_SERVICE);

    
 
 
 for (String cameraId : cameraManager.getCameraIdList()) { 
 CameraCharacteristics cameraCharacteristics =
 cameraManager.getCameraCharacteristics(cameraId);
 
 //We are interested in the back camera only 
 if (CameraCharacteristics.LENS_FACING_FRONT == cameraCharacteristics.
 get(CameraCharacteristics.LENS_FACING)) {
 continue;
 }
 
 
 StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.
 get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); } 3) CONFIGURAMOS LOS OUTPUTS 1/2
  13. ANDROID CUSTOM CAMERA WITH CAMERA2 API StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.

    get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); mPreviewSize = getPreferredPreviewSize(streamConfigurationMap (SurfaceTexture.class), width, height); 4) CONFIGURAMOS LOS OUTPUTS 2/2
  14. ANDROID CUSTOM CAMERA WITH CAMERA2 API private void openCamera() {

    
 CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); 
 try { 
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 if (checkSelfPermission(Manifest.permission.CAMERA)
 != PackageManager.PERMISSION_GRANTED) {
 requestCameraPermission();
 return;
 }
 } cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, mBackgroundHandler); 
 } catch (CameraAccessException e) {
 e.printStackTrace();
 }
 } 5) INICIAMOS LA CAMARA
  15. ANDROID CUSTOM CAMERA WITH CAMERA2 API private CameraDevice.StateCallback mCameraDeviceStateCallback =

    new CameraDevice.StateCallback() {
 @Override
 public void onOpened(CameraDevice camera) {
 mCameraDevice = camera;
 createCameraPreviewSession();
 }
 
 @Override
 public void onDisconnected(CameraDevice camera) {
 camera.close();
 mCameraDevice = null;
 }
 
 @Override
 public void onError(CameraDevice camera, int error) {
 camera.close();
 mCameraDevice = null;
 }
 }; 6) DEFINIMOS UN LISTENER PARA EL INICIO DE LA CAMARA
  16. ANDROID CUSTOM CAMERA WITH CAMERA2 API private void createCameraPreviewSession() {

    
 SurfaceTexture texture = mTextureView.getSurfaceTexture(); 
 texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
 Surface previewSurface = new Surface(texture); . . . } 7) CREAMOS LA SESION DE PREVIEW 1/2
  17. ANDROID CUSTOM CAMERA WITH CAMERA2 API try {
 // We

    set up a CaptureRequest.Builder with the output Surface.
 mPreviewRequestBuilder
 = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
 mPreviewRequestBuilder.addTarget(previewSurface);
 
 mCameraDevice.createCaptureSession(Arrays.asList(previewSurface), new CameraCaptureSession.StateCallback() {
 @Override
 public void onConfigured(CameraCaptureSession session) {
 // The camera is already closed
 if (null == mCameraDevice) {
 return;
 }
 
 // When the session is ready, we start displaying the preview.
 mCaptureSession = session;
 try {
 // Auto focus should be continuous for camera preview.
 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
 CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
 // Flash is automatically enabled when necessary.
 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
 CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
 
 
 mPreviewRequest = mPreviewRequestBuilder.build(); //We tell the camera to start the preview
 mCaptureSession.setRepeatingRequest(mPreviewRequest,
 mSessionCaptureCallback,
 null);
 
 } catch (CameraAccessException e) {
 e.printStackTrace();
 }
 }
 
 @Override
 public void onConfigureFailed(CameraCaptureSession session) {
 
 }
 }, null);
 8) CREAMOS LA SESION DE PREVIEW 2/2
  18. ANDROID CUSTOM CAMERA WITH CAMERA2 API 
 @Override
 public void

    onResume() {
 super.onResume();
 startBackgroundThread();
 
 // When the screen is turned off and turned back on, the SurfaceTexture is already
 // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
 // a camera and start preview from here (otherwise, we wait until the surface is ready in
 // the SurfaceTextureListener).
 if (mTextureView.isAvailable()) {
 openCamera(mTextureView.getWidth(), mTextureView.getHeight());
 } else {
 mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
 }
 }
 
 @Override
 public void onPause() {
 closeCamera();
 super.onPause();
 } 9) MANEJAMOS LOS RECURSOS DE LA CAMARA EN EL CICLO DE VIDA DEL FRAGMENT/ACTIVITY
  19. ANDROID CUSTOM CAMERA WITH CAMERA2 API // For still image

    captures, we use the largest available size.
 Size largest = Collections.max(
 Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
 new CompareSizesByArea()); mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
 ImageFormat.JPEG, /*maxImages*/2); 
 mImageReader.setOnImageAvailableListener(
 mOnImageAvailableListener, mBackgroundHandler); 1) CREAMOS UN IMAGE READER
  20. ANDROID CUSTOM CAMERA WITH CAMERA2 API private final ImageReader.OnImageAvailableListener mOnImageAvailableListener


    = new ImageReader.OnImageAvailableListener() {
 
 @Override
 public void onImageAvailable(ImageReader reader) {
 mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));
 }
 
 }; 2) CREAMOS UN LISTENER PARA EL IMAGE READER
  21. ANDROID CUSTOM CAMERA WITH CAMERA2 API private void lockFocus() {


    try { // Tell #mCaptureCallback to wait for the lock.
 mState = STATE_WAITING_LOCK;
 // This is how to tell the camera to lock focus.
 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
 CameraMetadata.CONTROL_AF_TRIGGER_START); //Notify capture session that the builder has change mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
 mBackgroundHandler);
 } catch (CameraAccessException e) {
 e.printStackTrace();
 }
 } 3) BLOQUEAMOS EL FOCO
  22. ANDROID CUSTOM CAMERA WITH CAMERA2 API private CameraCaptureSession.CaptureCallback mSessionCaptureCallback =

    new CameraCaptureSession.CaptureCallback() {
 
 
 private void process(CaptureResult result) {
 switch(mState) {
 case STATE_PREVIEW:
 // Do nothing
 break;
 case STATE_WAIT_LOCK:
 Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
 if(afState == CaptureRequest.CONTROL_AF_STATE_FOCUSED_LOCKED) {
 captureStillImage();
 }
 break;
 }
 }
 
 @Override
 public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber) {
 super.onCaptureStarted(session, request, timestamp, frameNumber);
 }
 
 @Override
 public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) {
 super.onCaptureProgressed(session, request, partialResult);
 }
 
 @Override
 public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
 super.onCaptureCompleted(session, request, result);
 process(result);
 }
 
 @Override
 public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) {
 super.onCaptureFailed(session, request, failure);
 }
 
 };
 4) MODIFICAMOS EL CALLBACK DE CAPTURA Y PROCESAMOS EL RESULTADO
  23. ANDROID CUSTOM CAMERA WITH CAMERA2 API 
 private void captureStillImage()

    {
 try { 
 CaptureRequest.Builder captureStillBuilder = mCameraDevice. createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); 
 captureStillBuilder.addTarget(mImageReader.getSurface());
 
 int rotation = getWindowManager().getDefaultDisplay().getRotation();
 captureStillBuilder.set(CaptureRequest.JPEG_ORIENTATION,
 ORIENTATIONS.get(rotation));
 
 CameraCaptureSession.CaptureCallback captureCallback =
 new CameraCaptureSession.CaptureCallback() {
 @Override
 public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { 
 super.onCaptureCompleted(session, request, result);
 
 unLockFocus();
 }
 };
 
 mCaptureSession.capture(
 captureStillBuilder.build(), captureCallback, null
 );
 
 } catch (CameraAccessException e) {
 e.printStackTrace();
 }
 } 5) PEDIMOS AL CAPTURE SESSION QUE HAGA LA CAPTURA
  24. ANDROID CUSTOM CAMERA WITH CAMERA2 API 
 private void unLockFocus()

    {
 try {
 
 mState = STATE_PREVIEW;
 
 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
 CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
 
 mCaptureSession.capture(mPreviewRequestBuilder.build(),
 mSessionCaptureCallback, mBackgroundHandler);
 
 } catch (CameraAccessException e) {
 e.printStackTrace();
 }
 } 7) DESBLOQUEAMOS EL FOCO
  25. ANDROID CUSTOM CAMERA WITH CAMERA2 API SOURCES ▸ DevBytes: Android

    L Developer Preview - Camera2 API ▸ Android Camera2 API by Huyen Tue Dao Android Camera2 API by Huyen Tue Dao ▸ Camera 2 API by Mobile Application Tutorials ▸ https://github.com/googlesamples/android- Camera2Basic ▸ developer.android.com