7af684039d
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>
292 lines
7.2 KiB
C++
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
|