e7b7a311f7
Introduce params parsing, pixel/format decisions, capture layout, shared YuvStimProducer per camera, and channel stimulus buffers with attach flow. Co-authored-by: Cursor <cursoragent@cursor.com>
355 lines
9.6 KiB
C++
355 lines
9.6 KiB
C++
#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>> attachedStimulusProducers;
|
|
|
|
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_releaseDeviceCReq(nullptr),
|
|
lcameraDev_configureSessionModeCReq(nullptr)
|
|
{}
|
|
|
|
std::shared_ptr<YuvStimProducer> findStimProducerByCameraId(
|
|
const std::string& resolvedCameraId)
|
|
{
|
|
for (const std::shared_ptr<YuvStimProducer>& producer :
|
|
attachedStimulusProducers)
|
|
{
|
|
assert(producer != nullptr);
|
|
if (producer->resolvedCameraId == resolvedCameraId) {
|
|
return producer;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
std::shared_ptr<YuvStimProducer> findStimProducerWithAttachedBuffer(
|
|
const std::shared_ptr<device::DeviceAttachmentSpec>& desc)
|
|
{
|
|
for (const std::shared_ptr<YuvStimProducer>& producer :
|
|
attachedStimulusProducers)
|
|
{
|
|
assert(producer != nullptr);
|
|
if (producer->getAttachedStimulusBufferByAttachIdentity(
|
|
desc->deviceIdentifier, desc->qualeIfaceApi))
|
|
{
|
|
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 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_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_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);
|
|
|
|
attachedStimulusProducers.push_back(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");
|
|
|
|
lcameraDevDll.dlopenHandle.reset(
|
|
dlopen(
|
|
libPath.value_or("liblcameraDev.so").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()
|
|
{
|
|
for (const std::shared_ptr<YuvStimProducer>& producer :
|
|
attachedStimulusProducers)
|
|
{
|
|
assert(producer != nullptr);
|
|
producer->deviceSession.reset();
|
|
}
|
|
|
|
attachedStimulusProducers.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};
|
|
}
|
|
|
|
auto producer = findStimProducerWithAttachedBuffer(desc);
|
|
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);
|
|
|
|
const std::shared_ptr<lcamera_dev::CameraSession> deviceSession =
|
|
producer->deviceSession;
|
|
|
|
co_await (*lcameraDevDll.lcameraDev_releaseDeviceCReq)(deviceSession);
|
|
|
|
if (!producer->attachedStimulusBuffers.empty()) {
|
|
co_return StimBuffDeviceOpResult{true, desc};
|
|
}
|
|
|
|
attachedStimulusProducers.erase(
|
|
std::remove_if(
|
|
attachedStimulusProducers.begin(),
|
|
attachedStimulusProducers.end(),
|
|
[&producer](const std::shared_ptr<YuvStimProducer>& candidate)
|
|
{
|
|
return candidate == producer;
|
|
}),
|
|
attachedStimulusProducers.end());
|
|
|
|
producer->deviceSession.reset();
|
|
|
|
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;
|
|
}
|