转载:多进程打开相机
多进程打开相机

只要满足一定的条件是可以多进程打开相机

1、CameraService打开相机的入口

Status CameraService::connectDevice(const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,const String16& cameraId,const String16& clientPackageName,int clientUid,/*out*/sp<hardware::camera2::ICameraDeviceUser>* device) {ATRACE_CALL();Status ret = Status::ok();String8 id = String8(cameraId);sp<CameraDeviceClient> client = nullptr;ret = connectHelper<hardware::camera2::ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id,/*api1CameraId*/-1,CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName,clientUid, USE_CALLING_PID, API_2,/*legacyMode*/ false, /*shimUpdateOnly*/ false,/*out*/client);if(!ret.isOk()) {logRejected(id, getCallingPid(), String8(clientPackageName),ret.toString8());return ret;}*device = client;return ret;
}

2、进入connectHelper

template<class CALLBACK, class CLIENT>
Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,int api1CameraId, int halVersion, const String16& clientPackageName, int clientUid,int clientPid, apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,/*out*/sp<CLIENT>& device) {binder::Status ret = binder::Status::ok();String8 clientName8(clientPackageName);int originalClientPid = 0;ALOGI("CameraService::connect call (PID %d \"%s\", camera ID %s) for HAL version %s and ""Camera API version %d", clientPid, clientName8.string(), cameraId.string(),(halVersion == -1) ? "default" : std::to_string(halVersion).c_str(),static_cast<int>(effectiveApiLevel));sp<CLIENT> client = nullptr;{// Acquire mServiceLock and prevent other clients from connecting//1)加这个锁的主要目的是防止不同应用同一时刻尝试打开相机,比如一个应用正在打开相机,另外一个应用也在尝试打开相机,这个时候就需要等待了std::unique_ptr<AutoConditionLock> lock =AutoConditionLock::waitAndAcquire(mServiceLockWrapper, DEFAULT_CONNECT_TIMEOUT_NS);if (lock == nullptr) {ALOGE("CameraService::connect (PID %d) rejected (too many other clients connecting).", clientPid);return STATUS_ERROR_FMT(ERROR_MAX_CAMERAS_IN_USE,"Cannot open camera %s for \"%s\" (PID %d): Too many other clients connecting",cameraId.string(), clientName8.string(), clientPid);}// Enforce client permissions and do basic sanity checks//2)这个主要是对应用做一些基本的检查//包括://1.检测UID是否可信//2.检测PID是否可信//3.检测是否有anddroid.permission.CAMERA权限//4.检测用户是否在mAllowedusers中,即检测是否是有效用户,//mAllowedusers的赋值实在CameraserviceProxy.javaif(!(ret = validateConnectLocked(cameraId, clientName8,/*inout*/clientUid, /*inout*/clientPid, /*out*/originalClientPid)).isOk()) {return ret;}// Check the shim parameters after acquiring lock, if they have already been updated and// we were doing a shim update, return immediatelyif (shimUpdateOnly) {auto cameraState = getCameraState(cameraId);if (cameraState != nullptr) {if (!cameraState->getShimParams().isEmpty()) return ret;}}status_t err;sp<BasicClient> clientTmp = nullptr;std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>> partial;//3)handleEvictionsLocked是处理多进程互斥逻辑的地方,//多进程同时打开相机的互斥逻辑就是在这个函数实现。//我们重点分析下这个函数if ((err = handleEvictionsLocked(cameraId, originalClientPid, effectiveApiLevel,IInterface::asBinder(cameraCb), clientName8, /*out*/&clientTmp,/*out*/&partial)) != NO_ERROR) {switch (err) {case -ENODEV:return STATUS_ERROR_FMT(ERROR_DISCONNECTED,"No camera device with ID \"%s\" currently available",cameraId.string());case -EBUSY:return STATUS_ERROR_FMT(ERROR_CAMERA_IN_USE,"Higher-priority client using camera, ID \"%s\" currently unavailable",cameraId.string());default:return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,"Unexpected error %s (%d) opening camera \"%s\"",strerror(-err), err, cameraId.string());}}..............sp<BasicClient> tmp = nullptr;if(!(ret = makeClient(this, cameraCb, clientPackageName,cameraId, api1CameraId, facing,clientPid, clientUid, getpid(), legacyMode,halVersion, deviceVersion, effectiveApiLevel,/*out*/&tmp)).isOk()) {return ret;}client = static_cast<CLIENT*>(tmp.get());LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state",__FUNCTION__);err = client->initialize(mCameraProviderManager, mMonitorTags);............if (shimUpdateOnly) {// If only updating legacy shim parameters, immediately disconnect clientmServiceLock.unlock();client->disconnect();mServiceLock.lock();} else {// Otherwise, add client to active clients listfinishConnectLocked(client, partial);}} // lock is destroyed, allow further connect calls// Important: release the mutex here so the client can call back into the service from its// destructor (can be at the end of the call)device = client;return ret;
}

3、分析handleEvictionsLocked

status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clientPid,apiLevel effectiveApiLevel, const sp<IBinder>& remoteCallback, const String8& packageName,/*out*/sp<BasicClient>* client,std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial) {ATRACE_CALL();status_t ret = NO_ERROR;std::vector<DescriptorPtr> evictedClients;DescriptorPtr clientDescriptor;{if (effectiveApiLevel == API_1) {// If we are using API1, any existing client for this camera ID with the same remote// should be returned rather than evicted to allow MediaRecorder to work properly.............}// Get current active client PIDs 获取已经打开相机的所有pidstd::vector<int> ownerPids(mActiveClientManager.getAllOwners());//将当前正在打开相机的应用进程号也保存到ownerPids 中ownerPids.push_back(clientPid);//创建进程priorityScores,states ventorstd::vector<int> priorityScores(ownerPids.size());std::vector<int> states(ownerPids.size());// Get priority scores of all active PIDs  //获取所有ownerPids中的进程priority和statesstatus_t err = ProcessInfoService::getProcessStatesScoresFromPids(ownerPids.size(), &ownerPids[0], /*out*/&states[0],/*out*/&priorityScores[0]);if (err != OK) {ALOGE("%s: Priority score query failed: %d",__FUNCTION__, err);return err;}// Update all active clients' priorities//将上一步获取的进程优先级更新到mActiveClientManager中std::map<int,resource_policy::ClientPriority> pidToPriorityMap;for (size_t i = 0; i < ownerPids.size() - 1; i++) {pidToPriorityMap.emplace(ownerPids[i],resource_policy::ClientPriority(priorityScores[i], states[i]));}mActiveClientManager.updatePriorities(pidToPriorityMap);// Get state for the given cameraId// 获取cameraId相机的状态,Cameraservice启动的时候会获取所有相机的状态auto state = getCameraState(cameraId);if (state == nullptr) {ALOGE("CameraService::connect X (PID %d) rejected (no camera device with ID %s)",clientPid, cameraId.string());// Should never get here because validateConnectLocked should have errored outreturn BAD_VALUE;}// Make descriptor for incoming client//由当前正在打开相机的cameraId,priorityScores,states,clientPid等信息//创建ClientDescriptor对象,key=cameraId;value=null;//ClientDescriptor未填充client,因为还未makeclientclientDescriptor = CameraClientManager::makeClientDescriptor(cameraId,sp<BasicClient>{nullptr}, static_cast<int32_t>(state->getCost()),state->getConflicting(),//当前正在打开相机的priorityScorespriorityScores[priorityScores.size() - 1],clientPid,//当前正在打开相机的statesstates[states.size() - 1]);// Find clients that would be evicted// 在mActiveClientManager查找和上一步创建的ClientDescriptor存在冲突的所有client// 如果有冲突的client,都保存到evicted队列中// evicted队列中的所有client都需要关闭正在使用的相机,// 如果当前正在打开相机的client也在该evicted,说明当前client不能打开相机// 接下来会重点分析该函数实现auto evicted = mActiveClientManager.wouldEvict(clientDescriptor);// If the incoming client was 'evicted,' higher priority clients have the camera in the// background, so we cannot do evictions//如果clientDescriptor(即当前应用)在evicted列表中,则说明当前相机应用的进程优先级较低,则本次打开相机失败,上报的错误log如下if (std::find(evicted.begin(), evicted.end(), clientDescriptor) != evicted.end()) {ALOGE("CameraService::connect X (PID %d) rejected (existing client(s) with higher"" priority).", clientPid);sp<BasicClient> clientSp = clientDescriptor->getValue();String8 curTime = getFormattedCurrentTime();//获取与当前client存在冲突的clientauto incompatibleClients =mActiveClientManager.getIncompatibleClients(clientDescriptor);//打印当前client信息String8 msg = String8::format("%s : DENIED connect device %s client for package %s ""(PID %d, score %d state %d) due to eviction policy", curTime.string(),cameraId.string(), packageName.string(), clientPid,priorityScores[priorityScores.size() - 1],states[states.size() - 1]);//打印与当前client冲突的client信息for (auto& i : incompatibleClients) {msg.appendFormat("\n   - Blocked by existing device %s client for package %s""(PID %" PRId32 ", score %" PRId32 ", state %" PRId32 ")",i->getKey().string(),String8{i->getValue()->getPackageName()}.string(),i->getOwnerId(), i->getPriority().getScore(),i->getPriority().getState());ALOGE("   Conflicts with: Device %s, client package %s (PID %"PRId32 ", score %" PRId32 ", state %" PRId32 ")", i->getKey().string(),String8{i->getValue()->getPackageName()}.string(), i->getOwnerId(),i->getPriority().getScore(), i->getPriority().getState());}// Log the client's attemptMutex::Autolock l(mLogLock);mEventLog.add(msg);return -EBUSY;}//关闭evicted中client正在使用的相机  如果clientDescriptor不在evicted,则强制关闭evicted中其他正在使用相机的应用for (auto& i : evicted) {sp<BasicClient> clientSp = i->getValue();if (clientSp.get() == nullptr) {ALOGE("%s: Invalid state: Null client in active client list.", __FUNCTION__);// TODO: Remove thisLOG_ALWAYS_FATAL("%s: Invalid state for CameraService, null client in active list",__FUNCTION__);mActiveClientManager.remove(i);continue;}ALOGE("CameraService::connect evicting conflicting client for camera ID %s",i->getKey().string());//将有效evicted的client push到evictedClientsevictedClients.push_back(i);// Log the clients evictedlogEvent(String8::format("EVICT device %s client held by package %s (PID"" %" PRId32 ", score %" PRId32 ", state %" PRId32 ")\n - Evicted by device %s client for"" package %s (PID %d, score %" PRId32 ", state %" PRId32 ")",i->getKey().string(), String8{clientSp->getPackageName()}.string(),i->getOwnerId(), i->getPriority().getScore(),i->getPriority().getState(), cameraId.string(),packageName.string(), clientPid,priorityScores[priorityScores.size() - 1],states[states.size() - 1]));// Notify the client of disconnection//通知evicted中client,相机马上就要关闭了clientSp->notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED,CaptureResultExtras());}}// Do not hold mServiceLock while disconnecting clients, but retain the condition blocking// other clients from connecting in mServiceLockWrapper if heldmServiceLock.unlock();// Clear caller identity temporarily so client disconnect PID checks work correctlyint64_t token = IPCThreadState::self()->clearCallingIdentity();// Destroy evicted clients//关闭所有在evictedClients中的正在使用相机的应用  for (auto& i : evictedClients) {// Disconnect is blocking, and should only have returned when HAL has cleaned upi->getValue()->disconnect(); // Clients will remove themselves from the active client list}IPCThreadState::self()->restoreCallingIdentity(token);for (const auto& i : evictedClients) {ALOGV("%s: Waiting for disconnect to complete for client for device %s (PID %" PRId32 ")",__FUNCTION__, i->getKey().string(), i->getOwnerId());ret = mActiveClientManager.waitUntilRemoved(i, DEFAULT_DISCONNECT_TIMEOUT_NS);if (ret == TIMED_OUT) {ALOGE("%s: Timed out waiting for client for device %s to disconnect, ""current clients:\n%s", __FUNCTION__, i->getKey().string(),mActiveClientManager.toString().string());return -EBUSY;}if (ret != NO_ERROR) {ALOGE("%s: Received error waiting for client for device %s to disconnect: %s (%d), ""current clients:\n%s", __FUNCTION__, i->getKey().string(), strerror(-ret),ret, mActiveClientManager.toString().string());return ret;}}evictedClients.clear();// Once clients have been disconnected, relockmServiceLock.lock();// Check again if the device was unplugged or something while we weren't holding mServiceLockif ((ret = checkIfDeviceIsUsable(cameraId)) != NO_ERROR) {return ret;}*partial = clientDescriptor;return NO_ERROR;
}

4、分析mActiveClientManager.wouldEvict(clientDescriptor);

实现函数为wouldEvictLocked

ClientManager<KEY, VALUE, LISTENER>::wouldEvict(const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {Mutex::Autolock lock(mLock);return wouldEvictLocked(client);
}
//returnIncompatibleClients为默认值false
template<class KEY, class VALUE, class LISTENER>
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
ClientManager<KEY, VALUE, LISTENER>::wouldEvictLocked(const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,bool returnIncompatibleClients) const {std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> evictList;// Disallow null clients, return inputif (client == nullptr) {evictList.push_back(client);return evictList;}//获取当前进程的现关信息const KEY& key = client->getKey(); //key就是cameraidint32_t cost = client->getCost();   //cost 在getCameraState会介绍ClientPriority priority = client->getPriority();  //获取优先级int32_t owner = client->getOwnerId();   //获取进程pidint64_t totalCost = getCurrentCostLocked() + cost;// Determine the MRU of the owners tied for having the highest priority//获取所有打开相机应用中优先级最高的pidint32_t highestPriorityOwner = owner;ClientPriority highestPriority = priority;for (const auto& i : mClients) {ClientPriority curPriority = i->getPriority();if (curPriority <= highestPriority) {// 值越小优先级越高highestPriority = curPriority;highestPriorityOwner = i->getOwnerId();}}if (highestPriority == priority) {// Switch back owner if the incoming client has the highest priority, as it is MRUhighestPriorityOwner = owner;}// Build eviction list of clients to remove//创建eviction list,这个队列就是存在冲突的clientfor (const auto& i : mClients) {const KEY& curKey = i->getKey();int32_t curCost = i->getCost();ClientPriority curPriority = i->getPriority();int32_t curOwner = i->getOwnerId();// 判断是否冲突://1.camera id 相同,存在冲突//isConflicting检查底层是否存在冲突bool conflicting = (curKey == key || i->isConflicting(key) ||client->isConflicting(curKey));if (!returnIncompatibleClients) {  //默认returnIncompatibleClients=false// Find evicted clients//查找冲突clientif (conflicting && curPriority < priority) {// Pre-existing conflicting client with higher priority exists//之前存在的client具有较高优先级,在将当前client添加到evictList中,//表示当前clieng不能打开相机evictList.clear();evictList.push_back(client);return evictList;} else if (conflicting || ((totalCost > mMaxCost && curCost > 0) &&(curPriority >= priority) &&!(highestPriorityOwner == owner && owner == curOwner))) {//在满足下边的条件时,将正在使用相机的client添加到evictList,表示在打开当前相机时,//正在使用相机的client因为和正在打开相机的client冲突需要关闭正在使用相机的client使用的相机。//1)存在冲突,且正在使用相机的client优先级低//2) 不存在冲突,但是totalCost 大于最大允许值mMaxCost //在当满足如下条件时也需要将正在使用相机的client添加到evictList://1.如果正在打开相机的client和正在使用相机的client不是同一进程而且//正在打开相机的client优先级高于或者等于正在使用相机的client//2.如果正在打开相机的client和正在使用相机的client是同一进程//但是该进程不是最高优先级。// Add a pre-existing client to the eviction list if:// - We are adding a client with higher priority that conflicts with this one.// - The total cost including the incoming client's is more than the allowable//   maximum, and the client has a non-zero cost, lower priority, and a different//   owner than the incoming client when the incoming client has the//   highest priority.evictList.push_back(i);totalCost -= curCost;}} else {// Find clients preventing the incoming client from being added// 这个条件上一步已经涵盖了if (curPriority < priority && (conflicting || (totalCost > mMaxCost && curCost > 0))) {// Pre-existing conflicting client with higher priority existsevictList.push_back(i);}}}// Immediately return the incompatible clients if we are calculating these insteadif (returnIncompatibleClients) {return evictList;}// If the total cost is too high, return the input unless the input has the highest priority//如果经过上一步检测,totalCost还是超过最大允许值且当前client不具有最高优先级,则将当前client添加到evictList,//表示当前client不能打开相机if (totalCost > mMaxCost && highestPriorityOwner != owner) {evictList.clear();evictList.push_back(client);return evictList;}return evictList;}

4、分析getCameraState

std::shared_ptr<CameraService::CameraState> CameraService::getCameraState(const String8& cameraId) const {std::shared_ptr<CameraState> state;{Mutex::Autolock lock(mCameraStatesLock);auto iter = mCameraStates.find(cameraId);if (iter != mCameraStates.end()) {state = iter->second;}}return state;
}

mCameraStates的赋值时在CameraService::enumerateProviders(),

主要是 mCameraStates.emplace(id8, std::make_shared(id8, cost.resourceCost, conflicting));

就是从HAL层获取,cameraid为id8的相机的resourceCost和conflicting列表(和那些相机存在冲突)

至此分析完成,可以得到的结论是:
多进程可以在满足一定条件是能都同时打开相机,需要满足的条件有:
evictList为空且totalCost <= mMaxCost.
如果totalCost <= mMaxCost不成立,可以适当调大mMaxCost。如果evictList不为空,则可能需要调剂下进程优先级或者在cameraservice中强制改下进程优先级

Android 多进程同时打开相机相关推荐

  1. Android使用CameraX打开相机拍照简单使用

    1.首先是效果图 2.布局及依赖 布局: <?xml version="1.0" encoding="utf-8"?> <androidx.c ...

  2. Android 调用系统打开相机,打开相册获取图片路径

    我们在开发中经常遇到一些功能需要调取系统相机拍照获取图片,或者有的时候直接打开图库获取图片,那我们怎么获取呢,今天分享下, 第一步,打开相机 public static final int CAMER ...

  3. Android WebView中打开相机拍照和选择相册

    一般在项目中与js交互,可能会遇到上传文件图片等操作,避免不了一些坑,下面简单说一下,Android 在不同版本中webView调用相机,选择相册的方法是不一样的,3.0以下的调用 public vo ...

  4. Android 之 打开相机 打开相册

    Android 之 打开系统摄像头拍照 打开系统相册,并展示 1,清单文件 AndroidManifest.xml ``` <uses-permission android:name=" ...

  5. Android Camera2教程之打开相机、开启预览、实现PreviewCallback、拍照

    转载请注明出处: http://blog.csdn.net/lb377463323/article/details/52740411 Android API 21新增了Camera2,这与之前的cam ...

  6. OpenCV学习笔记(六)—— OpenCV for Android打开相机

    在之前的篇章中,我们完成了Android平台开发环境的配置,也找到了剔除OpenCV Manager API的办法,那么接下来我们开始从零开始,完成一个个人的程序,实现功能如下: 1.识别指定的图片, ...

  7. android 7.0之后的打开相机拍摄头像上传

    今天一天都在搞这个头像相关的android开发,不断的在网上查找资料,什么都不懂,然后就照抄. 就是一个什么问题呢,现在android打开一个相机不再简单的请求打开就行了,按照我今天查到的资料,就是说 ...

  8. android调取手机相册或打开相机选择图片并显示

    作为一个android小白,自己想尝试写一个小项目,因此写个小博客记录一下自己的开发历程.这一篇记录自己学习调取手机相册以及打开相机选择图片并显示 示例是采用PopupWindow弹出底部菜单,选择相 ...

  9. 详记Android打开相机拍照流程

    写在前面 本文并不是基于Camera2的,所以想要了解Camera2的同学可以先散了.文题加了详记二字,因为相机整个打开的流程的确是比较复杂的,稍有疏忽可能就会引发一系列问题.我也是看了一下Andro ...

最新文章

  1. /dev/rdsk 与 /dev/dsk区别
  2. 倪光南院士:openEuler与全球开发者共同推动计算产业发展
  3. 3.15 排列对称串
  4. 烙饼啊烙饼{转自ITEO
  5. Silverlight进度条控件动画源代码
  6. what's the differences between readonly const in C# 在C#中readonly和const的区别
  7. 计算两个路径的父亲路径
  8. ArrayList类源码阅读
  9. MonkeyTest脚本
  10. Smartbi:用Excel制作移动端的九型人格测试
  11. 人工智能:一种现代的方法|课后习题解答
  12. eeglab和matlab,哪位大神会eeglab
  13. 2021 天勤率辉计算机考研(附408)
  14. 拒绝从网络访问这台计算机 注册表,拒绝访问的解决方法
  15. 中点分割裁剪算法 c语言,裁剪算法——中点分割算法/Liang-Barsky算法
  16. c语言 ascii码转成字符串,ASCII码与字符串的相互转换
  17. vue+echarts平面地图和飞线
  18. .axf文件_Keil开发环境如何生成BIN文件
  19. win7 各版本下载地址
  20. 网络自动化装机(批量装机)

热门文章

  1. 新建第一个HBuilder项目
  2. Unity 工具之 获取当前所在城市的天气数据的封装(自动定位当前所在城市,天气数据可以获得多天天数据)
  3. mysql 5.7.17 zip_win10下mysql 5.7.17 zip压缩包版安装教程
  4. 计算机中如何美化表格,手把手教你如何一秒美化Excel表格
  5. Android内、外存储 易混淆点剖析(/mnt/sdcard、/storage/sdcard0、/storage/emulated/0等区别)
  6. STC12C5A60S2 AD 转换详解
  7. 编程实现键盘记录功能
  8. C 程序设计语言——第四章练习题
  9. python 3d图形控件 可交互_Python和Matplotlib:在Jupyter Noteb中使3D plot交互
  10. MONTHS_BETWEEN函数使用