package org.opencv.android; import android.annotation.TargetApi; import android.content.Context; import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.params.StreamConfigurationMap; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; import android.util.Size; import android.view.Surface; import java.util.Arrays; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @TargetApi(21) public class Camera2Renderer extends CameraGLRendererBase { protected final String LOGTAG = "Camera2Renderer"; private CameraDevice mCameraDevice; private CameraCaptureSession mCaptureSession; private CaptureRequest.Builder mPreviewRequestBuilder; private String mCameraID; private Size mPreviewSize = new Size(-1, -1); private HandlerThread mBackgroundThread; private Handler mBackgroundHandler; private Semaphore mCameraOpenCloseLock = new Semaphore(1); Camera2Renderer(CameraGLSurfaceView view) { super(view); } @Override protected void doStart() { Log.d(LOGTAG, "doStart"); startBackgroundThread(); super.doStart(); } @Override protected void doStop() { Log.d(LOGTAG, "doStop"); super.doStop(); stopBackgroundThread(); } boolean cacPreviewSize(final int width, final int height) { Log.i(LOGTAG, "cacPreviewSize: "+width+"x"+height); if(mCameraID == null) { Log.e(LOGTAG, "Camera isn't initialized!"); return false; } CameraManager manager = (CameraManager) mView.getContext() .getSystemService(Context.CAMERA_SERVICE); try { CameraCharacteristics characteristics = manager .getCameraCharacteristics(mCameraID); StreamConfigurationMap map = characteristics .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); int bestWidth = 0, bestHeight = 0; float aspect = (float)width / height; for (Size psize : map.getOutputSizes(SurfaceTexture.class)) { int w = psize.getWidth(), h = psize.getHeight(); Log.d(LOGTAG, "trying size: "+w+"x"+h); if ( width >= w && height >= h && bestWidth <= w && bestHeight <= h && Math.abs(aspect - (float)w/h) < 0.2 ) { bestWidth = w; bestHeight = h; } } Log.i(LOGTAG, "best size: "+bestWidth+"x"+bestHeight); if( bestWidth == 0 || bestHeight == 0 || mPreviewSize.getWidth() == bestWidth && mPreviewSize.getHeight() == bestHeight ) return false; else { mPreviewSize = new Size(bestWidth, bestHeight); return true; } } catch (CameraAccessException e) { Log.e(LOGTAG, "cacPreviewSize - Camera Access Exception"); } catch (IllegalArgumentException e) { Log.e(LOGTAG, "cacPreviewSize - Illegal Argument Exception"); } catch (SecurityException e) { Log.e(LOGTAG, "cacPreviewSize - Security Exception"); } return false; } @Override protected void openCamera(int id) { Log.i(LOGTAG, "openCamera"); CameraManager manager = (CameraManager) mView.getContext().getSystemService(Context.CAMERA_SERVICE); try { String camList[] = manager.getCameraIdList(); if(camList.length == 0) { Log.e(LOGTAG, "Error: camera isn't detected."); return; } if(id == CameraBridgeViewBase.CAMERA_ID_ANY) { mCameraID = camList[0]; } else { for (String cameraID : camList) { CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraID); if( id == CameraBridgeViewBase.CAMERA_ID_BACK && characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK || id == CameraBridgeViewBase.CAMERA_ID_FRONT && characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) { mCameraID = cameraID; break; } } } if(mCameraID != null) { if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { throw new RuntimeException( "Time out waiting to lock camera opening."); } Log.i(LOGTAG, "Opening camera: " + mCameraID); manager.openCamera(mCameraID, mStateCallback, mBackgroundHandler); } } catch (CameraAccessException e) { Log.e(LOGTAG, "OpenCamera - Camera Access Exception"); } catch (IllegalArgumentException e) { Log.e(LOGTAG, "OpenCamera - Illegal Argument Exception"); } catch (SecurityException e) { Log.e(LOGTAG, "OpenCamera - Security Exception"); } catch (InterruptedException e) { Log.e(LOGTAG, "OpenCamera - Interrupted Exception"); } } @Override protected void closeCamera() { Log.i(LOGTAG, "closeCamera"); try { mCameraOpenCloseLock.acquire(); if (null != mCaptureSession) { mCaptureSession.close(); mCaptureSession = null; } if (null != mCameraDevice) { mCameraDevice.close(); mCameraDevice = null; } } catch (InterruptedException e) { throw new RuntimeException("Interrupted while trying to lock camera closing.", e); } finally { mCameraOpenCloseLock.release(); } } private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice cameraDevice) { mCameraDevice = cameraDevice; mCameraOpenCloseLock.release(); createCameraPreviewSession(); } @Override public void onDisconnected(CameraDevice cameraDevice) { cameraDevice.close(); mCameraDevice = null; mCameraOpenCloseLock.release(); } @Override public void onError(CameraDevice cameraDevice, int error) { cameraDevice.close(); mCameraDevice = null; mCameraOpenCloseLock.release(); } }; private void createCameraPreviewSession() { int w=mPreviewSize.getWidth(), h=mPreviewSize.getHeight(); Log.i(LOGTAG, "createCameraPreviewSession("+w+"x"+h+")"); if(w<0 || h<0) return; try { mCameraOpenCloseLock.acquire(); if (null == mCameraDevice) { mCameraOpenCloseLock.release(); Log.e(LOGTAG, "createCameraPreviewSession: camera isn't opened"); return; } if (null != mCaptureSession) { mCameraOpenCloseLock.release(); Log.e(LOGTAG, "createCameraPreviewSession: mCaptureSession is already started"); return; } if(null == mSTexture) { mCameraOpenCloseLock.release(); Log.e(LOGTAG, "createCameraPreviewSession: preview SurfaceTexture is null"); return; } mSTexture.setDefaultBufferSize(w, h); Surface surface = new Surface(mSTexture); mPreviewRequestBuilder = mCameraDevice .createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mPreviewRequestBuilder.addTarget(surface); mCameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() { @Override public void onConfigured( CameraCaptureSession cameraCaptureSession) { mCaptureSession = cameraCaptureSession; try { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, mBackgroundHandler); Log.i(LOGTAG, "CameraPreviewSession has been started"); } catch (CameraAccessException e) { Log.e(LOGTAG, "createCaptureSession failed"); } mCameraOpenCloseLock.release(); } @Override public void onConfigureFailed( CameraCaptureSession cameraCaptureSession) { Log.e(LOGTAG, "createCameraPreviewSession failed"); mCameraOpenCloseLock.release(); } }, mBackgroundHandler); } catch (CameraAccessException e) { Log.e(LOGTAG, "createCameraPreviewSession"); } catch (InterruptedException e) { throw new RuntimeException( "Interrupted while createCameraPreviewSession", e); } finally { //mCameraOpenCloseLock.release(); } } private void startBackgroundThread() { Log.i(LOGTAG, "startBackgroundThread"); stopBackgroundThread(); mBackgroundThread = new HandlerThread("CameraBackground"); mBackgroundThread.start(); mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); } private void stopBackgroundThread() { Log.i(LOGTAG, "stopBackgroundThread"); if(mBackgroundThread == null) return; mBackgroundThread.quitSafely(); try { mBackgroundThread.join(); mBackgroundThread = null; mBackgroundHandler = null; } catch (InterruptedException e) { Log.e(LOGTAG, "stopBackgroundThread"); } } @Override protected void setCameraPreviewSize(int width, int height) { Log.i(LOGTAG, "setCameraPreviewSize("+width+"x"+height+")"); if(mMaxCameraWidth > 0 && mMaxCameraWidth < width) width = mMaxCameraWidth; if(mMaxCameraHeight > 0 && mMaxCameraHeight < height) height = mMaxCameraHeight; try { mCameraOpenCloseLock.acquire(); boolean needReconfig = cacPreviewSize(width, height); mCameraWidth = mPreviewSize.getWidth(); mCameraHeight = mPreviewSize.getHeight(); if( !needReconfig ) { mCameraOpenCloseLock.release(); return; } if (null != mCaptureSession) { Log.d(LOGTAG, "closing existing previewSession"); mCaptureSession.close(); mCaptureSession = null; } mCameraOpenCloseLock.release(); createCameraPreviewSession(); } catch (InterruptedException e) { mCameraOpenCloseLock.release(); throw new RuntimeException("Interrupted while setCameraPreviewSize.", e); } } }