在前面文章中已经介绍了如何使用 CameraView ,这是 Google 官方提供的库,并有相关 demo,因为 Android 的碎片化太严重,官方也是考虑到这些,才提供了CameraView 供大家学习和参考。
源码分析 源码结构 根据官方的说明:
API Level
Camera API
Preview View
9-13
Camera1
SurfaceView
14-20
Camera1
TextureView
21-23
Camera2
TextureView
24
Camera2
SurfaceView
具体的实现都在CameraView类 中。
类图 源码中涉及到的主要类之间的关系,是从最新的源码中查看到(跟官网的表格不太一样,最新的源码sdk最低版本为14):
Camera 区分:Android5.0(21)以下使用 Camera1,以上使用 Camera2
Preview View:Android6.0(23)以上使用SurfaceView(SurfaceView在Android7.0上增加了新特性(平移、旋转等)),这里应该是 Android7.0以上(>23)使用SurfaceView,其他都使用TextureView,最新的源码sdk最低版本要求14。
CameraView CameraView使用非常简单,在 CameraView 使用那篇文章已经做过详细说明。
初始化 1 2 3 4 5 6 7 8 9 10 <com.google.android.cameraview.CameraView android:id="@+id/camera" android:layout_width="match_parent" android:layout_height="wrap_content" android:keepScreenOn="true" android:adjustViewBounds="true" app:autoFocus="true" app:aspectRatio="4:3" app:facing="back" app:flash="auto" />
直接看下CameraView
的构造函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public CameraView (Context context, AttributeSet attrs, int defStyleAttr) { super (context, attrs, defStyleAttr); ... final PreviewImpl preview = createPreviewImpl(context); mCallbacks = new CallbackBridge (); if (Build.VERSION.SDK_INT < 21 ) { mImpl = new Camera1 (mCallbacks, preview); } else if (Build.VERSION.SDK_INT < 23 ) { mImpl = new Camera2 (mCallbacks, preview, context); } else { mImpl = new Camera2Api23 (mCallbacks, preview, context); } TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CameraView, defStyleAttr, R.style.Widget_CameraView); mAdjustViewBounds = a.getBoolean(R.styleable.CameraView_android_adjustViewBounds, false ); setFacing(a.getInt(R.styleable.CameraView_facing, FACING_BACK)); String aspectRatio = a.getString(R.styleable.CameraView_aspectRatio); if (aspectRatio != null ) { setAspectRatio(AspectRatio.parse(aspectRatio)); } else { setAspectRatio(Constants.DEFAULT_ASPECT_RATIO); } setAutoFocus(a.getBoolean(R.styleable.CameraView_autoFocus, true )); setFlash(a.getInt(R.styleable.CameraView_flash, Constants.FLASH_AUTO)); a.recycle(); mDisplayOrientationDetector = new DisplayOrientationDetector (context) { @Override public void onDisplayOrientationChanged (int displayOrientation) { mImpl.setDisplayOrientation(displayOrientation); } }; }
createPreviewImpl
实现:
1 2 3 4 5 6 7 8 9 private PreviewImpl createPreviewImpl (Context context) { PreviewImpl preview; if (Build.VERSION.SDK_INT >= 23 ) { preview = new SurfaceViewPreview (context, this ); } else { preview = new TextureViewPreview (context, this ); } return preview; }
这里主要代码角度看下CameraView版本选择策略,在最开始的已经说明。 Camera的选择:api <21的,使用Camera1,>=21使用 Camera2,这里完全没有问题。 Camera Preview的选择,存在几个疑问,api >=23使用SurfaceView,不过api 24的时候使用SurfaceView才是官方推荐的。
Starting in platform version N, SurfaceView’s window position is updated synchronously with other View rendering. This means that translating and scaling a SurfaceView on screen will not cause rendering artifacts. Such artifacts may occur on previous versions of the platform when its window is positioned asynchronously.
这里应该 api >23使用SurfaceView,其他情况使用TextureView,因为在工程中 定义了minSdkVersion = 14
,api 14-23使用TextureView,表格可以更新为:
API Level
Camera API
Preview View
14-20
Camera1
TextureView
21-23
Camera2
TextureView
24
Camera2
SurfaceView
Preview定义 PreviewImpl
封装了预览控件的操作方法,SurfaceViewPreview
和TextureViewPreview
分别对应SurfaceView
和TextureView
的PreviewImpl
实现。
SurfaceViewPreview 实现很简单,直接加载有SurfaceView
控件的布局,并封装了SurfaceHolder
操作
TextureViewPreview 直接加载有TextureView
控件布局,并监听TextureView.SurfaceTextureListener
。
Camera定义 CameraViewImpl
定义了相机的各种操作,Camera1
、Camera2
、Camera2Api23
都是CameraViewImpl
的具体实现。 看下它们的构造函数:Camera1
1 2 3 4 5 6 7 8 9 10 11 12 Camera1(Callback callback, PreviewImpl preview) { super (callback, preview); preview.setCallback(new PreviewImpl .Callback() { @Override public void onSurfaceChanged () { if (mCamera != null ) { setUpPreview(); adjustCameraParameters(); } } }); }
增加了 Callback,当SurfaceChanged的时候,设置预览和 Camera 参数。Camera2
1 2 3 4 5 6 7 8 9 10 Camera2(Callback callback, PreviewImpl preview, Context context) { super (callback, preview); mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); mPreview.setCallback(new PreviewImpl .Callback() { @Override public void onSurfaceChanged () { startCaptureSession(); } }); }
1.初始化了CameraManager
2.增加Callback,当SurfaceChanged的时候,对CaptureSession进行设置
Camera2Api23
继承了Camera2
,重用了Camera2
的构造。 Camera1和 Camera2的实现和使用流程,可以参考前面的几篇文章,接下来介绍CameraView
的几个重要方法的实现。
start 1 2 3 4 5 6 7 8 9 10 public void start () { if (!mImpl.start()) { Parcelable state = onSaveInstanceState(); mImpl = new Camera1 (mCallbacks, createPreviewImpl(getContext())); onRestoreInstanceState(state); mImpl.start(); } }
开启相机,首先判断是否可以开启,如果不能开启,默认会使用 Camera1。
Camera1.start 1 2 3 4 5 6 7 8 9 10 11 12 13 14 boolean start () { chooseCamera(); openCamera(); if (mPreview.isReady()) { setUpPreview(); } mShowingPreview = true ; mCamera.startPreview(); return true ; }
具体的流程,可以参考Camera1使用 ,这里介绍几个重要的方法。 1.chooseCamera
会遍历所有摄像头,然后根据CameraView
初始化时传入的值进行对比,默认是FACING_BACK
后置摄像头。 2.openCamera
方法,这里具体展开介绍下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 private void openCamera () { if (mCamera != null ) { releaseCamera(); } mCamera = Camera.open(mCameraId); mCameraParameters = mCamera.getParameters(); mPreviewSizes.clear(); for (Camera.Size size : mCameraParameters.getSupportedPreviewSizes()) { Log.d("DEBUG" , "###### SupportedPreviewSizes: width=" + size.width + ", height=" + size.height); mPreviewSizes.add(new Size (size.width, size.height)); } mPictureSizes.clear(); for (Camera.Size size : mCameraParameters.getSupportedPictureSizes()) { Log.d("DEBUG" , "###### SupportedPictureSizes: width=" + size.width + ", height=" + size.height); mPictureSizes.add(new Size (size.width, size.height)); } if (mAspectRatio == null ) { mAspectRatio = Constants.DEFAULT_ASPECT_RATIO; } adjustCameraParameters(); mCamera.setDisplayOrientation(calcDisplayOrientation(mDisplayOrientation)); mCallback.onCameraOpened(); }
3.setUpPreview
设置预览
1 2 3 4 5 6 7 8 9 10 11 void setUpPreview () { try { if (mPreview.getOutputClass() == SurfaceHolder.class) { mCamera.setPreviewDisplay(mPreview.getSurfaceHolder()); } else { mCamera.setPreviewTexture((SurfaceTexture) mPreview.getSurfaceTexture()); } } catch (IOException e) { throw new RuntimeException (e); } }
根据mPreview类型的不同,使用SurfaceView
预览或者TextureView
预览。 4.mCamera.startPreview()
开启预览
Camera2.start 1 2 3 4 5 6 7 8 9 10 11 12 13 boolean start () { if (!chooseCameraIdByFacing()) { return false ; } collectCameraInfo(); prepareImageReader(); startOpeningCamera(); return true ; }
具体流程可以参考Camera2使用 ,这里只介绍重要方法说明。 1.chooseCameraIdByFacing
遍历支持的摄像头列表,根据条件筛选获取到指定的摄像头,默认后置摄像头。 2.collectCameraInfo
方法,获取所有支持预览尺寸,和拍照尺寸,并获取支持的预览尺寸比例 3.prepareImageReader
方法,初始化ImageReader
1 2 3 4 5 6 7 8 9 private void prepareImageReader () { if (mImageReader != null ) { mImageReader.close(); } Size largest = mPictureSizes.sizes(mAspectRatio).last(); mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 2 ); mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, null ); }
设置输出格式为 JPEG,并增加ImageAvailableListener
回调监听。 4.startOpeningCamera
方法,打开摄像头,并设置CameraDevice.StateCallback
监听
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 private void startOpeningCamera () { try { mCameraManager.openCamera(mCameraId, mCameraDeviceCallback, null ); } catch (CameraAccessException e) { throw new RuntimeException ("Failed to open camera: " + mCameraId, e); } } private final CameraDevice.StateCallback mCameraDeviceCallback = new CameraDevice .StateCallback() { @Override public void onOpened (@NonNull CameraDevice camera) { mCamera = camera; mCallback.onCameraOpened(); startCaptureSession(); } @Override public void onClosed (@NonNull CameraDevice camera) { mCallback.onCameraClosed(); } @Override public void onDisconnected (@NonNull CameraDevice camera) { mCamera = null ; } @Override public void onError (@NonNull CameraDevice camera, int error) { Log.e(TAG, "onError: " + camera.getId() + " (" + error + ")" ); mCamera = null ; } };
监听相机打开后,开启预览画面,startCaptureSession
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void startCaptureSession () { if (!isCameraOpened() || !mPreview.isReady() || mImageReader == null ) { return ; } Size previewSize = chooseOptimalSize(); mPreview.setBufferSize(previewSize.getWidth(), previewSize.getHeight()); Surface surface = mPreview.getSurface(); try { mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mPreviewRequestBuilder.addTarget(surface); mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), mSessionCallback, null ); } catch (CameraAccessException e) { throw new RuntimeException ("Failed to start camera session" ); } }
监听CameraCaptureSession
状态的回调声明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 private final CameraCaptureSession.StateCallback mSessionCallback = new CameraCaptureSession .StateCallback() { @Override public void onConfigured (@NonNull CameraCaptureSession session) { if (mCamera == null ) { return ; } mCaptureSession = session; updateAutoFocus(); updateFlash(); try { mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, null ); } catch (CameraAccessException e) { Log.e(TAG, "Failed to start camera preview because it couldn't access camera" , e); } catch (IllegalStateException e) { Log.e(TAG, "Failed to start camera preview." , e); } } @Override public void onConfigureFailed (@NonNull CameraCaptureSession session) { Log.e(TAG, "Failed to configure capture session." ); } @Override public void onClosed (@NonNull CameraCaptureSession session) { if (mCaptureSession != null && mCaptureSession.equals(session)) { mCaptureSession = null ; } } };
takePicture 1 2 3 public void takePicture () { mImpl.takePicture(); }
根据 api 选择不同的实现
Camera1.takePicture 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void takePicture () { if (!isCameraOpened()) { throw new IllegalStateException ( "Camera is not ready. Call start() before takePicture()." ); } if (getAutoFocus()) { mCamera.cancelAutoFocus(); mCamera.autoFocus(new Camera .AutoFocusCallback() { @Override public void onAutoFocus (boolean success, Camera camera) { takePictureInternal(); } }); } else { takePictureInternal(); } }
真正执行拍照的方法takePictureInternal
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void takePictureInternal () { if (!isPictureCaptureInProgress.getAndSet(true )) { mCamera.takePicture(null , null , null , new Camera .PictureCallback() { @Override public void onPictureTaken (byte [] data, Camera camera) { isPictureCaptureInProgress.set(false ); mCallback.onPictureTaken(data); camera.cancelAutoFocus(); camera.startPreview(); } }); } }
takePicture
中增加的 jpeg 的PictureCallback
回调,返回的data通过callback回调到上层
Camera2.takePicture 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 void takePicture () { if (mAutoFocus) { lockFocus(); } else { captureStillPicture(); } } private void lockFocus () { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); try { mCaptureCallback.setState(PictureCaptureCallback.STATE_LOCKING); mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, null ); } catch (CameraAccessException e) { Log.e(TAG, "Failed to lock focus." , e); } } PictureCaptureCallback mCaptureCallback = new PictureCaptureCallback () { @Override public void onPrecaptureRequired () { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); setState(STATE_PRECAPTURE); try { mCaptureSession.capture(mPreviewRequestBuilder.build(), this , null ); mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE); } catch (CameraAccessException e) { Log.e(TAG, "Failed to run precapture sequence." , e); } } @Override public void onReady () { captureStillPicture(); } };
判断是否自动对焦,最后调用captureStillPicture
方法进行拍照请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 void captureStillPicture () { try { CaptureRequest.Builder captureRequestBuilder = mCamera.createCaptureRequest( CameraDevice.TEMPLATE_STILL_CAPTURE); captureRequestBuilder.addTarget(mImageReader.getSurface()); captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, mPreviewRequestBuilder.get(CaptureRequest.CONTROL_AF_MODE)); ... @SuppressWarnings("ConstantConditions") int sensorOrientation = mCameraCharacteristics.get( CameraCharacteristics.SENSOR_ORIENTATION); captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, (sensorOrientation + mDisplayOrientation * (mFacing == Constants.FACING_FRONT ? 1 : -1 ) + 360 ) % 360 ); mCaptureSession.stopRepeating(); mCaptureSession.capture(captureRequestBuilder.build(), new CameraCaptureSession .CaptureCallback() { @Override public void onCaptureCompleted (@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { unlockFocus(); } }, null ); } catch (CameraAccessException e) { Log.e(TAG, "Cannot capture a still picture." , e); } } void unlockFocus () { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); try { mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, null ); updateAutoFocus(); updateFlash(); mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE); mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, null ); mCaptureCallback.setState(PictureCaptureCallback.STATE_PREVIEW); } catch (CameraAccessException e) { Log.e(TAG, "Failed to restart camera preview." , e); } }
拍照后真正的数据回调,是在初始化 ImageReader 的OnImageAvailableListener
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader .OnImageAvailableListener() { @Override public void onImageAvailable (ImageReader reader) { try (Image image = reader.acquireNextImage()) { Image.Plane[] planes = image.getPlanes(); if (planes.length > 0 ) { ByteBuffer buffer = planes[0 ].getBuffer(); byte [] data = new byte [buffer.remaining()]; buffer.get(data); mCallback.onPictureTaken(data); } } } };
stop 如果不用拍照,或者退出应用,调用 stop 方法
1 2 3 public void stop () { mImpl.stop(); }
Camera1.stop 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void stop () { if (mCamera != null ) { mCamera.stopPreview(); } mShowingPreview = false ; releaseCamera(); } private void releaseCamera () { if (mCamera != null ) { mCamera.release(); mCamera = null ; mCallback.onCameraClosed(); } }
Camera2.stop 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void stop () { if (mCaptureSession != null ) { mCaptureSession.close(); mCaptureSession = null ; } if (mCamera != null ) { mCamera.close(); mCamera = null ; } if (mImageReader != null ) { mImageReader.close(); mImageReader = null ; } }
CameraView源码相关的分析就结束了,该库很好的封装了 Camera1和 Camera2的使用,本篇文章对其中的重点方法进行了分析,讲解了它的实现原理,希望对大家有所帮助。
参考: