Add lcameraBuff Stage 2 plugin with YUV attach and unit tests.

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>
This commit is contained in:
2026-06-14 11:03:19 -04:00
parent 809861be2b
commit e7b7a311f7
18 changed files with 1698 additions and 0 deletions
@@ -0,0 +1,290 @@
#include <lcameraBuffParams.h>
#include <sstream>
#include <stdexcept>
#include <unordered_map>
#include <vector>
namespace smo {
namespace stim_buff {
namespace lcamera_buff {
namespace {
constexpr const char *mutuallyExclusiveResolutionMessage =
"lcameraBuff: explicit dimension width/height params cannot be combined "
"with vertical-resolution shorthand params";
constexpr const char *missingResolutionMessage =
"lcameraBuff: specify either explicit dimension width and height, or a "
"vertical-resolution shorthand (v-res, vres, etc.)";
constexpr const char *incompleteExplicitResolutionMessage =
"lcameraBuff: both dimension width and height are required when using "
"explicit dimension params";
const std::vector<std::string> widthParamSynonyms = {
"frame-width",
"frame-w",
"dimension-width",
"dimension-w",
"dim-width",
"dim-w",
};
const std::vector<std::string> heightParamSynonyms = {
"frame-height",
"frame-h",
"dimension-height",
"dimension-h",
"dim-height",
"dim-h",
};
const std::vector<std::string> verticalResolutionParamSynonyms = {
"vertical-resolution",
"v-resolution",
"v-res",
"vres",
};
const std::vector<std::string> colourSpaceParamSynonyms = {
"colorspace",
"color-space",
"colourspace",
"colour-space",
};
const std::vector<std::string> optPlanarParamSynonyms = {
"full-planar-is-optional",
"opt-planar",
};
std::string normalizeVerticalResolutionToken(const std::string& token)
{
std::string normalizedToken =
device::DeviceAttachmentSpec::normalizeParamToken(token);
if (normalizedToken.empty())
{
throw std::runtime_error(
"lcameraBuff: vertical-resolution value is empty");
}
if (normalizedToken.back() == 'p')
{
normalizedToken.pop_back();
}
if (normalizedToken.empty())
{
throw std::runtime_error(
"lcameraBuff: vertical-resolution value is empty after "
"normalization");
}
return normalizedToken;
}
struct VerticalResolutionPreset
{
unsigned width;
unsigned height;
};
const std::unordered_map<std::string, VerticalResolutionPreset>&
verticalResolutionPresetTable()
{
static const std::unordered_map<std::string, VerticalResolutionPreset>
presets = {
{"360", {640, 360}},
{"480", {640, 480}},
{"720", {1280, 720}},
{"1080", {1920, 1080}},
};
return presets;
}
VerticalResolutionPreset resolveVerticalResolutionPreset(
const std::string& token)
{
const std::string normalizedToken =
normalizeVerticalResolutionToken(token);
const std::unordered_map<std::string, VerticalResolutionPreset>& presets =
verticalResolutionPresetTable();
const auto presetIt = presets.find(normalizedToken);
if (presetIt == presets.end())
{
std::ostringstream supportedStream;
bool first = true;
for (const auto& entry : presets)
{
if (!first) {
supportedStream << ", ";
}
supportedStream << entry.first << "p";
first = false;
}
throw std::runtime_error(
"lcameraBuff: unsupported vertical-resolution '"
+ token + "'; supported values: "
+ supportedStream.str());
}
return presetIt->second;
}
lcamera_dev::LcameraDevColourSpace parseColourSpaceValue(
const std::string& value)
{
const std::string lowered =
device::DeviceAttachmentSpec::normalizeParamToken(value);
if (lowered == "yuv") {
return lcamera_dev::LcameraDevColourSpace::Yuv;
}
throw std::runtime_error(
"lcameraBuff: unsupported colour-space '" + value
+ "'; supported values: yuv");
}
void applyExplicitDimensions(
const std::vector<std::pair<std::string, std::string>>& params,
LcameraBuffParsedParams& parsedParams)
{
const std::optional<std::pair<std::string, std::string>> widthParam =
device::DeviceAttachmentSpec::findOptionalParamWithSynonyms(
params, widthParamSynonyms);
const std::optional<std::pair<std::string, std::string>> heightParam =
device::DeviceAttachmentSpec::findOptionalParamWithSynonyms(
params, heightParamSynonyms);
if (!widthParam && !heightParam) { return; }
const std::optional<std::pair<std::string, std::string>>
verticalResolutionParam =
device::DeviceAttachmentSpec::findOptionalParamWithSynonyms(
params, verticalResolutionParamSynonyms);
if (verticalResolutionParam) {
throw std::runtime_error(mutuallyExclusiveResolutionMessage);
}
if (!widthParam || !heightParam) {
throw std::runtime_error(incompleteExplicitResolutionMessage);
}
parsedParams.width = static_cast<unsigned>(
device::DeviceAttachmentSpec::parseParamValueAsPositiveInt(
widthParam->first, widthParam->second));
parsedParams.height = static_cast<unsigned>(
device::DeviceAttachmentSpec::parseParamValueAsPositiveInt(
heightParam->first, heightParam->second));
}
void applyVerticalResolutionShorthand(
const std::vector<std::pair<std::string, std::string>>& params,
LcameraBuffParsedParams& parsedParams)
{
const std::optional<std::string> verticalResolutionValue =
device::DeviceAttachmentSpec::parseOptionalParamValueWithSynonyms(
params,
verticalResolutionParamSynonyms,
"lcameraBuff: vertical-resolution param requires a value "
"(e.g. v-res=480p)");
if (!verticalResolutionValue) { return; }
const VerticalResolutionPreset preset =
resolveVerticalResolutionPreset(*verticalResolutionValue);
parsedParams.width = preset.width;
parsedParams.height = preset.height;
}
void applyColourSpaceParam(
const std::vector<std::pair<std::string, std::string>>& params,
LcameraBuffParsedParams& parsedParams)
{
const std::optional<std::string> colourSpaceValue =
device::DeviceAttachmentSpec::parseOptionalParamValueWithSynonyms(
params,
colourSpaceParamSynonyms,
"lcameraBuff: colour-space param requires a value "
"(e.g. colour-space=yuv)");
if (!colourSpaceValue) { return; }
parsedParams.colourSpace = parseColourSpaceValue(*colourSpaceValue);
}
void finalizeCheckResolutionValues(const LcameraBuffParsedParams& parsedParams)
{
if (parsedParams.width == 0 || parsedParams.height == 0)
{
throw std::runtime_error(missingResolutionMessage);
}
}
} // namespace
LcameraBuffParsedParams parseLcameraBuffParams(
const device::DeviceAttachmentSpec& spec)
{
const auto& params = spec.stimBuffApiParams;
device::DeviceAttachmentSpec::rejectUnknownParams(
params,
"lcameraBuff: unknown stimbuff-api param '",
widthParamSynonyms,
heightParamSynonyms,
verticalResolutionParamSynonyms,
colourSpaceParamSynonyms,
optPlanarParamSynonyms);
LcameraBuffParsedParams parsedParams;
applyExplicitDimensions(params, parsedParams);
applyVerticalResolutionShorthand(params, parsedParams);
applyColourSpaceParam(params, parsedParams);
/* Presence flag: opt-planar or opt-planar= with no value means true. */
parsedParams.fullPlanarIsOptional =
device::DeviceAttachmentSpec::parseOptionalParamAsBoolWithSynonyms(
params,
optPlanarParamSynonyms,
/*defaultValue=*/false,
/*emptyMeansTrue=*/true);
finalizeCheckResolutionValues(parsedParams);
return parsedParams;
}
lcamera_dev::LcameraDevCameraModeRequest toCameraModeRequest(
const LcameraBuffParsedParams& parsedParams)
{
lcamera_dev::LcameraDevCameraModeRequest request;
request.width = parsedParams.width;
request.height = parsedParams.height;
request.colourSpace = parsedParams.colourSpace;
request.fullPlanarIsOptional = parsedParams.fullPlanarIsOptional;
return request;
}
bool lcameraBuffParamsEqual(
const LcameraBuffParsedParams& left,
const LcameraBuffParsedParams& right)
{
return left.width == right.width
&& left.height == right.height
&& left.colourSpace == right.colourSpace
&& left.fullPlanarIsOptional == right.fullPlanarIsOptional;
}
} // namespace lcamera_buff
} // namespace stim_buff
} // namespace smo