Android 5 (API 21, L) 新增加了Camera2,和之前的Camera使用起來較複雜,但是多了許多功能,支援RAW輸出、調整對焦模式、曝光模式等。
Camera2
首先先了解幾個重要的Class
- CameraManager : 管理攝影設備,主要功用是獲取所有攝影設備和打開指定的攝影鏡頭。
- CameraDevice : 攝影設備,可透過CameraManager.openCamera()來取得。
- CameraCaptureSession : 用於處理拍照及預覽工作。
- CaptureRequest :捕捉畫面請求,用來定義輸出緩衝區及顯示 (TextureView or SurfaceView)
我們在畫面中加入一個TextureView,用來預覽攝影鏡頭目前捕捉到的畫面。
並設定Listener監聽TextureView是否已設置好。
程式參數
MainActivity.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| private TextureView mTextureView; private Size mPreviewSize;
private CameraDevice mCameraDevice; private String mCameraId;
private ImageReader mImageReader; private CaptureRequest.Builder mCaptureRequestBuilder; private CaptureRequest mCaptureRequest; private CameraCaptureSession mPreviewSession;
private static String[] PERMISSIONS_STORAGE = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA };
private static final int RC_HANDLE_CAMERA_PERM = 2;
|
MainActivity: onCreate1 2
| mTextureView = findViewById(R.id.textureView); mTextureView.setSurfaceTextureListener(textureListener);
|
當TextureView設置好之後,再來設置並打開攝影鏡頭。
MainActivity.java1 2 3 4 5 6 7 8 9
| TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { setupCamera(width, height); openCamera(); } };
|
利用CamaraManager取得所有攝影鏡頭的資訊,此例子使用後置鏡頭,我們可根據CameraCharacteristics屬性略過前置鏡頭並開啟後置鏡頭。
由於攝影鏡頭支援眾多格式和大小,可由StreamConfigurationMap取得這些屬性。根據TextureView的長寬,設定攝影鏡頭的格式與大小。
MainActivity.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| private void setupCamera(int width, int height) { CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { String[] cameraList = manager.getCameraIdList(); for (String cameraId: cameraList) { CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) continue; StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); mPreviewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height); mCameraId = cameraId; break; } } catch (CameraAccessException e) { e.printStackTrace(); } }
|
MainActivity.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| private Size getOptimalSize(Size[] sizeMap, int width, int height) { List<Size> sizeList = new ArrayList<>(); for (Size option : sizeMap) { if (width > height) { if (option.getWidth() > width && option.getHeight() > height) { sizeList.add(option); } } else { if (option.getWidth() > height && option.getHeight() > width) { sizeList.add(option); } } } if (sizeList.size() > 0) { return Collections.min(sizeList, new Comparator<Size>() { @Override public int compare(Size lhs, Size rhs) { return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight()); } }); } return sizeMap[0]; }
|
根據mCameraId來決定要開啟的鏡頭,並設置CameraDevice.StateCallback監聽狀態。
MainActivity.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| private void openCamera() { CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, RC_HANDLE_CAMERA_PERM); return; } manager.openCamera(mCameraId, mStateCallback, null); } catch (CameraAccessException e) { e.printStackTrace(); } }
|
MainActivity.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) { mCameraDevice = camera; startPreview(); } @Override public void onDisconnected(@NonNull CameraDevice camera) { Log.e(TAG, "onDisconnected"); } @Override public void onError(@NonNull CameraDevice camera, int error) { Log.e(TAG, "onError: " + error); } };
|
設置預覽畫面,將捕捉到的畫面顯示於TextureView中。
MainActivity.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| private void startPreview() { SurfaceTexture mSurfaceTexture = mTextureView.getSurfaceTexture(); mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); Surface mSurface = new Surface(mSurfaceTexture); try { mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mCaptureRequestBuilder.addTarget(mSurface); mCameraDevice.createCaptureSession(Arrays.asList(mSurface),mCaptureCallback, null); } catch (CameraAccessException e) { e.printStackTrace(); } }
|
當預覽請求設置好之後,設定Repeat,讓畫面持續更新。
MainActivity.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| private final CameraCaptureSession.StateCallback mCaptureCallback = new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { try { mCaptureRequest = mCaptureRequestBuilder.build(); mPreviewSession = session; mPreviewSession.setRepeatingRequest(mCaptureRequest, null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession session) { } };
|
執行結果