#include #include #include #include #include #include #include namespace lcamera_dev { namespace { static LcameraDevState lcameraDevState; void startCameraManager(CameraManagerResources& resources) { resources.cameraManager = std::make_unique(); if (resources.cameraManager->start()) { resources.cameraManager.reset(); throw std::runtime_error( "lcameraDev: failed to start libcamera CameraManager"); } } void stopCameraManager(CameraManagerResources& resources) { if (!resources.cameraManager) { return; } resources.cameraManager->stop(); resources.cameraManager.reset(); } std::shared_ptr findCameraById( const std::vector>& cameras, const std::string& cameraId) { for (const std::shared_ptr& camera : cameras) { if (camera && camera->id() == cameraId) { return camera; } } return nullptr; } } // namespace LcameraDevState& getLcameraDevState() { return lcameraDevState; } std::vector> listLibcameraCameras() { LcameraDevState& state = getLcameraDevState(); if (!state.isInitialized || !state.managerState.rsrc.cameraManager) { return {}; } return state.managerState.rsrc.cameraManager->cameras(); } void lcameraDevMain( const std::shared_ptr& componentThread) { LcameraDevState& state = getLcameraDevState(); if (state.isInitialized) { return; } if (!componentThread) { throw std::runtime_error( "lcameraDev_main: componentThread must be non-null"); } startCameraManager(state.managerState.rsrc); state.componentThread = componentThread; state.isInitialized = true; } void lcameraDevExit() { LcameraDevState& state = getLcameraDevState(); if (!state.isInitialized) { return; } CameraManagerResources& resources = state.managerState.rsrc; for (auto& entry : resources.sessionsByCameraId) { std::shared_ptr session = entry.second; if (!session || !session->s.rsrc.camera) { continue; } session->s.rsrc.camera->release(); } resources.sessionsByCameraId.clear(); stopCameraManager(resources); state.componentThread.reset(); state.isInitialized = false; } sscl::co::ViralNonPostingInvoker getOrCreateDeviceSessionCReq(const std::string& deviceSelector) { if (deviceSelector.empty()) { throw std::runtime_error( "lcameraDev_getOrCreateDeviceCReq: deviceSelector is empty"); } LcameraDevState& state = getLcameraDevState(); sscl::co::CoQutex::ReleaseHandle managerGuard = co_await state.managerState.lock.getAcquireInvocationAndSuspensionPolicy(); const std::vector criteria = parseDeviceSelector(deviceSelector); const std::vector> cameras = state.managerState.rsrc.cameraManager->cameras(); const std::vector identityRecords = buildIdentityRecords(cameras); const CameraIdentityRecord resolvedRecord = resolveSelectorAgainstRecords(criteria, identityRecords); const std::string& resolvedCameraId = resolvedRecord.id; auto sessionIt = state.managerState.rsrc.sessionsByCameraId.find(resolvedCameraId); if (sessionIt != state.managerState.rsrc.sessionsByCameraId.end()) { std::shared_ptr session = sessionIt->second; sscl::co::CoQutex::ReleaseHandle sessionGuard = co_await session->s.lock.getAcquireInvocationAndSuspensionPolicy(); session->incrementRefcount(); LcameraDevGetOrCreateResult result; result.deviceSession = session; result.resolvedIdentity = session->getIdentityRecord(); co_return result; } std::shared_ptr camera = findCameraById(cameras, resolvedCameraId); if (!camera) { throw std::runtime_error( "lcameraDev: resolved camera is no longer available: " + resolvedCameraId); } if (camera->acquire()) { throw std::runtime_error( "lcameraDev: failed to acquire camera: " + resolvedCameraId); } std::shared_ptr session = std::make_shared(resolvedRecord, camera); session->incrementRefcount(); state.managerState.rsrc.sessionsByCameraId.emplace( resolvedCameraId, session); LcameraDevGetOrCreateResult result; result.deviceSession = session; result.resolvedIdentity = session->getIdentityRecord(); co_return result; } sscl::co::ViralNonPostingInvoker releaseDeviceSessionCReq( const std::shared_ptr& deviceSession) { if (!deviceSession) { co_return; } LcameraDevState& state = getLcameraDevState(); sscl::co::CoQutex::ReleaseHandle managerGuard = co_await state.managerState.lock.getAcquireInvocationAndSuspensionPolicy(); const auto sessionIt = std::find_if( state.managerState.rsrc.sessionsByCameraId.begin(), state.managerState.rsrc.sessionsByCameraId.end(), [&deviceSession](const auto& entry) { return entry.second == deviceSession; }); if (sessionIt == state.managerState.rsrc.sessionsByCameraId.end()) { co_return; } bool shouldDestroy = false; { sscl::co::CoQutex::ReleaseHandle sessionGuard = co_await deviceSession->s.lock.getAcquireInvocationAndSuspensionPolicy(); shouldDestroy = deviceSession->decrementRefcount(); } if (!shouldDestroy) { co_return; } if (deviceSession->s.rsrc.camera) { deviceSession->s.rsrc.camera->release(); } state.managerState.rsrc.sessionsByCameraId.erase(sessionIt); co_return; } sscl::co::ViralNonPostingInvoker> enumerateCamerasCReq() { LcameraDevState& state = getLcameraDevState(); sscl::co::CoQutex::ReleaseHandle managerGuard = co_await state.managerState.lock.getAcquireInvocationAndSuspensionPolicy(); const std::vector> cameras = state.managerState.rsrc.cameraManager->cameras(); const std::vector identityRecords = buildIdentityRecords(cameras); std::vector cameraInfos; cameraInfos.reserve(identityRecords.size()); for (const CameraIdentityRecord& record : identityRecords) { cameraInfos.push_back(LcameraDevCameraInfo{ record.id, record.model, record.locationLabel }); } co_return cameraInfos; } } // namespace lcamera_dev