Files

378 lines
10 KiB
C++
Raw Permalink Normal View History

#include <boostAsioLinkageFix.h>
#include <lcameraBuffInternal.h>
#include <lcameraBuffParams.h>
#include <yuvStimProducer.h>
#include <algorithm>
#include <cassert>
#include <dlfcn.h>
#include <iostream>
#include <stdexcept>
namespace smo::stim_buff::lcamera_buff {
const SmoCallbacks *lcameraBuffSmoHooksPtr = nullptr;
SmoThreadingModelDesc lcameraBuffThreadingModelDesc;
LcameraDevDllState lcameraDevDll;
std::vector<std::shared_ptr<YuvStimProducer>> managedStimulusProducers;
void addManagedStimulusProducer(
const std::shared_ptr<YuvStimProducer>& producer)
{
managedStimulusProducers.push_back(producer);
producer->start();
}
sscl::co::ViralNonPostingInvoker<void> removeManagedStimulusProducerIfUnused(
const std::shared_ptr<YuvStimProducer>& 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<YuvStimProducer>& candidate)
{
return candidate == producer;
}),
managedStimulusProducers.end());
const std::shared_ptr<lcamera_dev::CameraSession> 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<YuvStimProducer> findStimProducerByCameraId(
const std::string& resolvedCameraId)
{
for (const std::shared_ptr<YuvStimProducer>& producer :
managedStimulusProducers)
{
assert(producer != nullptr);
if (producer->resolvedCameraId == resolvedCameraId) {
return producer;
}
}
return nullptr;
}
bool validateAttachRequest(
const std::shared_ptr<device::DeviceAttachmentSpec>& 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<YuvStimProducer>& producer :
managedStimulusProducers)
{
assert(producer != nullptr);
producer->stop();
}
}
void loadLcameraDevSymbols()
{
lcameraDevDll.lcameraDev_main = reinterpret_cast<lcameraDev_mainFn *>(
dlsym(lcameraDevDll.dlopenHandle.get(), "lcameraDev_main"));
lcameraDevDll.lcameraDev_exit = reinterpret_cast<lcameraDev_exitFn *>(
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<StimBuffDeviceOpResult>
attachChannelBufferToProducer(
const std::shared_ptr<device::DeviceAttachmentSpec>& desc,
const std::shared_ptr<YuvStimProducer>& producer)
{
producer->getOrCreateAttachedStimulusBuffer(desc);
co_return StimBuffDeviceOpResult{true, desc};
}
sscl::co::ViralNonPostingInvoker<StimBuffDeviceOpResult>
attachToExistingProducer(
const std::shared_ptr<device::DeviceAttachmentSpec>& desc,
const std::shared_ptr<YuvStimProducer>& 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<StimBuffDeviceOpResult>
attachByCreatingProducer(
const std::shared_ptr<device::DeviceAttachmentSpec>& desc,
const std::shared_ptr<sscl::ComponentThread>& 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<YuvStimProducer>(
desc,
componentThread->getIoContext(),
createResult.deviceSession,
createResult.resolvedIdentity,
parsedParams,
configuredMode);
addManagedStimulusProducer(producer);
co_return co_await attachChannelBufferToProducer(desc, producer);
}
} // namespace
sscl::co::ViralNonPostingInvoker<int> lcameraBuff_initializeCInd()
{
if (!lcameraBuffSmoHooksPtr)
{
throw std::runtime_error(
std::string(__func__) + ": SMO hooks not initialized");
}
const std::optional<std::string> 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<int> lcameraBuff_finalizeCInd()
{
stopAllManagedProducersBeforeFinalize();
managedStimulusProducers.clear();
if (lcameraDevDll.lcameraDev_exit) {
(*lcameraDevDll.lcameraDev_exit)();
}
lcameraDevDll.dlopenHandle.reset(nullptr);
lcameraDevDll = LcameraDevDllState();
co_return 0;
}
sscl::co::DynamicViralPostingInvoker<StimBuffDeviceOpResult>
lcameraBuff_attachDeviceCReq(
sscl::co::ExplicitPostTarget,
const std::shared_ptr<device::DeviceAttachmentSpec>& desc,
const std::shared_ptr<sscl::ComponentThread>& 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<StimBuffDeviceOpResult>
lcameraBuff_detachDeviceCReq(
[[maybe_unused]] sscl::co::ExplicitPostTarget postTarget,
const std::shared_ptr<device::DeviceAttachmentSpec>& 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;
}