#include #include #include #include #include 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 widthParamSynonyms = { "frame-width", "frame-w", "dimension-width", "dimension-w", "dim-width", "dim-w", }; const std::vector heightParamSynonyms = { "frame-height", "frame-h", "dimension-height", "dimension-h", "dim-height", "dim-h", }; const std::vector verticalResolutionParamSynonyms = { "vertical-resolution", "v-resolution", "v-res", "vres", }; const std::vector colourSpaceParamSynonyms = { "colorspace", "color-space", "colourspace", "colour-space", }; const std::vector 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& verticalResolutionPresetTable() { static const std::unordered_map 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& 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>& params, LcameraBuffParsedParams& parsedParams) { const std::optional> widthParam = device::DeviceAttachmentSpec::findOptionalParamWithSynonyms( params, widthParamSynonyms); const std::optional> heightParam = device::DeviceAttachmentSpec::findOptionalParamWithSynonyms( params, heightParamSynonyms); if (!widthParam && !heightParam) { return; } const std::optional> 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( device::DeviceAttachmentSpec::parseParamValueAsPositiveInt( widthParam->first, widthParam->second)); parsedParams.height = static_cast( device::DeviceAttachmentSpec::parseParamValueAsPositiveInt( heightParam->first, heightParam->second)); } void applyVerticalResolutionShorthand( const std::vector>& params, LcameraBuffParsedParams& parsedParams) { const std::optional 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>& params, LcameraBuffParsedParams& parsedParams) { const std::optional 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