基于MTK Camera2框架以及Google developers里面关于Camera2的介绍,加以梳理,去整理一下关于Camera CaptureSession的时序以及需要注意的点,方便以后分析问题。
1.MTK CaptureSession时序
这份时序图整理的比较早,现在回过头再去check相关的流程,发现很多地方并没有重点说明,甚至遗漏了很多重要的点。
2.MTK 应用层CreateCaptureSession重点
1.应用层调用FrameWork层CameraDevice.createCaptureSession的过程是通过俩部分来完成的,第一步:List<OutputConfiguration>绑定target surface需要的信息
private void configureSession(boolean isFromOpen) {
...
mOutputConfigs = new ArrayList<>();
//预览的surface是GLSurface,需要绑定定其对应的纹理ID SurfaceTexture
android.util.Size previewSize = new android.util.Size(mPreviewWidth, mPreviewHeight);
OutputConfiguration previewConfig = new OutputConfiguration(previewSize, SurfaceTexture.class);
//captureSurface、previewImageSurface是TextureViewSurface
OutputConfiguration captureConfig = new OutputConfiguration(mCaptureSurface.getSurface());
mPreviewImageSurface.updatePictureInfo(mPreviewWidth, mPreviewHeight, ImageFormat.YUV_420_888, 1);
OutputConfiguration previewYuvConfig = new OutputConfiguration(mPreviewImageSurface.getSurface());
mOutputConfigs.add(previewConfig);
mOutputConfigs.add(captureConfig);
mOutputConfigs.add(previewYuvConfig);
...
mCamera2Proxy.createCaptureSession(mSessionCallback, mModeHandler, mBuilder, mOutputConfigs);
...
}
第二步:List<OutputConfiguration>绑定target surface
private void configureSession(boolean isFromOpen) {
...
List<Surface> surfaces = new LinkedList<>();
surfaces.add(mPreviewSurface);
surfaces.add(mCaptureSurface.getSurface());
surfaces.add(mPreviewImageSurface.getSurface());
...
mCamera2Proxy.createCaptureSession(surfaces, mSessionCallback, mModeHandler, mBuilder);
...
}
然后会在Camer2Handle里面的私有方法createCaptureSession中调用FraneWork层CameraDevice.createCaptureSession接口创建
private void createCaptureSession(SessionCreatorInfo info) throws CameraAccessException {
if (mCameraCaptureSession != null) {
mCameraCaptureSession = null;
}
List<Surface> surfaces = info.mSurfaces;
mSessionStateProxyCallback = info.mCallback;
mCaptureSessionProxyHandler = info.mHandler;
if (info.mBuilder == null) {
LogHelper.i(mTag, "[createCaptureSession] mBuilder is null");
mCameraDevice.createCaptureSession(surfaces, mSessionStateCallback, mRespondHandler);
return;
}
if (info.mOutputConfigs != null) {
SessionConfiguration sessionConfigByOutput = new SessionConfiguration(
SessionConfiguration.SESSION_REGULAR, info.mOutputConfigs,
new HandlerExecutor(mRespondHandler), mSessionStateCallback);
LogHelper.i(mTag, "[createCaptureSession] with mOutputConfigs");
sessionConfigByOutput.setSessionParameters(info.mBuilder.build());
mCameraDevice.createCaptureSession(sessionConfigByOutput);
return;
}
List<OutputConfiguration> outConfigurations = new ArrayList<>(surfaces.size());
for (Surface surface : surfaces) {
outConfigurations.add(new OutputConfiguration(surface));
}
SessionConfiguration sessionConfig = new SessionConfiguration(
SessionConfiguration.SESSION_REGULAR, outConfigurations,
new HandlerExecutor(mRespondHandler), mSessionStateCallback);
LogHelper.i(mTag, "[createCaptureSession] with parameters");
sessionConfig.setSessionParameters(info.mBuilder.build());
mCameraDevice.createCaptureSession(sessionConfig);
}
附加说明:
第一次绑定outputconfiguration通过CameraDevice.CreateCaptureSession调用之后会创建对应的CaptureSession,在第二次绑定surface信息之前会在session创建成功触发onConfingured之后调用CameraCaptureSession.finalizeOutputConfigurations。
2.CameraCaptureSession.StateCallback,这个根据Session状态触发的回调函数很重要但是没什么好讲的,绑定过程看时序图应该就欧凯了;这边拉出来是看谷歌开发手册时候发现一些涉及到状态回调容易忽略但是比较重要的点。
这边有俩个注意点:
新的session创建触发onConfigured之前会有旧的session的onclosed过程;
close之后的session会清掉repeatingRequest请求,但是也会完成已经下发的captureRequest的动作。
Camera有很多切换分辨率的场景,每次切换分辨率对应的output surface target的size都会随之发生改变,在切换之后重新createCaptureSession之前调用abortCaptures去close之前的session转态。需要注意的是调用abortCaptures会造成短暂的camera device stream的pause状态,在实际开发过程中,这个状态下继续下发repeatingRequest的请求肯定会有问题;或者新的output buffer还没有产生之前request请求已经下发都会造成CameraDevice close的问题。
3.针对这些问题,找了一些开发过程中遇到的问题,通过log分析去check createCaptureSession的一些流程上的问题。
通过log check createCaptureSession流程,updatePreviewSize可以看出Video模式下切换分辨率导致需要重新createCaptureSession,abortOldSession时候调用了abortCaptures去搞笑的clear之前的session,在新的session创建成功调用onConfigured之前,旧的session会close掉并唤醒onClosed状态回调函数,可以通过session 地址id去check;新session onReady的状态表明之前的repeatingRequest请求已经被处理完或者被clear掉了。
如下俩张图是createCaptureSession绑定output target surfaces出现问题时的log,第二张图是具体的报错定位点,错误原因是CaptureRequest contains unconfigured Input/Output Surface,造成这个crash的原因基本就是List<OutputConfiguration>俩次绑定的数据不一致导致的,具体是什么不一致可对比正常creataCaptureSession log便于定位。
查看正常的log可以发现在做createCaptureSession时候VideoHelper拿到了surfaceChanged的数据,成功创建了分辨率发生改变的TextureViewSurface,而发生onError的log里面并没有拿到分辨率改变之后的新的surface,这样就会导致之前分析的List<OutputConfiguration> addSurface时候与第一次绑定的surface信息不匹配的问题,导致session创建过程返回了onError的状态,进而导致CameraDevice的断开连接。
小结:
createCaptureSession创建过程中引发问题导致CameraDevice断开连接的问题是比较多的,会有其他的很多情况出现,需要耐心的check流程以及对这一过程进行更加深入的研究与分析。