一步一步解析google camera2 demo(二)

目錄:
一、Camera2關鍵類解析
二、打開相機
三、預覽
四、拍照

二、打開相機

1、用戶打開相機過程中,首先會根據Fragment的聲明週期進行下述調用。

Fragment聲明週期

2、前面三個都是一些初始化動作,這裏主要關注onResume()函數:

@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);
	}
}

3、surface is ready後會觸發SurfaceTextureListener的對應回調onSurfaceTextureAvailable():

/**
 * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a
 * {@link TextureView}.
 */
private final TextureView.SurfaceTextureListener mSurfaceTextureListener
		= new TextureView.SurfaceTextureListener() {

	@Override
	public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
		openCamera(width, height);
	}

	@Override
	public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
		configureTransform(width, height);
	}

	@Override
	public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
		return true;
	}

	@Override
	public void onSurfaceTextureUpdated(SurfaceTexture texture) {
	}

};

4、調用openCamera(),並傳入surface的size作爲參數,整體流程分爲三步:
(1) 權限檢查
(2) 配置Camera的Output
(3) 獲取CameraManager對象,並打開指定Camera設備

/**
 * Opens the camera specified by {@link Camera2BasicFragment#mCameraId}.
 */
private void openCamera(int width, int height) {
	// check permission of opening camera
	if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
			!= PackageManager.PERMISSION_GRANTED) {
		requestCameraPermission();
		return;
	}
	setUpCameraOutputs(width, height);
	configureTransform(width, height);
	Activity activity = getActivity();
	CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
	try {
		if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
			throw new RuntimeException("Time out waiting to lock camera opening.");
		}
		manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
	} catch (CameraAccessException e) {
		e.printStackTrace();
	} catch (InterruptedException e) {
		throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
	}
}

主要關注如何配置Camera的output,也就是setUpCameraOutputs()的實現:

private void setUpCameraOutputs(int width, int height) {
	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;
			}

			// Get available stream configurations to set up Surfaces
			StreamConfigurationMap map = characteristics.get(
					CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

			// 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);

			// 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);

			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());
			} 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;
			return;
		}
	} catch (CameraAccessException e) {
			...
	}
}

這個函數的註釋比較詳細,列下關鍵步驟:
(1) 遍歷所有可以拿到的Camera Id,一般情況下只能拿到後置和前置,代碼裏面continue了前置,只open後置攝像頭。
(2) 通過CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP拿到支持的surface的配置信息,拿到jpeg(即拍照)的最大size,用於初始化ImageReader。
(3) 通過對比sensor的rotation以及display的rotation來決定對預覽size的旋轉。

最後就是調用manager.openCamera()來打開指定的camera 設備。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章