我们使用TextureView显示相机预览数据,Camera2的预览和拍照数据都是使用CameraCaptureSession会话来请求的
private void startPreview() {
SurfaceTexture mSurfaceTexture = mTextureView.getSurfaceTexture();
mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
//通过view创建surface对象
Surface previewSurface = new Surface(mSurfaceTexture);
try {
mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
//绑定请求和surface
mCaptureRequestBuilder.addTarget(previewSurface);
//预览数据会同时输出到previewSurface和mImageReader
mCameraDevice.createCaptureSession(Arrays.asList(previewSurface, mImageReader.getSurface()), new
CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
try {
//创建请求
mCaptureRequest = mCaptureRequestBuilder.build();
//保存相机会话对象
mCameraCaptureSession = session;
//开始预览
mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, null, mCameraHandler);
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
}
}, mCameraHandler);
}
}
createCaptureSession阶段
当成功打开相机后,会通过CameraDevice.StateCallback回调接口的onOpened(@NonNull CameraDevice camera)方法返回一个CameraDevice对象给我们应用层,而这个CameraDevice对象真正是一个CameraDeviceImpl,那么接下来的createCaptureSession就是调用它来实现的。
frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
public void createCaptureSession(List<Surface> outputs,
CameraCaptureSession.StateCallback callback, Handler handler) {
List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
createCaptureSessionInternal(null, outConfigurations, callback,
checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
/*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的类型为CameraCaptureSession.java类的内部类StateCallback,和openCamera一样,当session创建成功后,framework也会通过这个回调接口的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。
frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
private void createCaptureSessionInternal(InputConfiguration inputConfig,
List<OutputConfiguration> outputConfigurations,
CameraCaptureSession.StateCallback callback, Executor executor,
int operatingMode, CaptureRequest sessionParams) {
synchronized(mInterfaceLock) {
boolean isConstrainedHighSpeed =
(operatingMode == ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE);
mCurrentSession.replaceSessionClose();
boolean configureSuccess = true;
Surface input = null;
configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,
operatingMode, sessionParams);
// Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
CameraCaptureSessionCore newSession = null;
if (isConstrainedHighSpeed) {
ArrayList<Surface> surfaces = new ArrayList<>(outputConfigurations.size());
for (OutputConfiguration outConfig : outputConfigurations) {
surfaces.add(outConfig.getSurface());
}
StreamConfigurationMap config =
getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config);
newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
callback, executor, this, mDeviceExecutor, configureSuccess,
mCharacteristics);
} else {
newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
callback, executor, this, mDeviceExecutor, configureSuccess);
}
mCurrentSession = newSession;
mSessionStateCallback = mCurrentSession.getDeviceStateCallback();
}
}
这个方法的作用就是配置surface了。该方法中最重要的就是configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations, operatingMode)这句了,它会执行surface配置,如果配置成功,则configureSuccess值为true,否则为false,接下来会创建session的实现类对象,一般是执行else分支,创建CameraCaptureSessionImpl对象
-----------------------------------------------------------------------------------------------------------------------------------
CameraCaptureSessionImpl的实现
frameworks/base/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
CameraCaptureSessionImpl(int id, Surface input,
CameraCaptureSession.StateCallback callback, Executor stateExecutor,
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
Executor deviceStateExecutor, boolean configureSuccess) {
mInput = input;
mStateExecutor = stateExecutor;
mStateCallback = createUserStateCallbackProxy(mStateExecutor, callback);//创建回调函数的代理对象
mDeviceExecutor = deviceStateExecutor;
mDeviceImpl = deviceImpl;
if (configureSuccess) {
mStateCallback.onConfigured(this);//回调函数
mConfigureSuccess = true;
}
}
private StateCallback createUserStateCallbackProxy(Executor executor, StateCallback callback) {
return new CallbackProxies.SessionStateCallbackProxy(executor, callback);
}
frameworks/base/core/java/android/hardware/camera2/impl/CallbackProxies.java
public class CallbackProxies {
public static class SessionStateCallbackProxy
extends CameraCaptureSession.StateCallback {
public SessionStateCallbackProxy(Executor executor,
CameraCaptureSession.StateCallback callback) {
mExecutor = executor;
mCallback = callback;
}
public void onConfigured(CameraCaptureSession session) {
final long ident = Binder.clearCallingIdentity();
mExecutor.execute(() -> mCallback.onConfigured(session));//将session通过回调函数传递给应用
}
}
}
通过以上流程,上层应用可以成功获得CameraCaptureSession,后续就可以调用CameraCaptureSession进行拍照或者预览了。
-----------------------------------------------------------------------------------------------------------------------------------
configureStreamsChecked的实现
frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
public boolean configureStreamsChecked(InputConfiguration inputConfig,
List<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams) {
checkInputConfiguration(inputConfig);//当前inputConfig为null,所以这部分不执行
synchronized(mInterfaceLock) {
//检查当前缓存的输出流数据列表,如果当前的输出流信息已经在列表中,则不必要重新创建流,如果没有则需要创建流
//要创建输出流的集合列表
HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);
//要删除的streamId列表,保证当前mConfiguredOutputs列表中的输出流数据是最新可用的
List<Integer> deleteList = new ArrayList<Integer>();
//创建输出流,mConfiguredOutputs是内存中的输出流缓存列表,会保存输出流streamId和输出流
for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
int streamId = mConfiguredOutputs.keyAt(i);
OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i);
if (!outputs.contains(outConfig) || outConfig.isDeferredConfiguration()) {
deleteList.add(streamId);
} else {
addSet.remove(outConfig);
}
}
mDeviceExecutor.execute(mCallOnBusy);//表示接下来要开始配置surface,处理繁忙状态了
stopRepeating();//停预览
try {
mRemoteDevice.beginConfigure();//通知CameraServer进程中的binder服务端对象,开始配置
InputConfiguration currentInputConfig = mConfiguredInput.getValue();
if (inputConfig != currentInputConfig &&
(inputConfig == null || !inputConfig.equals(currentInputConfig))) {
if (currentInputConfig != null) {
mRemoteDevice.deleteStream(mConfiguredInput.getKey());
mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
REQUEST_ID_NONE, null);
}
if (inputConfig != null) {
int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(),
inputConfig.getHeight(), inputConfig.getFormat());
mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
streamId, inputConfig);
}
}
//删除过期输出流
for (Integer streamId : deleteList) {
mRemoteDevice.deleteStream(streamId);
mConfiguredOutputs.delete(streamId);
}
for (OutputConfiguration outConfig : outputs) {//配置surface列表
if (addSet.contains(outConfig)) {
int streamId = mRemoteDevice.createStream(outConfig);
mConfiguredOutputs.put(streamId, outConfig);
}
}
if (sessionParams != null) {
mRemoteDevice.endConfigure(operatingMode, sessionParams.getNativeCopy());
} else {
mRemoteDevice.endConfigure(operatingMode, null);//通知CameraDeviceClient结束配置
}
success = true;
} finally {
if (success && outputs.size() > 0) {
mDeviceExecutor.execute(mCallOnIdle);
} else {
mDeviceExecutor.execute(mCallOnUnconfigured);
}
}
}
return success;
}
下面这张图详细列出配置输入输出流函数中执行的主要步骤,由于当前的inputConfig为null,