我们在之前的文章中介绍了 openCamera的流程,openCamera最终使CameraApp拿到了一个叫CameraDevice的对象,后面我们在创建会话、开启预览、拍照等流程中均看到这个CameraDvice的身影,今天我们来详细看这个在Camera中占据重要地位的对象:CameraDevice。
打开:frameworks\base\core\java\android\hardware\camera2\CameraDevice.java
开篇来了一个枚举的Template:
@IntDef(prefix = {"TEMPLATE_"}, value =
{TEMPLATE_PREVIEW,
TEMPLATE_STILL_CAPTURE,
TEMPLATE_RECORD,
TEMPLATE_VIDEO_SNAPSHOT,
TEMPLATE_ZERO_SHUTTER_LAG,
TEMPLATE_MANUAL})
其中有几个挺熟悉的:
接下来看到几个熟悉的方法
1. 获取CameraId的
public abstract String getId();
2. 创建会话的
public abstract void createCaptureSession(@NonNull List<Surface> outputs, @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
public abstract void createCaptureSessionByOutputConfigurations(List<OutputConfiguration> outputConfigurations,CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
public abstract void createCustomCaptureSession(
InputConfiguration inputConfig,
@NonNull List<OutputConfiguration> outputs,
@SessionOperatingMode int operatingMode,
@NonNull CameraCaptureSession.StateCallback callback,
@Nullable Handler handler)
public void createCaptureSession(SessionConfiguration config)
前两种形式比较常见,第3种createCustomCaptureSession,没见过,不知道是什么,最后一个,主要是参数比较特殊,是SessionConfiguration类型
我们去看下SessionConfiguration 是什么
frameworks\base\core\java\android\hardware\camera2\params\SessionConfiguration.java
public final class SessionConfiguration implements Parcelable {
private static final String TAG = "SessionConfiguration";
// Camera capture session related parameters.
private List<OutputConfiguration> mOutputConfigurations;
private CameraCaptureSession.StateCallback mStateCallback;
private int mSessionType;
private Executor mExecutor = null;
private InputConfiguration mInputConfig = null;
private CaptureRequest mSessionParameters = null;
/**
* Create a new {@link SessionConfiguration}.
*
* @param sessionType The session type.
* @param outputs A list of output configurations for the capture session.
* @param executor The executor which should be used to invoke the callback. In general it is
* recommended that camera operations are not done on the main (UI) thread.
* @param cb A state callback interface implementation.
*
* @see #SESSION_REGULAR
* @see #SESSION_HIGH_SPEED
* @see CameraDevice#createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)
* @see CameraDevice#createCaptureSessionByOutputConfigurations
* @see CameraDevice#createReprocessableCaptureSession
* @see CameraDevice#createConstrainedHighSpeedCaptureSession
*/
public SessionConfiguration(@SessionMode int sessionType,
@NonNull List<OutputConfiguration> outputs,
@NonNull @CallbackExecutor Executor executor,
@NonNull CameraCaptureSession.StateCallback cb) {
mSessionType = sessionType;
mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs));
mStateCallback = cb;
mExecutor = executor;
}
}
我截取了部分代码,可以看到SessionConfiguration 就是将原本一个个传给createCaptureSession的参数封装起来了,这样当然只传一个SessionConfiguration 对象就够了。
回到CameraDevice对象,继续往下看
3. 创建CaptureRequest的
public abstract CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType)
public CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType,Set<String> physicalCameraIdSet)
这个也很好理解,根据传入的 templateType ,创建CaptureRequest.Builder,为后面的拍照请求做准备
4. 关闭Camera
public abstract void close();
5. 一个内部类,关于Camera发生错误的类型定义和Camera状态的回调
public static abstract class StateCallback {
// error code的类型
public static final int ERROR_CAMERA_IN_USE = 1;
public static final int ERROR_MAX_CAMERAS_IN_USE = 2;
public static final int ERROR_CAMERA_DISABLED = 3;
public static final int ERROR_CAMERA_DEVICE = 4;
public static final int ERROR_CAMERA_SERVICE = 5;
public abstract void onOpened(@NonNull CameraDevice camera);
public void onClosed(@NonNull CameraDevice camera) {
// Default empty implementation
}
public abstract void onDisconnected(@NonNull CameraDevice camera);
public abstract void onError(@NonNull CameraDevice camera,
@ErrorCode int error);
}
CameraDevic的内容差不多就是这些了。我们注意到,CameraDevice是一个抽象类,它定义了一些方法,但是没有实现这些方法。
CameraDeviceImpl是CameraDevice的继承实现类
CameraDeviceImpl中有一个非常重要的成员变量
private ICameraDeviceUserWrapper mRemoteDevice;
你还记得这个成员变量的赋值来历吗? 我们在openCamera的流程中讲到了,在回顾下
在CameraManager中的openCameraDeviceUserAsync方法中,我们通过CameraService去连接Camera,得到一个cameraUser对象,
ICameraDeviceUser cameraUser = null;
// 创建CameraDeviceImpl
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
new android.hardware.camera2.impl.CameraDeviceImpl(
cameraId,
callback,
executor,
characteristics,
mContext.getApplicationInfo().targetSdkVersion);
//通过CameraService连接CameraId对应的ICameraDeviceUser
cameraUser = cameraService.connectDevice(callbacks, cameraId,
mContext.getOpPackageName(), mContext.getAttributionTag(), uid);
//将得到ICameraDeviceUser 对象赋给CmaeraDeviceImpl
deviceImpl.setRemoteDevice(cameraUser);
CameraDeviceImpl后续的操作都离不开这个ICameraDeviceUser对象
回到CameraDeviceImpl类中,在CameraManager调用CameraDevideImpl的setRemoteDevice方法,CameraDevideImpl先是将ICameraDeviceUser 包装成ICameraDeviceUserWrapper对象,然后执行了两个Runnable
public void setRemoteDevice(ICameraDeviceUser remoteDevice) throws CameraAccessException {
synchronized(mInterfaceLock) {
// TODO: Move from decorator to direct binder-mediated exceptions
// If setRemoteFailure already called, do nothing
if (mInError) return;
mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice);
IBinder remoteDeviceBinder = remoteDevice.asBinder();
// For legacy camera device, remoteDevice is in the same process, and
// asBinder returns NULL.
if (remoteDeviceBinder != null) {
try {
remoteDeviceBinder.linkToDeath(this, /*flag*/ 0);
} catch (RemoteException e) {
CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected);
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
"The camera device has encountered a serious error");
}
}
mDeviceExecutor.execute(mCallOnOpened);
mDeviceExecutor.execute(mCallOnUnconfigured);
}
}
private final Runnable mCallOnOpened = new Runnable() {
@Override
public void run() {
StateCallbackKK sessionCallback = null;
synchronized(mInterfaceLock) {
if (mRemoteDevice == null) return; // Camera already closed
sessionCallback = mSessionStateCallback;
}
if (sessionCallback != null) {
sessionCallback.onOpened(CameraDeviceImpl.this);
}
mDeviceCallback.onOpened(CameraDeviceImpl.this);//我们在CameraApp中接收到 onOpened回调,并拿到CameraDeviceImpl对象
}
};
private final Runnable mCallOnUnconfigured = new Runnable() {
@Override
public void run() {
StateCallbackKK sessionCallback = null;
synchronized(mInterfaceLock) {
if (mRemoteDevice == null) return; // Camera already closed
sessionCallback = mSessionStateCallback;
}
if (sessionCallback != null) {
sessionCallback.onUnconfigured(CameraDeviceImpl.this);
}
}
};
我们在来看下CameraDeviceImpl是如何实现createCaptureSession的,毕竟创建会话是很重要的流程
@Override
public void createCaptureSessionByOutputConfigurations(
List<OutputConfiguration> outputConfigurations,
CameraCaptureSession.StateCallback callback, Handler handler)
throws CameraAccessException {
// OutputConfiguration objects are immutable, but need to have our own array
List<OutputConfiguration> currentOutputs = new ArrayList<>(outputConfigurations);
createCaptureSessionInternal(null, currentOutputs, callback, checkAndWrapHandler(handler),
/*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/null);
}
流程转到 createCaptureSessionInternal方法中
1,检查当前CameraDeviceImpl的状态,如果当前Camera的状态异常,就直接抛出Exception退出,不在进行后续流程
2,如果 mCurrentSession != null ,说明当前CameraDevice已经创建过会话,需要将这个已经创建的会话close掉
3,通过mRemoteDevice对象,传入相关的 OutputConfiguration、CaptureRequest等参数去创建会话
4,mRemoteDevice创建会话成功,构建CameraCaptureSessionImpl对象
private void createCaptureSessionInternal(InputConfiguration inputConfig,
List<OutputConfiguration> outputConfigurations,
CameraCaptureSession.StateCallback callback, Executor executor,
int operatingMode, CaptureRequest sessionParams) throws CameraAccessException {
synchronized(mInterfaceLock) {
if (DEBUG) {
Log.d(TAG, "createCaptureSessionInternal");
}
// 1,检查当前CameraDeviceImpl的状态
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) {//2,当前CameraDevice已经创建过会话,需要将这个已经创建的会话close掉
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, sessionParams);//3,创建会话
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) {
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 {//4,mRemoteDevice创建会话成功,构建CameraCaptureSessionImpl对象
newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
callback, executor, this, mDeviceExecutor, configureSuccess);
}
// TODO: wait until current session closes, then create the new session
mCurrentSession = newSession;
if (pendingException != null) {
throw pendingException;
}
mSessionStateCallback = mCurrentSession.getDeviceStateCallback();
}
}
这个流程中第3点: 调用 configureStreamsChecked 去向底层配置surface ,我们来详细看下
①,检查当前CameraDevice的状态
②,for循环
如果是通过createCaptureSessionByOutputConfigurations第一次调过来,当前CameraDeviceImpl的mConfiguredOutputs成员变量为null,不走for循环
③:stopRepeating() 创建会话时会停止当前已经进行的预览
④,mRemoteDevice.beginConfigure();
⑤:将传入的 List outputs 一个个循环设置到mRemoteDevice.createStrea
⑥:将传入的OutputConfiguration对象put到当前CameraDeviceImpl的mConfiguredOutputs集合中管理
⑦:mRemoteDevice.endConfigure(operatingMode, null);
public boolean configureStreamsChecked(InputConfiguration inputConfig,
List<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams)
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();//1,检查当前CameraDevice的状态
// 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) {//2 for循环
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
}
}
mDeviceExecutor.execute(mCallOnBusy);
stopRepeating();//3,创建会话时会停止当前已经进行的预览
try {
waitUntilIdle();
mRemoteDevice.beginConfigure();//4,
// reconfigure the input stream if the input configuration is different.
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);
}
}
// Delete all streams first (to free up HW resources)
for (Integer streamId : deleteList) {
mRemoteDevice.deleteStream(streamId);
mConfiguredOutputs.delete(streamId);
}
// Add all new streams
for (OutputConfiguration outConfig : outputs) {
if (addSet.contains(outConfig)) {
int streamId = mRemoteDevice.createStream(outConfig);//5,将传入的 List outputs 一个个循环设置到mRemoteDevice
mConfiguredOutputs.put(streamId, outConfig);//6,将传入的OutputConfiguration对象put到当前CameraDeviceImpl的mConfiguredOutputs集合中管理
}
}
int offlineStreamIds[];
if (sessionParams != null) {
offlineStreamIds = mRemoteDevice.endConfigure(operatingMode,
sessionParams.getNativeCopy());
} else {
offlineStreamIds = mRemoteDevice.endConfigure(operatingMode, null);//7,
}
mOfflineSupport.clear();
if ((offlineStreamIds != null) && (offlineStreamIds.length > 0)) {
for (int offlineStreamId : offlineStreamIds) {
mOfflineSupport.add(offlineStreamId);
}
}
success = true;
} catch (IllegalArgumentException e) {
// OK. camera service can reject stream config if it's not supported by HAL
// This is only the result of a programmer misusing the camera2 api.
Log.w(TAG, "Stream configuration failed due to: " + e.getMessage());
return false;
} catch (CameraAccessException e) {
if (e.getReason() == CameraAccessException.CAMERA_IN_USE) {
throw new IllegalStateException("The camera is currently busy." +
" You must wait until the previous operation completes.", e);
}
throw e;
} finally {
if (success && outputs.size() > 0) {
mDeviceExecutor.execute(mCallOnIdle);
} else {
// Always return to the 'unconfigured' state if we didn't hit a fatal error
mDeviceExecutor.execute(mCallOnUnconfigured);
}
}
}
return success;
}
CameraCaptureSessionImpl 也是很重要的一块内容,我们会在下篇文章中单独来讲。
我们在继续看CameraDeviceImpl中的其它内容
创建CaptureRequest对象 这里的关键逻辑是:mRemoteDevice.createDefaultRequest(templateType);
@Override
public CaptureRequest.Builder createCaptureRequest(int templateType)
throws CameraAccessException {
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
CameraMetadataNative templatedRequest = null;
templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
// If app target SDK is older than O, or it's not a still capture template, enableZsl
// must be false in the default request.
if (mAppTargetSdkVersion < Build.VERSION_CODES.O ||
templateType != TEMPLATE_STILL_CAPTURE) {
overrideEnableZsl(templatedRequest, false);
}
CaptureRequest.Builder builder = new CaptureRequest.Builder(
templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
getId(), /*physicalCameraIdSet*/ null);
return builder;
}
}
prepare方法:mRemoteDevice.prepare(streamId);
public void prepare(Surface surface) throws CameraAccessException {
if (surface == null) throw new IllegalArgumentException("Surface is null");
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
int streamId = -1;
for (int i = 0; i < mConfiguredOutputs.size(); i++) {
final List<Surface> surfaces = mConfiguredOutputs.valueAt(i).getSurfaces();
if (surfaces.contains(surface)) {
streamId = mConfiguredOutputs.keyAt(i);
break;
}
}
if (streamId == -1) {
throw new IllegalArgumentException("Surface is not part of this session");
}
mRemoteDevice.prepare(streamId);
}
}
updateOutputConfiguration:mRemoteDevice.updateOutputConfiguration(streamId, config);
public void updateOutputConfiguration(OutputConfiguration config)
throws CameraAccessException {
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
int streamId = -1;
for (int i = 0; i < mConfiguredOutputs.size(); i++) {
if (config.getSurface() == mConfiguredOutputs.valueAt(i).getSurface()) {
streamId = mConfiguredOutputs.keyAt(i);
break;
}
}
if (streamId == -1) {
throw new IllegalArgumentException("Invalid output configuration");
}
mRemoteDevice.updateOutputConfiguration(streamId, config);
mConfiguredOutputs.put(streamId, config);
}
}
tearDown:mRemoteDevice.tearDown(streamId);
public void tearDown(Surface surface) throws CameraAccessException {
if (surface == null) throw new IllegalArgumentException("Surface is null");
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
int streamId = -1;
for (int i = 0; i < mConfiguredOutputs.size(); i++) {
if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
streamId = mConfiguredOutputs.keyAt(i);
break;
}
}
if (streamId == -1) {
throw new IllegalArgumentException("Surface is not part of this session");
}
mRemoteDevice.tearDown(streamId);
}
}
finalizeOutputConfigs:mRemoteDevice.finalizeOutputConfigurations(streamId, config);
public void finalizeOutputConfigs(List<OutputConfiguration> outputConfigs)
throws CameraAccessException {
if (outputConfigs == null || outputConfigs.size() == 0) {
throw new IllegalArgumentException("deferred config is null or empty");
}
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
for (OutputConfiguration config : outputConfigs) {
int streamId = -1;
for (int i = 0; i < mConfiguredOutputs.size(); i++) {
// Have to use equal here, as createCaptureSessionByOutputConfigurations() and
// createReprocessableCaptureSessionByConfigurations() do a copy of the configs.
if (config.equals(mConfiguredOutputs.valueAt(i))) {
streamId = mConfiguredOutputs.keyAt(i);
break;
}
}
if (streamId == -1) {
throw new IllegalArgumentException("Deferred config is not part of this "
+ "session");
}
if (config.getSurfaces().size() == 0) {
throw new IllegalArgumentException("The final config for stream " + streamId
+ " must have at least 1 surface");
}
mRemoteDevice.finalizeOutputConfigurations(streamId, config);
mConfiguredOutputs.put(streamId, config);
}
}
}
最后我们来看一个最最重要的方法:capture
public int capture(CaptureRequest request, CaptureCallback callback, Executor executor)
throws CameraAccessException {
if (DEBUG) {
Log.d(TAG, "calling capture");
}
List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
requestList.add(request);
return submitCaptureRequest(requestList, callback, executor, /*streaming*/false);
}
哎呦喂,乍一看,啥都没干,就是submitCaptureRequest,看这方法的含义,只是提交一个拍照request
private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
Executor executor, boolean repeating) throws CameraAccessException {
//1,停止预览
if (repeating) {
stopRepeating();
}
SubmitInfo requestInfo;
//2,convertSurfaceToStreamId
CaptureRequest[] requestArray = requestList.toArray(new CaptureRequest[requestList.size()]);
// Convert Surface to streamIdx and surfaceIdx
for (CaptureRequest request : requestArray) {
request.convertSurfaceToStreamId(mConfiguredOutputs);
}
//3,mRemoteDevice 继续向底层提交request
requestInfo = mRemoteDevice.submitRequestList(requestArray, repeating);
//4,recoverStreamIdToSurface
for (CaptureRequest request : requestArray) {
request.recoverStreamIdToSurface();
}
其中第3,mRemoteDevice 继续向底层提交request,这里涉及到aidl的跨进程通信了,具体实现如下涉及两个类:
frameworks\hardware\interfaces\cameraservice\device\2.0\ICameraDeviceUser.hal下的
submitRequestList(vec requestList, bool isRepeating)
generates (Status status, SubmitInfo submitInfo);
frameworks\av\services\camera\libcameraservice\api2\CameraDeviceClient.cpp
binder::Status CameraDeviceClient::submitRequestList(
const std::vectorhardware::camera2::CaptureRequest& requests,
bool streaming,
/out/
hardware::camera2::utils::SubmitInfo *submitInfo)
这块的调用及底层逻辑我们后续在另起文章介绍。
CameraDeviceImpl的内容大致就是上面这些了,我们看到CameraDeviceImpl的大部分逻辑是通过其内部成员变量mRemoteDevice去继续向底层请求实现的。
Camera是一套完整的从面向用户的CameraApp到我们今天这篇文章分析的hardware.camera2,在到hal的体系,其中的每个环节都至关重要,而camera的学习便是打通这个流程的过程。