Android camera2
一、序言
为了对阶段性的知识积累、方便以后调查问题,特做此文档!
将以camera app 使用camera2 api进行分析。
(1)、打开相机 openCamera
(2)、创建会话 createCaptureSession
(3)、开始预览 setRepeatingRequest
(4)、停止预览 stopRepeating
(5)、关闭相机 closeCamera
二、打开相机
1、camera app在使用camera2 api的第一步就是调用cameraManager的openCamera接口此接口有三个参数
2、调用时序
2.1 调用openCamera接口
2.2 内部调用到openCameraDeviceUserAsync
调用内部方法getCameraCharacteristics查询camera设备的相关属性,这里会通过binder调用到CameraService(c++), 主要是camera 的前后朝向、图像显示角度(0度、90度、180度、270度)
new CameraDeviceImpl 的时候将一些变量保存在该对象内,这个CameraDeviceImpl会返回一个CameraDeviceCallbacks后面会将这个callback通过CameraService的接口connect()给到CameraDeviceClientBase, 后续不管是预览、录制、拍照存在帧相关的error都会通过CameraDeviceCallbacks --> CaptureCallback
接着去new client
走到makeClient的方法里面,会根据camera api的版本选择new 不同的client
new CameraDeviceClient -> new Camera2ClientBase -> new CameraDeviceClientBase() -> new BasicClient(), 在Camera2ClientBase的构造函数里面还会new Camera3Device
等new client结束后就会去调用initialize
CameraDeviceClient::initialize() -> Camera2ClientBase::initialize() -> Camera2ClientBase::initializeImpl() -> Camera3Device::initialize -> CameraProviderManager::openSession, CameraProviderManager类可以理解为hal camera的代理类
Camera3Device持有CameraProviderManager对象,而CameraProviderManager可以理解native和hal camera直接的桥梁,它会枚举出camera provider以及这些provider提供的摄像头设备,并提供方法去访问它们.
这里必须要插播一下CameraProviderManager的实例化和初始化了,如下图在CameraService 被new的时候就会自动走进onFirstRef(), 然后在该方法里面去new CameraProviderManager,紧接着调用mCameraProviderManager的initialize()方法,initialize()第二个参数是默认参数,直接赋值了HardwareServiceInteractionProxy的对象sHardwareServiceInteractionProxy, HardwareServiceInteractionProxy有两个方法,一个是registerForNotifications(), 一个是getService(), 这里getService()会拿到进程android.hardware.camera.provider@2.4-service中CameraProvider类的远端代理,而CameraProvider中的initialize()方法在CameraProvider的结构体中会被调用,在CameraProvider::initialize() 会去通过hw_get_module()方法打开动态库(camera*.so), 然后CameraProvider::
CameraModule中的camera_module_t *mModule 持有这个真正实现操作camera device的so句柄。
接着看addDevice方法
好了插播到此结束,接着上面的2.15 deviceInfo3->mInterface->open, mInterface就是CameraDevice,并且持有CameraModule,那么这里就会调用到CameraModule::open,紧接着调用到hal层真正实现者v4l2_camera_hal中的open
三、创建会话
1、在opencamera成功后,就会返回一个对象CameraDeviceImpl,后面在创建的会话和请求预览的时候都会调用该对象的方法。
2、创建会话会调用CameraDeviceImpl::createCaptureSession,分别有三个参数
3、创建会话代码流程分析
frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
// 当open camera成功之后,就会返回CameraDeviceImpl对象,然后在opened回调里面去创建会话,// 需要传进来两个surface,一个用于拍照,一个用于预览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,checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,/*sessionParams*/ null);}
frameworks/base/core/java/android/hardware/camera2/params/OutputConfiguration.java
public OutputConfiguration(@NonNull Surface surface) {this(SURFACE_GROUP_ID_NONE, surface, ROTATION_0);}@SystemApipublic OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, int rotation) {checkNotNull(surface, "Surface must not be null");checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");mSurfaceGroupId = surfaceGroupId;mSurfaceType = SURFACE_TYPE_UNKNOWN;mSurfaces = new ArrayList<Surface>(); // 有效值其实只有surfacemSurfaces.add(surface);mRotation = rotation;mConfiguredSize = SurfaceUtils.getSurfaceSize(surface);mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface);mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface);mConfiguredGenerationId = surface.getGenerationId();mIsDeferredConfig = false;mIsShared = false;mPhysicalCameraId = null;mIsMultiResolution = false;mSensorPixelModesUsed = new ArrayList<Integer>();}
frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
紧接着上面的createCaptureSessionInternal
private void createCaptureSessionInternal(InputConfiguration inputConfig,List<OutputConfiguration> outputConfigurations,CameraCaptureSession.StateCallback callback, Executor executor,int operatingMode, CaptureRequest sessionParams) throws CameraAccessException {.......try {// configure streams and then block until IDLE// 这里开始创建streamsconfigureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,operatingMode, sessionParams, createSessionStartTime);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 {// 上面创建了stream之后就会去创建session, 后面又用session对象去请求预览camera// 创建session就是直接一个java的对象newSession = new CamerraCaptureSessionImpl(mNextSessionId++, input,callback, executor, this, mDeviceExecutor, configureSuccess);}// TODO: wait until current session closes, then create the new sessionmCurrentSession = newSession;if (pendingException != null) {throw pendingException;}mSessionStateCallback = mCurrentSession.getDeviceStateCallback();}}
构建Streams
public boolean configureStreamsChecked(InputConfiguration inputConfig,List<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams,long createSessionStartTime)throws CameraAccessException {// Treat a null input the same an empty listif (outputs == null) {outputs = new ArrayList<OutputConfiguration>();}if (outputs.size() == 0 && inputConfig != null) {throw new IllegalArgumentException("cannot configure an input stream without " +"any output streams");}// 传进来的inputConfig就是 nullcheckInputConfiguration(inputConfig);boolean success = false;synchronized(mInterfaceLock) {checkIfCameraClosedOrInError();// Streams to createHashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);// Streams to deleteList<Integer> deleteList = new ArrayList<Integer>();// Determine which streams need to be created, which to be deleted// 这里新创建的CameraDeviceImpl 对象 mConfiguredOutputs.size() 都是0for (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}}mDeviceExecutor.execute(mCallOnBusy);// 这里新创建的CameraDeviceImpl 对象, mRepeatingRequestId == REQUEST_ID_NONE , // stopRepeating()等于什么都没有做stopRepeating();try {waitUntilIdle();// 调到 CameraDeviceClient::beginConfigure 中这里是个空实现,什么都没有做mRemoteDevice.beginConfigure();......// Delete all streams first (to free up HW resources)for (Integer streamId : deleteList) {mRemoteDevice.deleteStream(streamId);mConfiguredOutputs.delete(streamId);}// Add all new streams// 根据新的OutputConfiguration创建streamfor (OutputConfiguration outConfig : outputs) {`if (addSet.contains(outConfig)) {// 这里创建了Camera3OutputStreamint streamId = mRemoteDevice.createStream(outConfig);mConfiguredOutputs.put(streamId, outConfig);}}int offlineStreamIds[];if (sessionParams != null) {// 这里将stream的一些配置给到hal层,最终会调用到V4L2Camera::setupStreamsofflineStreamIds = mRemoteDevice.endConfigure(operatingMode,sessionParams.getNativeCopy(), createSessionStartTime);} else {offlineStreamIds = mRemoteDevice.endConfigure(operatingMode, null,createSessionStartTime);}......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 errormDeviceExecutor.execute(mCallOnUnconfigured);}}}return success;}
frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient
根据outputConfiguration去创建stream
binder::Status CameraDeviceClient::createStream(const hardware::camera2::params::OutputConfiguration &outputConfiguration,/*out*/int32_t* newStreamId) {ATRACE_CALL();binder::Status res;if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;Mutex::Autolock icl(mBinderSerializationLock);// outputConfiguration是持有surface, surface中持有graphicBufferProducer,这个producer就是// 给到camera hal去填充camera数据的const std::vector<sp<IGraphicBufferProducer>>& bufferProducers =outputConfiguration.getGraphicBufferProducers();size_t numBufferProducers = bufferProducers.size();bool deferredConsumer = outputConfiguration.isDeferred();bool isShared = outputConfiguration.isShared();String8 physicalCameraId = String8(outputConfiguration.getPhysicalCameraId());bool deferredConsumerOnly = deferredConsumer && numBufferProducers == 0;bool isMultiResolution = outputConfiguration.isMultiResolution();..........std::vector<sp<Surface>> surfaces;std::vector<sp<IBinder>> binders;..........OutputStreamInfo streamInfo;bool isStreamInfoValid = false;const std::vector<int32_t> &sensorPixelModesUsed =outputConfiguration.getSensorPixelModesUsed();for (auto& bufferProducer : bufferProducers) {// Don't create multiple streams for the same target surface// 一个stream 可以对应多个surface,但是一个surface只能对应一个streamsp<IBinder> binder = IInterface::asBinder(bufferProducer);ssize_t index = mStreamMap.indexOfKey(binder);// if 走进去说明这个surface 已经对应了 stream,之前已经保存在了mStreamMap中if (index != NAME_NOT_FOUND) {String8 msg = String8::format("Camera %s: Surface already has a stream created for it ""(ID %zd)", mCameraIdStr.string(), index);ALOGW("%s: %s", __FUNCTION__, msg.string());return STATUS_ERROR(CameraService::ERROR_ALREADY_EXISTS, msg.string());}// 根据前面app申请的surface,获取其中的bufferProducer,然后去创建streamInfo和 c++ surface,// 并返回sp<Surface> surface;res = SessionConfigurationUtils::createSurfaceFromGbp(streamInfo,isStreamInfoValid, surface, bufferProducer, mCameraIdStr,mDevice->infoPhysical(physicalCameraId), sensorPixelModesUsed);.......binders.push_back(IInterface::asBinder(bufferProducer));surfaces.push_back(surface);}// If mOverrideForPerfClass is true, do not fail createStream() for small// JPEG sizes because existing createSurfaceFromGbp() logic will find the// closest possible supported size.int streamId = camera3::CAMERA3_STREAM_ID_INVALID;std::vector<int> surfaceIds;......} else {// 这里走到Camera3Device中,注意这里的streamId、surfaceIds是要在createStream进行赋值的,而surfaces给到Camera3OutputStream// 中,createStream 创建一个Camera3OutputStream 就会streamId++err = mDevice->createStream(surfaces, deferredConsumer, streamInfo.width,streamInfo.height, streamInfo.format, streamInfo.dataSpace,static_cast<camera_stream_rotation_t>(outputConfiguration.getRotation()),&streamId, physicalCameraId, streamInfo.sensorPixelModesUsed, &surfaceIds,outputConfiguration.getSurfaceSetID(), isShared, isMultiResolution);}if (err != OK) {......} else {int i = 0;// binders 中对应的是每个surface的bufferProducer,即可以理解binder中存在的就是surfacefor (auto& binder : binders) {ALOGV("%s: mStreamMap add binder %p streamId %d, surfaceId %d",__FUNCTION__, binder.get(), streamId, i);// streamMap 一个surface的bufferProducer, 对应一个StreamSurfaceId对象,// 该对象又存一个streamId和对应的surfaceIds[i], 这个streamId就是创建第几个// Camera3OutputStream,surfaceIds[i] 存在的都是0;mStreamMap.add(binder, StreamSurfaceId(streamId, surfaceIds[i]));i++;}// 一个streamId对应一个outputConfigurationmConfiguredOutputs.add(streamId, outputConfiguration);// 每个Camera3OutputStream 和 streamInfo对应关系mStreamInfoMap[streamId] = streamInfo;ALOGV("%s: Camera %s: Successfully created a new stream ID %d for output surface"" (%d x %d) with format 0x%x.",__FUNCTION__, mCameraIdStr.string(), streamId, streamInfo.width,streamInfo.height, streamInfo.format);// Set transform flags to ensure preview to be rotated correctly.res = setStreamTransformLocked(streamId);// Fill in mHighResolutionCameraIdToStreamIdSet mapconst String8 &cameraIdUsed =physicalCameraId.size() != 0 ? physicalCameraId : mCameraIdStr;const char *cameraIdUsedCStr = cameraIdUsed.string();// Only needed for high resolution sensorsif (mHighResolutionSensors.find(cameraIdUsedCStr) !=mHighResolutionSensors.end()) {mHighResolutionCameraIdToStreamIdSet[cameraIdUsedCStr].insert(streamId);}// 这个返回给app*newStreamId = streamId;}return res;
}
frameworks/av/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
// 构建OutputStreamInfo,从app 申请的surface 获取wdith、height、fromat等信息
binder::Status SessionConfigurationUtils::createSurfaceFromGbp(OutputStreamInfo& streamInfo, bool isStreamInfoValid,sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp,const String8 &logicalCameraId, const CameraMetadata &physicalCameraMetadata,const std::vector<int32_t> &sensorPixelModesUsed){.......// 其实前面的surface 中的gbp,new 一个c++ 层的Surface对象,C++ Surface 继承自ANativeWindowsurface = new Surface(gbp, useAsync);ANativeWindow *anw = surface.get();int width, height, format;android_dataspace dataSpace;// 从ANativeWindow 中获取surface的width、height、format等,if ((err = anw->query(anw, NATIVE_WINDOW_WIDTH, &width)) != OK) {String8 msg = String8::format("Camera %s: Failed to query Surface width: %s (%d)",logicalCameraId.string(), strerror(-err), err);ALOGE("%s: %s", __FUNCTION__, msg.string());return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());}if ((err = anw->query(anw, NATIVE_WINDOW_HEIGHT, &height)) != OK) {String8 msg = String8::format("Camera %s: Failed to query Surface height: %s (%d)",logicalCameraId.string(), strerror(-err), err);ALOGE("%s: %s", __FUNCTION__, msg.string());return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());}if ((err = anw->query(anw, NATIVE_WINDOW_FORMAT, &format)) != OK) {String8 msg = String8::format("Camera %s: Failed to query Surface format: %s (%d)",logicalCameraId.string(), strerror(-err), err);ALOGE("%s: %s", __FUNCTION__, msg.string());return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());}if ((err = anw->query(anw, NATIVE_WINDOW_DEFAULT_DATASPACE,reinterpret_cast<int*>(&dataSpace))) != OK) {String8 msg = String8::format("Camera %s: Failed to query Surface dataspace: %s (%d)",logicalCameraId.string(), strerror(-err), err);ALOGE("%s: %s", __FUNCTION__, msg.string());return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());}......// isStreamInfoValid 传进来就是false,然后将上面的信息给到streaminfoif (!isStreamInfoValid) {streamInfo.width = width;streamInfo.height = height;streamInfo.format = format;streamInfo.dataSpace = dataSpace;streamInfo.consumerUsage = consumerUsage;streamInfo.sensorPixelModesUsed = overriddenSensorPixelModes;return binder::Status::ok();}......}return binder::Status::ok();
}
frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,const String8& physicalCameraId, const std::unordered_set<int32_t> &sensorPixelModesUsed,std::vector<int> *surfaceIds, int streamSetId, bool isShared, bool isMultiResolution,uint64_t consumerUsage) {......sp<Camera3OutputStream> newStream;......if (format == HAL_PIXEL_FORMAT_BLOB) {......} else {// new 一个 Camera3OutputStreamnewStream = new Camera3OutputStream(mNextStreamId, consumers[0],width, height, format, dataSpace, rotation,mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,isMultiResolution);}size_t consumerCount = consumers.size();for (size_t i = 0; i < consumerCount; i++) {// 这里Camera3OutputStream::getSurfaceId 的实现就是返回0;int id = newStream->getSurfaceId(consumers[i]);if (id < 0) {SET_ERR_L("Invalid surface id");return BAD_VALUE;}// 这里将前面传进来的surface其中的Id都存到surfaceIds中 if (surfaceIds != nullptr) {surfaceIds->push_back(id);}}newStream->setStatusTracker(mStatusTracker);newStream->setBufferManager(mBufferManager);newStream->setImageDumpMask(mImageDumpMask);res = mOutputStreams.add(mNextStreamId, newStream);if (res < 0) {SET_ERR_L("Can't add new stream to set: %s (%d)", strerror(-res), res);return res;}mSessionStatsBuilder.addStream(mNextStreamId);*id = mNextStreamId++;mNeedConfig = true;// Continue captures if active at startif (wasActive) {ALOGV("%s: Restarting activity to reconfigure streams", __FUNCTION__);// Reuse current operating mode and session parameters for new stream configres = configureStreamsLocked(mOperatingMode, mSessionParams);if (res != OK) {CLOGE("Can't reconfigure device for new stream %d: %s (%d)",mNextStreamId, strerror(-res), res);return res;}internalResumeLocked();}ALOGV("Camera %s: Created new stream", mId.string());return OK;
}
frameworks/av/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
Camera3OutputStream::Camera3OutputStream(int id,sp<Surface> consumer,uint32_t width, uint32_t height, int format,android_dataspace dataSpace, camera_stream_rotation_t rotation,nsecs_t timestampOffset, const String8& physicalCameraId,const std::unordered_set<int32_t> &sensorPixelModesUsed,int setId, bool isMultiResolution) :Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, width, height,/*maxSize*/0, format, dataSpace, rotation,physicalCameraId, sensorPixelModesUsed, setId, isMultiResolution),mConsumer(consumer),mTransform(0),mTraceFirstBuffer(true),mUseBufferManager(false),mTimestampOffset(timestampOffset),mConsumerUsage(0),mDropBuffers(false),mDequeueBufferLatency(kDequeueLatencyBinSize) {if (mConsumer == NULL) {ALOGE("%s: Consumer is NULL!", __FUNCTION__);mState = STATE_ERROR;}bool needsReleaseNotify = setId > CAMERA3_STREAM_SET_ID_INVALID;mBufferProducerListener = new BufferProducerListener(this, needsReleaseNotify);
}
时序图如下:
四、开始预览
1、从CameraManger -> openCamera() 获取CameraDeviceImpl ,然后调用CameraDeviceImpl的createCaptureSession的去创建会话,获取CameraCaptureSessionImpl对象,接着调用CameraCaptureSessionImpl的setRepeatingRequest去预览。
frameworks/base/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,Handler handler) throws CameraAccessException {checkRepeatingRequest(request);synchronized (mDeviceImpl.mInterfaceLock) {checkNotClosed();handler = checkHandler(handler, callback);if (DEBUG) {Log.v(TAG, mIdString + "setRepeatingRequest - request " + request + ", callback " +callback + " handler" + " " + handler);}return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,createCaptureCallbackProxy(handler, callback), mDeviceExecutor));}}
frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
Executor executor) throws CameraAccessException {List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();requestList.add(request);return submitCaptureRequest(requestList, callback, executor, /*streaming*/true);}
private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,Executor executor, boolean repeating) throws CameraAccessException {......// 如果打开过了就先停掉,然后再打开if (repeating) {stopRepeating();}SubmitInfo requestInfo;CaptureRequest[] requestArray = requestList.toArray(new CaptureRequest[requestList.size()]);// Convert Surface to streamIdx and surfaceIdxfor (CaptureRequest request : requestArray) {request.convertSurfaceToStreamId(mConfiguredOutputs);}// 调到CameraDeviceClient的submitRequestListrequestInfo = mRemoteDevice.submitRequestList(requestArray, repeating);if (DEBUG) {Log.v(TAG, "last frame number " + requestInfo.getLastFrameNumber());}......if (repeating) {if (mRepeatingRequestId != REQUEST_ID_NONE) {checkEarlyTriggerSequenceCompleteLocked(mRepeatingRequestId,requestInfo.getLastFrameNumber(),mRepeatingRequestTypes);}mRepeatingRequestId = requestInfo.getRequestId();mRepeatingRequestTypes = getRequestTypes(requestArray);} else {mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestList, requestInfo));}if (mIdle) {mDeviceExecutor.execute(mCallOnActive);}mIdle = false;return requestInfo.getRequestId();}}
frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
binder::Status CameraDeviceClient::submitRequestList(const std::vector<hardware::camera2::CaptureRequest>& requests,bool streaming,/*out*/hardware::camera2::utils::SubmitInfo *submitInfo) {......List<const CameraDeviceBase::PhysicalCameraSettingsList> metadataRequestList;std::list<const SurfaceMap> surfaceMapList;submitInfo->mRequestId = mRequestIdCounter;uint32_t loopCounter = 0;// for (auto&& request: requests) {if (request.mIsReprocess) {if (!mInputStream.configured) {ALOGE("%s: Camera %s: no input stream is configured.", __FUNCTION__,mCameraIdStr.string());return STATUS_ERROR_FMT(CameraService::ERROR_ILLEGAL_ARGUMENT,"No input configured for camera %s but request is for reprocessing",mCameraIdStr.string());} else if (streaming) {ALOGE("%s: Camera %s: streaming reprocess requests not supported.", __FUNCTION__,mCameraIdStr.string());return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,"Repeating reprocess requests not supported");} else if (request.mPhysicalCameraSettings.size() > 1) {ALOGE("%s: Camera %s: reprocess requests not supported for ""multiple physical cameras.", __FUNCTION__,mCameraIdStr.string());return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,"Reprocess requests not supported for multiple cameras");}}if (request.mPhysicalCameraSettings.empty()) {ALOGE("%s: Camera %s: request doesn't contain any settings.", __FUNCTION__,mCameraIdStr.string());return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,"Request doesn't contain any settings");}//The first capture settings should always match the logical camera idString8 logicalId(request.mPhysicalCameraSettings.begin()->id.c_str());if (mDevice->getId() != logicalId) {ALOGE("%s: Camera %s: Invalid camera request settings.", __FUNCTION__,mCameraIdStr.string());return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,"Invalid camera request settings");}if (request.mSurfaceList.isEmpty() && request.mStreamIdxList.size() == 0) {ALOGE("%s: Camera %s: Requests must have at least one surface target. ""Rejecting request.", __FUNCTION__, mCameraIdStr.string());return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,"Request has no output targets");}/*** Write in the output stream IDs and map from stream ID to surface ID* which we calculate from the capture request's list of surface target*/SurfaceMap surfaceMap;Vector<int32_t> outputStreamIds;std::vector<std::string> requestedPhysicalIds;if (request.mSurfaceList.size() > 0) {for (const sp<Surface>& surface : request.mSurfaceList) {if (surface == 0) continue;int32_t streamId;sp<IGraphicBufferProducer> gbp = surface->getIGraphicBufferProducer();res = insertGbpLocked(gbp, &surfaceMap, &outputStreamIds, &streamId);if (!res.isOk()) {return res;}ssize_t index = mConfiguredOutputs.indexOfKey(streamId);if (index >= 0) {String8 requestedPhysicalId(mConfiguredOutputs.valueAt(index).getPhysicalCameraId());requestedPhysicalIds.push_back(requestedPhysicalId.string());} else {ALOGW("%s: Output stream Id not found among configured outputs!", __FUNCTION__);}}} else {for (size_t i = 0; i < request.mStreamIdxList.size(); i++) {int streamId = request.mStreamIdxList.itemAt(i);int surfaceIdx = request.mSurfaceIdxList.itemAt(i);ssize_t index = mConfiguredOutputs.indexOfKey(streamId);if (index < 0) {ALOGE("%s: Camera %s: Tried to submit a request with a surface that"" we have not called createStream on: stream %d",__FUNCTION__, mCameraIdStr.string(), streamId);return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,"Request targets Surface that is not part of current capture session");}const auto& gbps = mConfiguredOutputs.valueAt(index).getGraphicBufferProducers();if ((size_t)surfaceIdx >= gbps.size()) {ALOGE("%s: Camera %s: Tried to submit a request with a surface that"" we have not called createStream on: stream %d, surfaceIdx %d",__FUNCTION__, mCameraIdStr.string(), streamId, surfaceIdx);return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,"Request targets Surface has invalid surface index");}res = insertGbpLocked(gbps[surfaceIdx], &surfaceMap, &outputStreamIds, nullptr);if (!res.isOk()) {return res;}String8 requestedPhysicalId(mConfiguredOutputs.valueAt(index).getPhysicalCameraId());requestedPhysicalIds.push_back(requestedPhysicalId.string());}}CameraDeviceBase::PhysicalCameraSettingsList physicalSettingsList;for (const auto& it : request.mPhysicalCameraSettings) {if (it.settings.isEmpty()) {ALOGE("%s: Camera %s: Sent empty metadata packet. Rejecting request.",__FUNCTION__, mCameraIdStr.string());return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,"Request settings are empty");}// Check whether the physical / logical stream has settings// consistent with the sensor pixel mode(s) it was configured with.// mCameraIdToStreamSet will only have ids that are high resolutionconst auto streamIdSetIt = mHighResolutionCameraIdToStreamIdSet.find(it.id);if (streamIdSetIt != mHighResolutionCameraIdToStreamIdSet.end()) {std::list<int> streamIdsUsedInRequest = getIntersection(streamIdSetIt->second,outputStreamIds);if (!request.mIsReprocess &&!isSensorPixelModeConsistent(streamIdsUsedInRequest, it.settings)) {ALOGE("%s: Camera %s: Request settings CONTROL_SENSOR_PIXEL_MODE not ""consistent with configured streams. Rejecting request.",__FUNCTION__, it.id.c_str());return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,"Request settings CONTROL_SENSOR_PIXEL_MODE are not consistent with ""streams configured");}}String8 physicalId(it.id.c_str());bool hasTestPatternModePhysicalKey = std::find(mSupportedPhysicalRequestKeys.begin(),mSupportedPhysicalRequestKeys.end(), ANDROID_SENSOR_TEST_PATTERN_MODE) !=mSupportedPhysicalRequestKeys.end();bool hasTestPatternDataPhysicalKey = std::find(mSupportedPhysicalRequestKeys.begin(),mSupportedPhysicalRequestKeys.end(), ANDROID_SENSOR_TEST_PATTERN_DATA) !=mSupportedPhysicalRequestKeys.end();if (physicalId != mDevice->getId()) {auto found = std::find(requestedPhysicalIds.begin(), requestedPhysicalIds.end(),it.id);if (found == requestedPhysicalIds.end()) {ALOGE("%s: Camera %s: Physical camera id: %s not part of attached outputs.",__FUNCTION__, mCameraIdStr.string(), physicalId.string());return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,"Invalid physical camera id");}if (!mSupportedPhysicalRequestKeys.empty()) {// Filter out any unsupported physical request keys.CameraMetadata filteredParams(mSupportedPhysicalRequestKeys.size());camera_metadata_t *meta = const_cast<camera_metadata_t *>(filteredParams.getAndLock());set_camera_metadata_vendor_id(meta, mDevice->getVendorTagId());filteredParams.unlock(meta);for (const auto& keyIt : mSupportedPhysicalRequestKeys) {camera_metadata_ro_entry entry = it.settings.find(keyIt);if (entry.count > 0) {filteredParams.update(entry);}}physicalSettingsList.push_back({it.id, filteredParams,hasTestPatternModePhysicalKey, hasTestPatternDataPhysicalKey});}} else {physicalSettingsList.push_back({it.id, it.settings});}}if (!enforceRequestPermissions(physicalSettingsList.begin()->metadata)) {// Callee logsreturn STATUS_ERROR(CameraService::ERROR_PERMISSION_DENIED,"Caller does not have permission to change restricted controls");}physicalSettingsList.begin()->metadata.update(ANDROID_REQUEST_OUTPUT_STREAMS,&outputStreamIds[0], outputStreamIds.size());if (request.mIsReprocess) {physicalSettingsList.begin()->metadata.update(ANDROID_REQUEST_INPUT_STREAMS,&mInputStream.id, 1);}physicalSettingsList.begin()->metadata.update(ANDROID_REQUEST_ID,&(submitInfo->mRequestId), /*size*/1);loopCounter++; // loopCounter starts from 1ALOGV("%s: Camera %s: Creating request with ID %d (%d of %zu)",__FUNCTION__, mCameraIdStr.string(), submitInfo->mRequestId,loopCounter, requests.size());metadataRequestList.push_back(physicalSettingsList);surfaceMapList.push_back(surfaceMap);}mRequestIdCounter++;if (streaming) {// 预览走这里err = mDevice->setStreamingRequestList(metadataRequestList, surfaceMapList,&(submitInfo->mLastFrameNumber));} else {// 拍照走这里err = mDevice->captureList(metadataRequestList, surfaceMapList,&(submitInfo->mLastFrameNumber));}ALOGV("%s: Camera %s: End of function", __FUNCTION__, mCameraIdStr.string());return res;
}
status_t Camera3Device::setStreamingRequestList(const List<const PhysicalCameraSettingsList> &requestsList,const std::list<const SurfaceMap> &surfaceMaps, int64_t *lastFrameNumber) {ATRACE_CALL();return submitRequestsHelper(requestsList, surfaceMaps, /*repeating*/true, lastFrameNumber);
}status_t Camera3Device::submitRequestsHelper(const List<const PhysicalCameraSettingsList> &requests,const std::list<const SurfaceMap> &surfaceMaps,bool repeating,/*out*/int64_t *lastFrameNumber) {// 预览走这里if (repeating) {res = mRequestThread->setRepeatingRequests(requestList, lastFrameNumber);} else {res = mRequestThread->queueRequestList(requestList, lastFrameNumber);}......return res;
}status_t Camera3Device::RequestThread::setRepeatingRequests(const RequestList &requests,/*out*/int64_t *lastFrameNumber) {ATRACE_CALL();Mutex::Autolock l(mRequestLock);if (lastFrameNumber != NULL) {*lastFrameNumber = mRepeatingLastFrameNumber;}mRepeatingRequests.clear();mFirstRepeating = true;// 将requests请求都给到mRepeatingRequests里面mRepeatingRequests.insert(mRepeatingRequests.begin(),requests.begin(), requests.end());unpauseForNewRequests();mRepeatingLastFrameNumber = hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES;return OK;
}void Camera3Device::RequestThread::unpauseForNewRequests() {ATRACE_CALL();// With work to do, mark thread as unpaused.// If paused by request (setPaused), don't resume, to avoid// extra signaling/waiting overhead to waitUntilPaused// 主要是这里的signal然后,唤醒一个线程去进行发送请求给halmRequestSignal.signal();Mutex::Autolock p(mPauseLock);if (!mDoPause) {ALOGV("%s: RequestThread: Going active", __FUNCTION__);if (mPaused) {sp<StatusTracker> statusTracker = mStatusTracker.promote();if (statusTracker != 0) {statusTracker->markComponentActive(mStatusId);}}mPaused = false;}
}
mRequestSignal.signal()要去唤醒之前跑起来阻塞住的线程
下发请求
Threadloop 开始工作
等待的线程被打断开始获取buffer,通过processBatchCaptureRequests下发请求到camera hal
通过onResultAvailable 通知app camera data准备好了
五、关闭预览
停止预览app会调用接口CameraDeviceImpl的stopRepeating接口,通过trace可以看到如下调用
CameraDeviceImpl::stopRepeating -> CameraDeviceClient::cancelRequest -> Camera3Device::clearStreamingRequest -> Camera3Device::RequestThread::clearRepeatingRequests->
Camera3Device::RequestThread::clearRepeatingRequestsLocked
会在clearRepeatingRequestsLocked中clear mRepeatingRequests List, 然后mRepeatingRequests为0后
Camera3Device 的threadloop 就会走到
然后停止获取buffer向camera hal发送
六、关闭相机
app调用close接口关闭camera,调用时序如上,在cameraserver中会退出相关的threadloop线程,在camera hal会正式做camera ioctrl close 销毁ais client 对应的camera context
相关文章:

Android camera2
一、序言 为了对阶段性的知识积累、方便以后调查问题,特做此文档! 将以camera app 使用camera2 api进行分析。 (1)、打开相机 openCamera (2)、创建会话 createCaptureSession (3)、开始预览 setRepeatingRequest (4)、停止预览 stopRepeating (5)、关闭…...

nginx监控指标有哪些
Nginx 的监控指标可以帮助你了解服务器的性能、资源使用以及运行状态。下面是一些常见的 Nginx 监控指标,涵盖了访问、性能、资源使用等多个方面: 1. 访问量与请求处理 Active Connections(活跃连接数):当前 Nginx 处…...

我谈正态分布——正态偏态
目录 pdf和cdf参数 标准正态分布期望和方差分布形态 3 σ 3\sigma 3σ原则 正态和偏态正态偏态瑞利分布偏度 (Skewness)峰度 (Kurtosis) 比较 正态分布的英文是Normal Distribution,normal是“正常”或“标准”的意思,中文翻译是正态,多完美的…...

如何使用uniswap v2 获取两个代币的交易对池子
在 Uniswap V2 中,获取两个代币的交易对池子(即 pair)可以通过以下步骤实现: 连接到 Uniswap V2 的合约:你需要与 Uniswap V2 的 Factory 合约进行交互,通过该合约来查找代币交易对。 获取交易对地址:Uniswap V2 Factory 合约提供了一个 getPair 函数,可以通过该函数查…...

CSS中常见的两列布局、三列布局、百分比和多行多列布局!
目录 一、两列布局 1、前言: 2. 两列布局的常见用法 两列布局的元素示例: 代码运行后如下: 二、三列布局 1.前言 2. 三列布局的常见用法 三列布局的元素示例: 代码运行后如下: 三、多行多列 1.前言 2&…...

GaussDB Ustore存储引擎解读
目录 一、数据库存储引擎 二、GaussDB Ustore存储引擎 总结 本文将介绍GaussDB中的Ustore存储引擎,包括Ustore的设计背景、特点介绍和适用业务场景等。 一、数据库存储引擎 数据库的存储引擎负责在内存和磁盘上存储、检索和管理数据,确保每个节点的…...

JAVA基础:数组 (习题笔记)
一,编码题 1,数组查找操作:定义一个长度为10 的一维字符串数组,在每一个元素存放一个单词;然后运行时从命令行输入一个单词,程序判断数组是否包含有这个单词,包含这个单词就打印出“Yes”&…...

VMWARE ESXI VMFS阵列故障 服务器数据恢复
1:河南用户一台DELL R740 3块2.4T硬盘组的RAID5,早期坏了一个盘没有及时更换,这次又坏了一个,导致整组RAID5处于数据丢失的状态, 2:该服务器装的是VMware ESXI 6.7,用户把3块硬盘寄过来进行数据…...

实时金融股票数据API接口websocket接入方法
一、使用websocket的协议提升传输速度 实时金融股票数据对于投资者和交易员来说至关重要。通过使用WebSocket接入方法,可以轻松获取实时金融股票类数据并及时做出决策。WebSocket是一种高效的双向通信协议,它允许数据的实时推送,避免了不断的…...

机器学习与成像技术
机器学习与成像技术 在科技日新月异的今天,机器学习与成像技术的结合正引领着智能视觉领域进入一个全新的发展阶段。这一结合不仅推动了图像识别、目标检测、视频分析等领域的快速发展,还深刻影响着医疗、安防、自动驾驶等多个行业。本文将从机器学习与…...

【系统架构设计师】预测试卷一:综合知识(75道选择题)
更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 【第1题】【第2题】【第3题】【第4题】【第5~6题】【第7题】【第8~10题】【第11题】【第12题】【第13题】【第14题】【第15题】【第16题】【第17~18题】【第19~20题】【第21~22题】【第23~24题】【第25~26题】【第2…...

【addRepository 在tomcat 8和tomcat 9的支持情况】
项目中涉及将远程下载的 jar包进行解密后加载到 tomcat 容器中。 File jarFile new File(fileUrl); String jarFileUrl jarFile.toURI().toURL().toString(); WebappClassLoader webLoader (WebappClassLoader) classLoader; webLoader.addRepository(jarFileUrl);在升级到 …...

2024网鼎杯web1+re2 wp
这两道题属于比较简单的,顺道说一下,今年的题有点抽象,web不是misc,re不是web的,也有可能时代在进步,现在要求全栈✌了吧 web1 最开始被强网的小浣熊带偏思路了,进来疯狂找sql注入,…...

Python 自动化运维:安全与合规最佳实践
Python 自动化运维:安全与合规最佳实践 目录 🔒 Python安全编程实践与最佳实践🔑 使用Hashlib与Cryptography进行数据加密📊 安全审计与合规检查的重要性🔍 处理敏感数据与隐私保护的方法 1. 🔒 Python安…...

I2S、PDM、PCM、TDM、DSM、DCODEC、VAD、SPDIF
I2S (Inter-IC Sound) 用途: 一种用于芯片之间传输音频数据的串行总线标准。特点: 常用于高质量音频设备,如DAC、ADC和音频编解码器。I2S主要传输PCM格式的音频数据。PDM (Pulse Density Modulation) 用途: 主要用于数字麦克风等设备,以简化硬件接口。特点: 使用脉冲密度来编…...

关于我的编程语言——C/C++——第四篇(深入1)
(叠甲:如有侵权请联系,内容都是自己学习的总结,一定不全面,仅当互相交流(轻点骂)我也只是站在巨人肩膀上的一个小卡拉米,已老实,求放过) 字符类型介绍 char…...

2025年上半年软考高级科目有哪些?附选科指南
新手在准备报考软考时,都会遇到这样的一个问题——科目这么多,我适合考什么?2025上半年软考高级有哪些科目可以报考?要想知道自己适合报什么科目,就需要了解每个科目是什么,考什么等一系列的问题࿰…...

线上查企业该用哪家平台?
在销售领域,经常会遇到电话接通率低的问题。这可能是因为许多电话号码来源于某些商业信息平台,这些号码可能已经被代账公司使用,或者已经被同行业多次联系过,导致接通率不高。为了解决这一问题,可以采用数据过滤服务&a…...

Metrix:实现CI服务器上的DORA指标自动化计算
本文还有配套的精品资源,点击获取 简介:Metrix项目是一个Go语言编写的工具,旨在自动化计算DevOps效能的关键指标——DORA指标,这些指标包括部署频率、前置时间、平均恢复时间和变更失败率。它通过集成到CI服务器如Jenkins或GitH…...

【STL_list 模拟】——打造属于自己的高效链表容器
一、list节点 list是一个双向循环带头的链表,所以链表节点结构如下: template<class T>struct ListNode{T val;ListNode* next;ListNode* prve;ListNode(int x){val x;next prve this;}};二、list迭代器 2.1、list迭代器与vector迭代器区别…...

Java 基础教学:高级特性与实战-集合框架
Java 集合框架提供了一套性能优良、使用方便的接口和类,用于存储和操作群组数据。最常用的集合接口有 List、Set 和 Map。 List List 接口可以存储一系列有序的元素,并且可以包含重复的元素。List 的实现类常用的有 ArrayList 和 LinkedList。 ArrayL…...

单片机原理及应用笔记:C51数组与项目实践
作者介绍 刘滋瑞,男,银川科技学院计算机与人工智能学院,2022级计算机与科学技术8班本科生,单片机原理及应用课程第九组。 指导老师:王兴泽 电子邮箱:602054774qq.com 前言 本篇文章是参考《单片机原理…...

综合项目--博客
一。基础配置: 1.配置主机名,静态IP地址 2.开启防火墙配置 3.部分开启selinux并且配置 4.服务器之间使用同ntp.aliyun.com进行世家能同步 5.服务器之间实现SSH绵密登陆 二。业务需求 1.Sever-NFS-DNS主机配置NFS服务器,将博客网站资源…...

ARM64的Mac Node.js前置工作,nvm在线安装
1,通过 终端 ping raw.githubusercontent.com 获取到ip地址185.199.110.133 2,终端输入sudo vi /etc/hosts,打开hosts文件 3,在最后添加 185.199.110.133 raw.githubusercontent.com 保存后退出 3.1,清除环境 完全…...

C++《list的模拟实现》
在上一篇C《list》专题当中我们了解了STL当中list类当中的各个成员函数该如何使用,接下来在本篇当中我们将试着模拟实现list,在本篇当中我们将通过模拟实现list过程中深入理解list迭代器和之前学习的vector和string迭代器的不同,接下来就开始…...

Kubernetes的概述与架构
Kubernetes 的概述 Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,方便进行声明式配置和自动化。Kubernetes 拥有一个庞大且快速增长的生态系统,其服务、支持和工具的使用范围广泛。 Kubernetes 这个名字源于…...

Elasticsearch实战应用:构建高效的全文搜索引擎
Elasticsearch实战应用:构建高效的全文搜索引擎 在当今信息爆炸的时代,如何快速、准确地从海量数据中检索出所需信息成为了企业和开发者面临的重要挑战。Elasticsearch作为一款开源的分布式搜索引擎,凭借其强大的全文搜索、实时分析和可扩展…...

达梦数据库和人大金仓数据库对数据库的运行查看情况
1、查看服务器自身资源使用情况 查看内存: free -g 查看整体负载: top 查看磁盘io : iostat -d -x 1 2、查看数据库占用服务器内存情况,登录DM管理工具,达梦数据库使用的内存大致等于 BUFFER MPOOL,对应的 SQL 语句为:…...

Spring Boot解决 406 错误之返回对象缺少Getter/Setter方法引发的问题
目录 前言1. 问题背景2. 问题分析2.1 检查返回对象 3. 解决方案3.1 确保Controller返回Result类型3.2 测试接口响应 4. 原理探讨5. 常见问题排查与优化建议结语 前言 在Spring Boot开发中,接口请求返回数据是系统交互的重要环节,尤其在开发RESTful风格的…...

Automa入门教程详解(Automa工作流概述)
一、什么是工作流? 工作流其实就是一组功能模块,通过彼此的连接来完成一系列的自动化操作流程。你可以把它理解为一个流程图,系统会根据你设置的顺序,从触发块开始,一步一步地执行,直到最后一个模块。这让…...