lcameraDev: Add session mgr lib for libcamera device binding
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
#include <boostAsioLinkageFix.h>
|
||||
|
||||
#include <cameraIdentity.h>
|
||||
#include <cameraManagerState.h>
|
||||
#include <selectorParse.h>
|
||||
#include <selectorResolve.h>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace lcamera_dev {
|
||||
|
||||
namespace {
|
||||
|
||||
static LcameraDevState lcameraDevState;
|
||||
|
||||
void startCameraManager(CameraManagerResources& resources)
|
||||
{
|
||||
resources.cameraManager = std::make_unique<libcamera::CameraManager>();
|
||||
|
||||
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<libcamera::Camera> findCameraById(
|
||||
const std::vector<std::shared_ptr<libcamera::Camera>>& cameras,
|
||||
const std::string& cameraId)
|
||||
{
|
||||
for (const std::shared_ptr<libcamera::Camera>& camera : cameras)
|
||||
{
|
||||
if (camera && camera->id() == cameraId) {
|
||||
return camera;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
LcameraDevState& getLcameraDevState()
|
||||
{
|
||||
return lcameraDevState;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<libcamera::Camera>> listLibcameraCameras()
|
||||
{
|
||||
LcameraDevState& state = getLcameraDevState();
|
||||
if (!state.isInitialized || !state.managerState.rsrc.cameraManager) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return state.managerState.rsrc.cameraManager->cameras();
|
||||
}
|
||||
|
||||
void lcameraDevMain(
|
||||
const std::shared_ptr<sscl::ComponentThread>& 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<CameraSession> 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<LcameraDevGetOrCreateResult>
|
||||
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<SelectorCriterion> criteria =
|
||||
parseDeviceSelector(deviceSelector);
|
||||
const std::vector<std::shared_ptr<libcamera::Camera>> cameras =
|
||||
state.managerState.rsrc.cameraManager->cameras();
|
||||
const std::vector<CameraIdentityRecord> 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<CameraSession> 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<libcamera::Camera> 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<CameraSession> session =
|
||||
std::make_shared<CameraSession>(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<void>
|
||||
releaseDeviceSessionCReq(
|
||||
const std::shared_ptr<CameraSession>& 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<std::vector<LcameraDevCameraInfo>>
|
||||
enumerateCamerasCReq()
|
||||
{
|
||||
LcameraDevState& state = getLcameraDevState();
|
||||
sscl::co::CoQutex::ReleaseHandle managerGuard =
|
||||
co_await state.managerState.lock.getAcquireInvocationAndSuspensionPolicy();
|
||||
|
||||
const std::vector<std::shared_ptr<libcamera::Camera>> cameras =
|
||||
state.managerState.rsrc.cameraManager->cameras();
|
||||
const std::vector<CameraIdentityRecord> identityRecords =
|
||||
buildIdentityRecords(cameras);
|
||||
|
||||
std::vector<LcameraDevCameraInfo> 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
|
||||
Reference in New Issue
Block a user