上一次我们详细分析了openCamera启动过程的源码,从CameraServer进程创建了很多对象,比如CameraDeviceClient、Camera3Device、FrameProcessorBase,而真正打开相机还是在驱动层中上电后才完成的,有时候真想把公司的系统组件和芯片组件的源码拷回来,这样就能看到完整的过程了,但是还是不敢,哈哈哈!华为手机是仿照高通,和CameraServer进程交互的是CameraDaemon进程,HAL、算法的代码都是执行在CameraDaemon进程当中的,而这部分每个手机厂商都不一样,因为是手机厂商自定义的代码,所以一般都是在系统源码中vendor目录下的,而且是在芯片组件当中,这里没有代码,这部分也就无法分析了。
之前我们也说过,相机最重要的四个节点就是openCamera、createCaptureSession、preview、capture,我们这节就来看一下createCaptureSession过程。在之前openCamera过程的源码分析中,我们最后说到,当成功打开相机后,会通过CameraDevice.StateCallback回调接口的public abstract void onOpened(@NonNull CameraDevice camera)方法返回一个CameraDevice对象给我们应用层,而这个CameraDevice对象真正是一个CameraDeviceImpl,那么接下来的createCaptureSession就是调用它来实现的,我们就来看一下frameworks\base\core\java\android\hardware\camera2\impl\CameraDeviceImpl.java类的createCaptureSession方法是如何实现的,源码如下:
@Override
public void createCaptureSession(List<Surface> outputs,
CameraCaptureSession.StateCallback callback, Handler handler)
throws CameraAccessException {
List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
createCaptureSessionInternal(null, outConfigurations, callback, handler,
/*operatingMode*/ICameraDeviceUser.NORMAL_MODE);
}
这里我们来看一下该方法的几个参数,第一个参数是一个范型为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也会通过这个回调接口的public abstract void onConfigured(@NonNull CameraCaptureSession session)方法返回一个CameraCaptureSession对象给我们,而真正的实现是一个CameraCaptureSessionImpl对象,我们可以使用它来作很多的工作,比如断开session连接调用abortCaptures();拍照调用capture()方法;下预览调用setRepeatingRequest;停预览调用stopRepeating(),这里的设计和openCamera是完全一样的。第三个参数Handler的作用和openCamera也一样,还是为了保证线程不发生切换,我们在应用进程的哪个工作线程中执行createCaptureSession,那么framework回调我们时,也会通过这个handler把回调消息发送到当前handler线程的Looper循环上。好了,参数分析完了,我们继续往下看代码,它实际就是调用createCaptureSessionInternal方法进一步处理的,这里的会把我们传入的surface列表进行一下转换,转换为OutputConfiguration对象,调用createCaptureSessionInternal方法时的第一个参数inputConfig一般为空,我们只关注outputConfig。createCaptureSessionInternal方法的源码如下:
private void createCaptureSessionInternal(InputConfiguration inputConfig,
List<OutputConfiguration> outputConfigurations,
CameraCaptureSession.StateCallback callback, Handler handler,
int operatingMode) throws CameraAccessException {
synchronized (mInterfaceLock) {
if (DEBUG) {
Log.d(TAG, "createCaptureSessionInternal");
}
checkIfCameraClosedOrInError();
boolean isConstrainedHighSpeed =
(operatingMode == ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE);
if (isConstrainedHighSpeed && inputConfig != null) {
throw new IllegalArgumentException("Constrained high speed session doesn't support"
+ " input configuration yet.");
}
// Notify current session that it's going away, before starting camera operations
// After this call completes, the session is not allowed to call into CameraDeviceImpl
if (mCurrentSession != null) {
mCurrentSession.replaceSessionClose();
}
// TODO: dont block for this
boolean configureSuccess = true;
CameraAccessException pendingException = null;
Surface input = null;
try {
// configure streams and then block until IDLE
configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,
operatingMode);
if (configureSuccess == true && inputConfig != null) {
input = mRemoteDevice.getInputSurface();
}
} catch (CameraAccessException e) {
configureSuccess = false;
pendingException = e;
input = null;
if (DEBUG) {
Log.v(TAG, "createCaptureSession - failed with exception ", e);
}
}
// Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
CameraCaptureSessionCore newSession = null;
if (isConstrainedHighSpeed) {
newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
callback, handler, this, mDeviceHandler, configureSuccess,
mCharacteristics);
} else {
newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
callback, handler, this, mDeviceHandler,
configureSuccess);
}
// TODO: wait until current session closes, then create the new session
mCurrentSession = newSession;
if (pendingException != null) {
throw pendingException;
}
mSessionStateCallback = mCurrentSession.getDeviceStateCallback();
}
}
这个方法的作用就是配置surface了。该方法中最重要的就是configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations, operatingMode)这句了,它会执行surface配置,如果配置成功,则configureSuccess值为true,否则为false,接下来会创建session的实现类对象,一般是执行else分支,创建CameraCaptureSessionImpl对象,frameworks\base\core\java\android\hardware\camera2\impl\CameraCaptureSessionImpl.java类的构造方法的源码如下:
CameraCaptureSessionImpl(int id, Surface input,
CameraCaptureSession.StateCallback callback, Handler stateHandler,
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
Handler deviceStateHandler, boolean configureSuccess) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
mId = id;
mIdString = String.format("Session %d: ", mId);
mInput = input;
mStateHandler = checkHandler(stateHandler);
mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null");
mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
/*
* Use the same handler as the device's StateCallback for all the internal coming events
*
* This ensures total ordering between CameraDevice.StateCallback and
* CameraDeviceImpl.CaptureCallback events.
*/
mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
/*name*/"seq");
mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(),
/*name*/"idle");
mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
/*name*/"abort");
// CameraDevice should call configureOutputs and have it finish before constructing us
if (configureSuccess) {
mStateCallback.onConfigured(this);
if (DEBUG) Log.v(TAG, mIdString + "Created session successfully");
mConfigureSuccess = true;
} else {
mStateCallback.onConfigureFailed(this);
mClosed = true; // do not fire any other callbacks, do not allow any other work
Log.e(TAG, mIdString + "Failed to create capture session; configuration failed");
mConfigureSuccess = false;
}
}
可以看到,在它的构造方法中,就是通过调用mStateCallback.onConfigured(this)通过应用层,session已经创建成功,同时将当前的impl实现类对象返回给应用层。好了,我们继续看一下configureStreamsChecked方法的实现,来学习一下framework是如何配置surface的。configureStreamsChecked方法的源码如下:
public boolean configureStreamsChecked(InputConfiguration inputConfig,
List<OutputConfiguration> outputs, int operatingMode)
throws CameraAccessException {
// Treat a null input the same an empty list
if (outputs == null) {
outputs = new ArrayList<OutputConfiguration>();
}
if (outputs.size() == 0 && inputConfig != null) {
throw new IllegalArgumentException("cannot configure an input stream without " +
"any output streams");
}
checkInputConfiguration(inputConfig);
boolean success = false;
synchronized (mInterfaceLock) {
checkIfCameraClosedOrInError();
// Streams to create
HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);
// Streams to delete
List<Integer> deleteList = new ArrayList<Integer>();
// Determine which streams need to be created, which to be deleted
for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
int streamId = mConfiguredOutputs.keyAt(i);
OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i);
if (!outputs.contains(outConfig) || outConfig.isDeferredConfiguration()) {
// Always delete the deferred output configuration when the session
// is created, as the deferred output configuration doesn't have unique surface
// related identifies.
deleteList.add(streamId);
} else {
addSet.remove(outConfig); // Don't create a stream previously created
}
}
mDeviceHandler.post(mCallOnBusy);
stopRepeating();
try {
waitUntilIdle();
mRemoteDevice.beginConfigure();
// reconfigure the input stream if the input c