最近一段时间在学习Camera2,记录一下。
官方demo
demo很简单,CameraActivity中加入了一个fragment,AutoFitTextureView提供了一个方法用来设置textureView的宽高,而我们主要要分析Camera2BasicFragment的预览拍照流程
1.进入相机,获取TextureView对象,然后开启一个后台线程处理相机数据
2.判断TextureView是否有效,有效就直接openCamera(),无效就加入一个监听SufaceTextureListener,通过回调确保surfaceTexture有效,然后同样openCamera()。
3.设置相机特性--setUpCameraOutputs(),设置图片存储监听OnImageAvaliableListener,拍照图片有效会通知ImageSaver线程保存图片,设置AE,AF等
4.设置矩阵变换 configureTransform()
5.获取CameraManager对象,然后真正打开相机
manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
其中mStateCallBack是相机状态回调。
6.打开相机成功的话,获取CameraDevice,然后创建会话--createCameraPreviewSession(),
通过mTextureView获取SurfaceTexture,通过SurafceTexture获得Surface对象,在CaptureRequest.Builder中添加Surface
然后创建会话,获得CaptureRequest对象,通过CaptureRequest发送重复请求捕捉画面,开启预览。
7.拍照流程:首先有个入口,拍照按钮点击事件,触发takePicture(),takePicture中实现了lockFocus()锁住焦点。
8.lockFocus():设置了一个等待锁定的状态,发送一次请求,加入了一个回调CaptureCallback,这一步还没有进行拍照。
9.在CaptureCallback的process()中,状态切换为STATE_WAITING_LOCK,进行拍照,当然不一定就是该状态下进行拍照,还要对AE,AF的状态进行判断,最后不管哪个状态下,都会调用captureStillPicture()进行拍照。
10.captureStillPicture() 设置拍照捕捉请求,设置成像方向与预览方向一致,中断停止预览的重复请求,最终进行拍照,拍照数据会由imageSaver处理,保存到文件,然后通过CameraCaptureSession.CaptureCallback回调解除锁定,回复预览界面
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | @Override public void onViewCreated(final View view, Bundle savedInstanceState) { view.findViewById(R.id.picture).setOnClickListener(this); view.findViewById(R.id.info).setOnClickListener(this); mTextureView = (AutoFitTextureView) view.findViewById(R.id.texture);//获取mTextureView } ... @Override public void onResume() { super.onResume(); startBackgroundThread();//为相机开启了一个后台线程,这个进程用于后台执行相关的工作 if (mTextureView.isAvailable()) {//mTextureView已经创建,SurfaceTexture已经有效,则直接openCamera,用于屏幕熄灭等情况,这时onSurfaceTextureAvailable不会回调。 openCamera(mTextureView.getWidth(), mTextureView.getHeight()); } else {//SurfaceTexture处于无效状态中,则通过SurfaceTextureListener确保surface准备好。 mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);//设置mTextureView回调 } } ... private final TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {//TextureView回调 @Override public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { openCamera(width, height);//SurfaceTexture有效即可openCamera,宽高是控件宽高 } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { configureTransform(width, height);//配置transformation,主要是矩阵旋转相关 } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture texture) { } }; ... private void setUpCameraOutputs(int width, int height) {////包括对相机设备的选择,ImageReader的初始化和参数、回调设置。设置显示的转化矩阵,即将预览的图片调整至显示图层的大小。 Activity activity = getActivity(); CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); try { for (String cameraId : manager.getCameraIdList()) {//获取摄像头可用列表 CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);////获取相机的特性 // We don't use a front facing camera in this sample. Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);// 不使用前置摄像头 if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) { continue; } StreamConfigurationMap map = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); if (map == null) { continue; } // 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);//设置ImageReader接收的图片格式,以及允许接收的最大图片数目 mImageReader.setOnImageAvailableListener( mOnImageAvailableListener, mBackgroundHandler);//设置图片存储的监听,但在创建会话,调用capture后才能有数据 // Find out if we need to swap dimension to get the preview size relative to sensor // coordinate. int displayRotation = activity.getWindowManager().getDefaultDisplay().getRotation();//获取显示方向 //noinspection ConstantConditions mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);//获取sensor方向 boolean swappedDimensions = false; switch (displayRotation) { case Surface.ROTATION_0: case Surface.ROTATION_180: if (mSensorOrientation == 90 || mSensorOrientation == 270) { swappedDimensions = true; } break; case Surface.ROTATION_90: case Surface.ROTATION_270: if (mSensorOrientation == 0 || mSensorOrientation == 180) { swappedDimensions = true; } break; default: Log.e(TAG, "Display rotation is invalid: " + displayRotation); } Point displaySize = new Point(); activity.getWindowManager().getDefaultDisplay().getSize(displaySize); int rotatedPreviewWidth = width; int rotatedPreviewHeight = height; int maxPreviewWidth = displaySize.x; int maxPreviewHeight = displaySize.y; if (swappedDimensions) {//横竖屏交换尺寸 rotatedPreviewWidth = height; rotatedPreviewHeight = width; maxPreviewWidth = displaySize.y; maxPreviewHeight = displaySize.x; } if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { maxPreviewWidth = MAX_PREVIEW_WIDTH; } if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { maxPreviewHeight = MAX_PREVIEW_HEIGHT; } // Danger, W.R.! Attempting to use too large a preview size could exceed the camera // bus' bandwidth limitation, resulting in gorgeous previews but the storage of // garbage capture data. mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, maxPreviewHeight, largest);//获取最优的预览分辨率 // We fit the aspect ratio of TextureView to the size of preview we picked. int orientation = getResources().getConfiguration().orientation; if (orientation == Configuration.ORIENTATION_LANDSCAPE) { mTextureView.setAspectRatio( mPreviewSize.getWidth(), mPreviewSize.getHeight()); //设置TextureView预览分辨率。 } else { mTextureView.setAspectRatio( mPreviewSize.getHeight(), mPreviewSize.getWidth()); } // Check if the flash is supported. //设置闪光灯 Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); mFlashSupported = available == null ? false : available; mCameraId = cameraId;//获取当前ID return; } } catch (CameraAccessException e) { e.printStackTrace(); } catch (NullPointerException e) { // Currently an NPE is thrown when the Camera2API is used but not supported on the // device this code runs. ErrorDialog.newInstance(getString(R.string.camera_error)) .show(getChildFragmentManager(), FRAGMENT_DIALOG); } } ... private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) {//图片有效回调 mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));//通知ImageSaver线程保存图片,//reader.acquireNextImage()获取图片image } }; ... private void configureTransform(int viewWidth, int viewHeight) {//配置transformation,主要是矩阵旋转相关 Activity activity = getActivity(); if (null == mTextureView || null == mPreviewSize || null == activity) { return; } int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); Matrix matrix = new Matrix(); RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth()); float centerX = viewRect.centerX(); float centerY = viewRect.centerY(); if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) { bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); float scale = Math.max( (float) viewHeight / mPreviewSize.getHeight(), (float) viewWidth / mPreviewSize.getWidth()); matrix.postScale(scale, scale, centerX, centerY); matrix.postRotate(90 * (rotation - 2), centerX, centerY); } else if (Surface.ROTATION_180 == rotation) { matrix.postRotate(180, centerX, centerY); } mTextureView.setTransform(matrix);//设置mTextureView的transformation } ... private void createCameraPreviewSession() { try { SurfaceTexture texture = mTextureView.getSurfaceTexture();//通过mTextureView获取SurfaceTexture。 assert texture != null; // We configure the size of default buffer to be the size of camera preview we want. texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());//设置SurfaceTexture大小,预览尺寸 // This is the output Surface we need to start preview. Surface surface = new Surface(texture);//通过SurfaceTexture创建Surface来预览。 // We set up a CaptureRequest.Builder with the output Surface. mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);//创建TEMPLATE_PREVIEW预览CaptureRequest.Builder mPreviewRequestBuilder.addTarget(surface);//CaptureRequest.Builder中添加Surface,即mTextureView获取创建的Surface // Here, we create a CameraCaptureSession for camera preview. mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {//创建会话 @Override public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {//创建会话成功 // The camera is already closed if (null == mCameraDevice) { return; } // When the session is ready, we start displaying the preview. mCaptureSession = cameraCaptureSession;//从onConfigured参数获取mCaptureSession try { // Auto focus should be continuous for camera preview. mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);//设置AF自动对焦模式 // Flash is automatically enabled when necessary. setAutoFlash(mPreviewRequestBuilder); // Finally, we start displaying the camera preview. mPreviewRequest = mPreviewRequestBuilder.build();//转换为CaptureRequest mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);//设置预览,setRepeatingRequest不断的重复mPreviewRequest请求捕捉画面,常用于预览或者连拍场景。 } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(//创建会话失败 @NonNull CameraCaptureSession cameraCaptureSession) { showToast("Failed"); } }, null ); } catch (CameraAccessException e) { e.printStackTrace(); } } ... private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {//预览回调 private void process(CaptureResult result) { switch (mState) { case STATE_PREVIEW: {//预览状态,则什么都不做 // We have nothing to do when the camera preview is working normally. break; } case STATE_WAITING_LOCK: {//等待焦点被锁时,由设置拍照流时设置的STATE_WAITING_LOCK Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); if (afState == null) { captureStillPicture();//进行拍照 } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) { // CONTROL_AE_STATE can be null on some devices Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { mState = STATE_PICTURE_TAKEN; captureStillPicture(); } else { runPrecaptureSequence(); } } break; } case STATE_WAITING_PRECAPTURE: { // CONTROL_AE_STATE can be null on some devices Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE || aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) { mState = STATE_WAITING_NON_PRECAPTURE; } break; } case STATE_WAITING_NON_PRECAPTURE: { // CONTROL_AE_STATE can be null on some devices Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) { mState = STATE_PICTURE_TAKEN; captureStillPicture(); } break; } } } ... private void captureStillPicture() {//进行拍照 try { final Activity activity = getActivity(); if (null == activity || null == mCameraDevice) { return; } // This is the CaptureRequest.Builder that we use to take a picture. final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);//设置TEMPLATE_STILL_CAPTURE拍照CaptureRequest.Builder captureBuilder.addTarget(mImageReader.getSurface());//添加拍照mImageReader为Surface // Use the same AE and AF modes as the preview.//设置AF和AE captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); setAutoFlash(captureBuilder); // Orientation 设置成像方向 int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));//设置图片方向 CameraCaptureSession.CaptureCallback CaptureCallback = new CameraCaptureSession.CaptureCallback() {//拍照流程执行完成回调 @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { showToast("Saved: " + mFile);//提示拍照图片已经保存 Log.d(TAG, mFile.toString()); unlockFocus();//释放焦点锁,重新开启预览。 } }; mCaptureSession.stopRepeating();//停止预览,停止任何一个正常进行的重复请求。 mCaptureSession.abortCaptures();//中断Capture,尽可能快的取消当前队列中或正在处理中的所有捕捉请求。 mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);//重新Capture进行拍照,这时mImageReader的回调会执行并保存图片 } catch (CameraAccessException e) { e.printStackTrace(); } } |