在openCamera() 成功打开相机后,会通过CameraDevice.StateCallback回调接口onOpened()方法返回一个CameraDevice对象给应用层,而这个CameraDevice对象是一个CameraDeviceImpl,那么接下来的createCaptureSession就是调用它来实现的。
/frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@Override
520 public void createCaptureSession(List<Surface> outputs,
521 CameraCaptureSession.StateCallback callback, Handler handler)
522 throws CameraAccessException {
523 List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
524 for (Surface surface : outputs) {
525 outConfigurations.add(new OutputConfiguration(surface));
526 }
527 createCaptureSessionInternal(null, outConfigurations, callback,
528 checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
529 /*sessionParams*/ null);
}
第一个参数是一个范型为Surface的List,这里的Surface就是我们用来创建流的,一般如果没有特殊的要求,那我们只需要下两个Surface,一个提供预览,一个提供拍照,预览的Surface就是相机预览区域,buffer轮转时,预览区的buffer就是要从这个预览Surface当中获取的,这个Surface一定要正确,否则就会导致session创建失败,预览区就会黑屏了;而至于拍照Surface,我们一般使用ImageReader对象来获取,ImageReader是系统提供的一个类,它的创建过程已经为我们创建好了一个Surface,我们直接使用它来当作拍照Surface,当拍照成功后,我们就可以从ImageReader.OnImageAvailableListener内部类的onImageAvailable回调方法中获取到一个ImageReader对象,再调用getPlanes()获取到Plane数组,一般取第一个Plane,继续调用getBuffer()就可以获取到拍摄的照片的byte数组了
第二个参数callback的类型为frameworks\base\core\java\android\hardware\camera2\CameraCaptureSession.java类的内部类StateCallback,和openCamera一样,当session创建成功后,framework也会通过这个回调接口的onConfigured()方法返回一个CameraCaptureSession对象,而真正的实现是一个CameraCaptureSessionImpl对象,我们可以使用它来作很多的工作,比如断开session连接调用abortCaptures();拍照调用capture();预览调用setRepeatingRequest();停预览调用stopRepeating(),这里的设计和openCamera是完全一样的。
第三个参数Handler的作用和openCamera也一样,还是为了保证线程不发生切换,我们在应用进程的哪个工作线程中执行createCaptureSession,那么framework回调时也会通过这个handler把回调消息发送到当前handler线程的Looper循环上。
createCaptureSession()实际就是调用createCaptureSessionInternal()方法进一步处理的,这里的会把我们传入的surface列表进行一下转换,转换为OutputConfiguration对象,调用createCaptureSessionInternal方法时的第一个参数inputConfig一般为空,我们只需要关注outputConfig。
649 private void createCaptureSessionInternal(InputConfiguration inputConfig,
650 List<OutputConfiguration> outputConfigurations,
651 CameraCaptureSession.StateCallback callback, Executor executor,
652 int operatingMode, CaptureRequest sessionParams) throws CameraAccessException {
653 synchronized(mInterfaceLock) {
654 if (DEBUG) {
655 Log.d(TAG, "createCaptureSessionInternal");
656 }
657
658 checkIfCameraClosedOrInError();
659
660 boolean isConstrainedHighSpeed =
661 (operatingMode == ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE);
662 if (isConstrainedHighSpeed && inputConfig != null) {
663 throw new IllegalArgumentException("Constrained high speed session doesn't support"
664 + " input configuration yet.");
665 }
666
667 // Notify current session that it's going away, before starting camera operations
668 // After this call completes, the session is not allowed to call into CameraDeviceImpl
669 if (mCurrentSession != null) {
670 mCurrentSession.replaceSessionClose();
671 }
672
673 // TODO: dont block for this
674 boolean configureSuccess = true;
675 CameraAccessException pendingException = null;
676 Surface input = null;
677 try {
678 // configure streams and then block until IDLE
//执行surface配置, !!重要!!
679 configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,
680 operatingMode, sessionParams);
681 if (configureSuccess == true && inputConfig != null) {
682 input = mRemoteDevice.getInputSurface();
683 }
684 } catch (CameraAccessException e) {
685 configureSuccess = false;
686 pendingException = e;
687 input = null;
688 if (DEBUG) {
689 Log.v(TAG, "createCaptureSession - failed with exception ", e);
690 }
691 }
692
693 // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
694 CameraCaptureSessionCore newSession = null;
695 if (isConstrainedHighSpeed) {
696 ArrayList<Surface> surfaces = new ArrayList<>(outputConfigurations.size());
697 for (OutputConfiguration outConfig : outputConfigurations) {
698 surfaces.add(outConfig.getSurface());
699 }
700 StreamConfigurationMap config =
701 getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
702 SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config);
703
704 newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
705 callback, executor, this, mDeviceExecutor, configureSuccess,
706 mCharacteristics);
707 } else {
//创建CameraCaptureSessionImpl对象
708 newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
709 callback, executor, this, mDeviceExecutor, configureSuccess);
710 }
711
712 // TODO: wait until current session closes, then create the new session
713 mCurrentSession = newSession;
714
715 if (pendingException != null) {
716 throw pendingException;
717 }
718
719 mSessionStateCallback = mCurrentSession.getDeviceStateCallback();
720 }
721 }
/frameworks\base\core\java\android\hardware\camera2\impl\CameraCaptureSessionImpl.java
CameraCaptureSessionImpl构造函数调用mStateCallback.onConfigured(this)通知应用层,session已经创建成功,同时将当前的impl实现类对象返回给应用层。
下面看一下configureStreamsChecked方法的实现:
/**
382 * Attempt to configure the input and outputs; the device goes to idle and then configures the
383 * new input and outputs if possible.
384 *
385 * <p>The configuration may gracefully fail, if input configuration is not supported,
386 * if there are too many outputs, if the formats are not supported, or if the sizes for that
387 * format is not supported. In this case this function will return {@code false} and the
388 * unconfigured callback will be fired.</p>
389 *
390 * <p>If the configuration succeeds (with 1 or more outputs with or without an input),
391 * then the idle callback is fired. Unconfiguring the device always fires the idle callback.</p>
392 *
393 * @param inputConfig input configuration or {@code null} for no input
394 * @param outputs a list of one or more surfaces, or {@code null} to unconfigure
395 * @param operatingMode If the stream configuration is for a normal session,
396 * a constrained high speed session, or something else.
397 * @param sessionParams Session parameters.
398 * @return whether or not the configuration was successful
399 *
400 * @throws CameraAccessException if there were any unexpected problems during configuration
*/
public boolean configureStreamsChecked(InputConfiguration inputConfig,
403 List<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams)
404 throws CameraAccessException {
405 // Treat a null input the same an empty list
406 if (outputs == null) {
407 outputs = new ArrayList<OutputConfiguration>();
408 }
409 if (outputs.size() == 0 && inputConfig != null) {
410 throw new IllegalArgumentException("cannot configure an input stream without " +
411 "any output streams");
412 }
413
414 checkInputConfiguration(inputConfig);
415
416 boolean success = false;
417
418 synchronized(mInterfaceLock) {
419 checkIfCameraClosedOrInError();
420 // Streams to create
421 HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);
422 // Streams to delete
423 List<Integer> deleteList = new ArrayList<Integer>();
424
425 // Determine which streams need to be created, which to be deleted
426 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
427 int streamId = mConfiguredOutputs.keyAt(i);
428 OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i);
429
430 if (!outputs.contains(outConfig) || outConfig.isDeferredConfiguration()) {
431 // Always delete the deferred output configuration when the session
432 // is created, as the deferred output configuration doesn't have unique surface
433 // related identifies.
434 deleteList.add(streamId);
435 } else {
436 addSet.remove(outConfig); // Don't create a stream previously created
437 }
438 }
//往DeviceHandler的Looper循环上发一个CallOnBusy的消息,表示接下来要开始配置surface
//处理繁忙状态了,然后调用stopRepeating()停预览
440 mDeviceExecutor.execute(mCallOnBusy);
441 stopRepeating();
442
443 try {
444 waitUntilIdle();
//通知CameraServer进程中的binder服务端对象,开始配置
//mRemoteDevice实际上就是CameraDeviceClient对象
446 mRemoteDevice.beginConfigure();
447
448 // reconfigure the input stream if the input configuration is different.
449 InputConfiguration currentInputConfig = mConfiguredInput.getValue();
450 if (inputConfig != currentInputConfig &&
451 (inputConfig == null || !inputConfig.equals(currentInputConfig))) {
452 if (currentInputConfig != null) {
453 mRemoteDevice.deleteStream(mConfiguredInput.getKey());
454 mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
455 RE