#include #include #include #include #include #include #include #include #include namespace smo::stim_buff::lcamera_buff { const SmoCallbacks *lcameraBuffSmoHooksPtr = nullptr; SmoThreadingModelDesc lcameraBuffThreadingModelDesc; LcameraDevDllState lcameraDevDll; std::vector> managedStimulusProducers; void addManagedStimulusProducer( const std::shared_ptr& producer) { managedStimulusProducers.push_back(producer); producer->start(); } sscl::co::ViralNonPostingInvoker removeManagedStimulusProducerIfUnused( const std::shared_ptr& producer) { if (!producer) { co_return; } if (!producer->attachedStimulusBuffers.empty()) { co_return; } producer->stop(); managedStimulusProducers.erase( std::remove_if( managedStimulusProducers.begin(), managedStimulusProducers.end(), [&producer](const std::shared_ptr& candidate) { return candidate == producer; }), managedStimulusProducers.end()); const std::shared_ptr deviceSession = producer->deviceSession; if (deviceSession) { co_await (*lcameraDevDll.lcameraDev_releaseDeviceCReq)(deviceSession); } producer->deviceSession.reset(); } void LcameraDevDllState::DlCloser::operator()(void *handle) { if (handle) { dlclose(handle); } } LcameraDevDllState::LcameraDevDllState() : dlopenHandle(nullptr, DlCloser{}), lcameraDev_main(nullptr), lcameraDev_exit(nullptr), lcameraDev_getOrCreateDeviceCReq(nullptr), lcameraDev_resolveDeviceSelectorCReq(nullptr), lcameraDev_releaseDeviceCReq(nullptr), lcameraDev_configureSessionModeCReq(nullptr) {} std::shared_ptr findStimProducerByCameraId( const std::string& resolvedCameraId) { for (const std::shared_ptr& producer : managedStimulusProducers) { assert(producer != nullptr); if (producer->resolvedCameraId == resolvedCameraId) { return producer; } } return nullptr; } bool validateAttachRequest( const std::shared_ptr& spec) { if (!spec) { std::cerr << __func__ << ": null DeviceAttachmentSpec\n"; return false; } if (spec->stimBuffApi != "lcameraBuff") { std::cerr << __func__ << ": stimBuffApi must be lcameraBuff, got '" << spec->stimBuffApi << "'\n"; return false; } if (spec->provider != "lcameraDev") { std::cerr << __func__ << ": provider must be lcameraDev(), got '" << spec->provider << "'\n"; return false; } if (!YuvStimProducer::supportsQualeIfaceApi(spec->qualeIfaceApi)) { std::cerr << __func__ << ": unsupported qualeIfaceApi '" << spec->qualeIfaceApi << "'\n"; return false; } return true; } namespace { void stopAllManagedProducersBeforeFinalize() { for (const std::shared_ptr& producer : managedStimulusProducers) { assert(producer != nullptr); producer->stop(); } } void loadLcameraDevSymbols() { lcameraDevDll.lcameraDev_main = reinterpret_cast( dlsym(lcameraDevDll.dlopenHandle.get(), "lcameraDev_main")); lcameraDevDll.lcameraDev_exit = reinterpret_cast( dlsym(lcameraDevDll.dlopenHandle.get(), "lcameraDev_exit")); lcameraDevDll.lcameraDev_getOrCreateDeviceCReq = reinterpret_cast< lcameraDev_getOrCreateDeviceCReqFn *>( dlsym( lcameraDevDll.dlopenHandle.get(), "lcameraDev_getOrCreateDeviceCReq")); lcameraDevDll.lcameraDev_resolveDeviceSelectorCReq = reinterpret_cast< lcameraDev_resolveDeviceSelectorCReqFn *>( dlsym( lcameraDevDll.dlopenHandle.get(), "lcameraDev_resolveDeviceSelectorCReq")); lcameraDevDll.lcameraDev_releaseDeviceCReq = reinterpret_cast< lcameraDev_releaseDeviceCReqFn *>( dlsym( lcameraDevDll.dlopenHandle.get(), "lcameraDev_releaseDeviceCReq")); lcameraDevDll.lcameraDev_configureSessionModeCReq = reinterpret_cast< lcameraDev_configureSessionModeCReqFn *>( dlsym( lcameraDevDll.dlopenHandle.get(), "lcameraDev_configureSessionModeCReq")); if (!lcameraDevDll.lcameraDev_main || !lcameraDevDll.lcameraDev_exit || !lcameraDevDll.lcameraDev_getOrCreateDeviceCReq || !lcameraDevDll.lcameraDev_resolveDeviceSelectorCReq || !lcameraDevDll.lcameraDev_releaseDeviceCReq || !lcameraDevDll.lcameraDev_configureSessionModeCReq) { throw std::runtime_error( std::string(__func__) + ": failed to resolve lcameraDev library symbols"); } } sscl::co::ViralNonPostingInvoker attachChannelBufferToProducer( const std::shared_ptr& desc, const std::shared_ptr& producer) { producer->getOrCreateAttachedStimulusBuffer(desc); co_return StimBuffDeviceOpResult{true, desc}; } sscl::co::ViralNonPostingInvoker attachToExistingProducer( const std::shared_ptr& desc, const std::shared_ptr& producer) { const LcameraBuffParsedParams parsedParams = parseLcameraBuffParams(*desc); const lcamera_dev::LcameraDevCameraModeRequest modeRequest = toCameraModeRequest(parsedParams); co_await (*lcameraDevDll.lcameraDev_configureSessionModeCReq)( producer->deviceSession, modeRequest); co_return co_await attachChannelBufferToProducer(desc, producer); } sscl::co::ViralNonPostingInvoker attachByCreatingProducer( const std::shared_ptr& desc, const std::shared_ptr& componentThread, const lcamera_dev::LcameraDevGetOrCreateResult& createResult) { const LcameraBuffParsedParams parsedParams = parseLcameraBuffParams(*desc); const lcamera_dev::LcameraDevCameraModeRequest modeRequest = toCameraModeRequest(parsedParams); const lcamera_dev::LcameraDevConfiguredCameraMode configuredMode = co_await (*lcameraDevDll.lcameraDev_configureSessionModeCReq)( createResult.deviceSession, modeRequest); auto producer = std::make_shared( desc, componentThread->getIoContext(), createResult.deviceSession, createResult.resolvedIdentity, parsedParams, configuredMode); addManagedStimulusProducer(producer); co_return co_await attachChannelBufferToProducer(desc, producer); } } // namespace sscl::co::ViralNonPostingInvoker lcameraBuff_initializeCInd() { if (!lcameraBuffSmoHooksPtr) { throw std::runtime_error( std::string(__func__) + ": SMO hooks not initialized"); } const std::optional libPath = lcameraBuffSmoHooksPtr->searchForLibInSmoSearchPaths( "liblcameraDev.so.0"); lcameraDevDll.dlopenHandle.reset( dlopen( libPath.value_or("liblcameraDev.so.0").c_str(), RTLD_LAZY)); if (!lcameraDevDll.dlopenHandle) { throw std::runtime_error( std::string(__func__) + ": failed to load lcameraDev library: " + (dlerror() ? dlerror() : "unknown error")); } loadLcameraDevSymbols(); (*lcameraDevDll.lcameraDev_main)( lcameraBuffThreadingModelDesc.componentThread); co_return 0; } sscl::co::ViralNonPostingInvoker lcameraBuff_finalizeCInd() { stopAllManagedProducersBeforeFinalize(); managedStimulusProducers.clear(); if (lcameraDevDll.lcameraDev_exit) { (*lcameraDevDll.lcameraDev_exit)(); } lcameraDevDll.dlopenHandle.reset(nullptr); lcameraDevDll = LcameraDevDllState(); co_return 0; } sscl::co::DynamicViralPostingInvoker lcameraBuff_attachDeviceCReq( sscl::co::ExplicitPostTarget, const std::shared_ptr& desc, const std::shared_ptr& componentThread) { if (!validateAttachRequest(desc)) { co_return StimBuffDeviceOpResult{false, desc}; } const lcamera_dev::LcameraDevGetOrCreateResult createResult = co_await (*lcameraDevDll.lcameraDev_getOrCreateDeviceCReq)( desc->deviceSelector); const std::string resolvedCameraId = createResult.resolvedIdentity.id; auto existingProducer = findStimProducerByCameraId(resolvedCameraId); if (existingProducer) { co_return co_await attachToExistingProducer(desc, existingProducer); } co_return co_await attachByCreatingProducer( desc, componentThread, createResult); } sscl::co::DynamicViralPostingInvoker lcameraBuff_detachDeviceCReq( [[maybe_unused]] sscl::co::ExplicitPostTarget postTarget, const std::shared_ptr& desc) { (void)postTarget; if (!validateAttachRequest(desc)) { co_return StimBuffDeviceOpResult{false, desc}; } const lcamera_dev::CameraIdentityRecord resolvedIdentity = co_await (*lcameraDevDll.lcameraDev_resolveDeviceSelectorCReq)( desc->deviceSelector); auto producer = findStimProducerByCameraId(resolvedIdentity.id); if (!producer) { co_return StimBuffDeviceOpResult{true, desc}; } auto stimBuffer = producer->getAttachedStimulusBufferByAttachIdentity( desc->deviceIdentifier, desc->qualeIfaceApi); if (!stimBuffer) { co_return StimBuffDeviceOpResult{true, desc}; } producer->destroyAttachedStimulusBuffer(stimBuffer); if (!producer->attachedStimulusBuffers.empty()) { co_return StimBuffDeviceOpResult{true, desc}; } co_await removeManagedStimulusProducerIfUnused(producer); co_return StimBuffDeviceOpResult{true, desc}; } } // namespace smo::stim_buff::lcamera_buff extern "C" smo::stim_buff::SMO_GET_STIM_BUFF_API_DESC_FN_TYPEDEF SMO_GET_STIM_BUFF_API_DESC_FN_NAME; namespace { static const smo::stim_buff::StimBuffApiDesc lcameraBuffApiDesc = { .name = "lcameraBuff", .exportedQualeIfaceApis = { {.name = "colour-yuv-y"}, {.name = "colour-yuv-u"}, {.name = "colour-yuv-v"}, }, .sal_mgmt_libOps = { .initializeCInd = smo::stim_buff::lcamera_buff::lcameraBuff_initializeCInd, .finalizeCInd = smo::stim_buff::lcamera_buff::lcameraBuff_finalizeCInd, .attachDeviceCReq = smo::stim_buff::lcamera_buff::lcameraBuff_attachDeviceCReq, .detachDeviceCReq = smo::stim_buff::lcamera_buff::lcameraBuff_detachDeviceCReq, }, }; } // namespace const smo::stim_buff::StimBuffApiDesc& SMO_GET_STIM_BUFF_API_DESC_FN_NAME( const smo::stim_buff::SmoCallbacks& callbacks, const smo::stim_buff::SmoThreadingModelDesc& threadingModel) { smo::stim_buff::lcamera_buff::lcameraBuffSmoHooksPtr = &callbacks; smo::stim_buff::lcamera_buff::lcameraBuffThreadingModelDesc = threadingModel; return lcameraBuffApiDesc; }