Files
salmanoff/commonLibs/lcameraDev/cameraManagerState.cpp
T
hayodea 7af684039d lcameraDev: add resolve-only deviceSelector API and deduplicate resolve paths.
Export lcameraDev_resolveDeviceSelectorCReq for attach-identity consumers,
factor live-camera snapshot helpers, and share resolveDeviceSelectorAgainstRecords
with get-or-create session acquisition.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 11:02:11 -04:00

292 lines
7.2 KiB
C++

#include <boostAsioLinkageFix.h>
#include <cameraIdentity.h>
#include <cameraManagerState.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;
}
struct LiveCameraSnapshot
{
std::vector<std::shared_ptr<libcamera::Camera>> cameras;
std::vector<CameraIdentityRecord> identityRecords;
};
LiveCameraSnapshot snapshotLiveCameras(CameraManagerResources& resources)
{
LiveCameraSnapshot snapshot;
snapshot.cameras = resources.cameraManager->cameras();
snapshot.identityRecords = buildIdentityRecords(snapshot.cameras);
return snapshot;
}
CameraIdentityRecord resolveDeviceSelectorForLiveCameras(
const std::string& deviceSelector,
CameraManagerResources& resources)
{
const LiveCameraSnapshot snapshot = snapshotLiveCameras(resources);
return resolveDeviceSelectorAgainstRecords(
deviceSelector, snapshot.identityRecords);
}
void requireNonEmptyDeviceSelector(
const std::string& deviceSelector,
const char *apiName)
{
if (deviceSelector.empty())
{
throw std::runtime_error(
std::string(apiName) + ": deviceSelector is empty");
}
}
} // 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<CameraIdentityRecord>
resolveDeviceSelectorCReq(const std::string& deviceSelector)
{
requireNonEmptyDeviceSelector(
deviceSelector, "lcameraDev_resolveDeviceSelectorCReq");
LcameraDevState& state = getLcameraDevState();
sscl::co::CoQutex::ReleaseHandle managerGuard =
co_await state.managerState.lock.getAcquireInvocationAndSuspensionPolicy();
co_return resolveDeviceSelectorForLiveCameras(
deviceSelector, state.managerState.rsrc);
}
sscl::co::ViralNonPostingInvoker<LcameraDevGetOrCreateResult>
getOrCreateDeviceSessionCReq(const std::string& deviceSelector)
{
requireNonEmptyDeviceSelector(
deviceSelector, "lcameraDev_getOrCreateDeviceCReq");
LcameraDevState& state = getLcameraDevState();
sscl::co::CoQutex::ReleaseHandle managerGuard =
co_await state.managerState.lock.getAcquireInvocationAndSuspensionPolicy();
const LiveCameraSnapshot snapshot =
snapshotLiveCameras(state.managerState.rsrc);
const CameraIdentityRecord resolvedRecord =
resolveDeviceSelectorAgainstRecords(
deviceSelector, snapshot.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(snapshot.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 LiveCameraSnapshot snapshot =
snapshotLiveCameras(state.managerState.rsrc);
std::vector<LcameraDevCameraInfo> cameraInfos;
cameraInfos.reserve(snapshot.identityRecords.size());
for (const CameraIdentityRecord& record : snapshot.identityRecords)
{
cameraInfos.push_back(LcameraDevCameraInfo{
record.id,
record.model,
record.locationLabel
});
}
co_return cameraInfos;
}
} // namespace lcamera_dev