feat(downstream): add cvmmap downstream runtime implementation

This commit introduces the full downstream runtime implementation needed to ingest, transform, and publish streams.

It preserves the original upstream request boundary by packaging the entire cvmmap-streamer module (build config, public API, protocol and IPC glue, and simulator/tester entrypoints) in one coherent core unit.

Keeping this group isolated enables reviewers to validate runtime behavior and correctness without mixing test evidence or process documentation changes.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-03-05 20:31:58 +08:00
commit 56e874ab6d
27 changed files with 8483 additions and 0 deletions
+524
View File
@@ -0,0 +1,524 @@
#include "cvmmap_streamer/config/runtime_config.hpp"
#include <charconv>
#include <cstdint>
#include <limits>
#include <sstream>
#include <string_view>
#include <utility>
namespace cvmmap_streamer {
namespace {
std::expected<std::string_view, std::string>
next_value(int argc, char **argv, int &index, std::string_view flag_name) {
if (index + 1 >= argc) {
return std::unexpected("missing value for " + std::string(flag_name));
}
++index;
return std::string_view{argv[index]};
}
std::expected<std::uint32_t, std::string> parse_u32(std::string_view raw, std::string_view flag_name) {
std::uint32_t value{0};
const auto *begin = raw.data();
const auto *end = raw.data() + raw.size();
const auto result = std::from_chars(begin, end, value, 10);
if (result.ec != std::errc{} || result.ptr != end) {
return std::unexpected("invalid value for " + std::string(flag_name) + ": '" + std::string(raw) + "'");
}
return value;
}
std::expected<std::uint16_t, std::string> parse_u16(std::string_view raw, std::string_view flag_name) {
std::uint16_t value{0};
const auto *begin = raw.data();
const auto *end = raw.data() + raw.size();
const auto result = std::from_chars(begin, end, value, 10);
if (result.ec != std::errc{} || result.ptr != end) {
return std::unexpected("invalid value for " + std::string(flag_name) + ": '" + std::string(raw) + "'");
}
return value;
}
std::expected<std::size_t, std::string> parse_size(std::string_view raw, std::string_view flag_name) {
unsigned long long parsed{0};
const auto *begin = raw.data();
const auto *end = raw.data() + raw.size();
const auto result = std::from_chars(begin, end, parsed, 10);
if (result.ec != std::errc{} || result.ptr != end) {
return std::unexpected("invalid value for " + std::string(flag_name) + ": '" + std::string(raw) + "'");
}
if (parsed > static_cast<unsigned long long>(std::numeric_limits<std::size_t>::max())) {
return std::unexpected("value out of range for " + std::string(flag_name) + ": '" + std::string(raw) + "'");
}
return static_cast<std::size_t>(parsed);
}
std::expected<bool, std::string> parse_bool(std::string_view raw, std::string_view flag_name) {
if (raw == "true" || raw == "1") {
return true;
}
if (raw == "false" || raw == "0") {
return false;
}
return std::unexpected(
"invalid value for " + std::string(flag_name) + ": '" + std::string(raw) + "' (expected: true|false|1|0)");
}
std::expected<CodecType, std::string> parse_codec(std::string_view raw) {
if (raw == "h264") {
return CodecType::H264;
}
if (raw == "h265") {
return CodecType::H265;
}
return std::unexpected("invalid codec: '" + std::string(raw) + "' (expected: h264|h265)");
}
std::expected<RunMode, std::string> parse_run_mode(std::string_view raw) {
if (raw == "pipeline") {
return RunMode::Pipeline;
}
if (raw == "ingest") {
return RunMode::Ingest;
}
return std::unexpected("invalid run mode: '" + std::string(raw) + "' (expected: pipeline|ingest)");
}
std::expected<RtmpMode, std::string> parse_rtmp_mode(std::string_view raw) {
if (raw == "enhanced") {
return RtmpMode::Enhanced;
}
if (raw == "domestic") {
return RtmpMode::Domestic;
}
return std::unexpected("invalid rtmp mode: '" + std::string(raw) + "' (expected: enhanced|domestic)");
}
std::expected<std::pair<std::string, std::uint16_t>, std::string> parse_rtp_endpoint(std::string_view endpoint) {
if (endpoint.empty()) {
return std::unexpected("invalid RTP config: --rtp-endpoint must not be empty");
}
const auto colon = endpoint.rfind(':');
if (colon == std::string_view::npos || colon == 0 || colon + 1 >= endpoint.size()) {
return std::unexpected("invalid RTP config: --rtp-endpoint must be in '<host>:<port>' format");
}
const auto host = endpoint.substr(0, colon);
const auto port = endpoint.substr(colon + 1);
if (host.empty()) {
return std::unexpected("invalid RTP config: --rtp-endpoint host must not be empty");
}
auto parsed_port = parse_u16(port, "--rtp-endpoint");
if (!parsed_port) {
return std::unexpected(parsed_port.error());
}
if (*parsed_port == 0) {
return std::unexpected("invalid RTP config: --rtp-endpoint port must be in range [1,65535]");
}
return std::pair{std::string(host), *parsed_port};
}
}
RuntimeConfig RuntimeConfig::defaults() {
return RuntimeConfig{};
}
std::string_view to_string(CodecType codec) {
switch (codec) {
case CodecType::H264:
return "h264";
case CodecType::H265:
return "h265";
default:
return "unknown";
}
}
std::string_view to_string(RunMode mode) {
switch (mode) {
case RunMode::Pipeline:
return "pipeline";
case RunMode::Ingest:
return "ingest";
default:
return "unknown";
}
}
std::string_view to_string(RtmpMode mode) {
switch (mode) {
case RtmpMode::Enhanced:
return "enhanced";
case RtmpMode::Domestic:
return "domestic";
default:
return "unknown";
}
}
std::expected<RuntimeConfig, std::string> parse_runtime_config(int argc, char **argv) {
RuntimeConfig config = RuntimeConfig::defaults();
for (int i = 1; i < argc; ++i) {
const std::string_view arg{argv[i]};
if (arg == "--codec") {
auto raw = next_value(argc, argv, i, arg);
if (!raw) {
return std::unexpected(raw.error());
}
auto codec = parse_codec(*raw);
if (!codec) {
return std::unexpected(codec.error());
}
config.codec = *codec;
continue;
}
if (arg == "--run-mode") {
auto raw = next_value(argc, argv, i, arg);
if (!raw) {
return std::unexpected(raw.error());
}
auto mode = parse_run_mode(*raw);
if (!mode) {
return std::unexpected(mode.error());
}
config.run_mode = *mode;
continue;
}
if (arg == "--shm-name") {
auto value = next_value(argc, argv, i, arg);
if (!value) {
return std::unexpected(value.error());
}
config.input.shm_name = std::string(*value);
continue;
}
if (arg == "--zmq-endpoint") {
auto value = next_value(argc, argv, i, arg);
if (!value) {
return std::unexpected(value.error());
}
config.input.zmq_endpoint = std::string(*value);
continue;
}
if (arg == "--rtmp") {
config.outputs.rtmp.enabled = true;
continue;
}
if (arg == "--rtmp-url") {
auto value = next_value(argc, argv, i, arg);
if (!value) {
return std::unexpected(value.error());
}
config.outputs.rtmp.enabled = true;
config.outputs.rtmp.urls.emplace_back(*value);
continue;
}
if (arg == "--rtmp-mode") {
auto raw = next_value(argc, argv, i, arg);
if (!raw) {
return std::unexpected(raw.error());
}
auto mode = parse_rtmp_mode(*raw);
if (!mode) {
return std::unexpected(mode.error());
}
config.outputs.rtmp.mode = *mode;
continue;
}
if (arg == "--rtp") {
config.outputs.rtp.enabled = true;
continue;
}
if (arg == "--rtp-endpoint") {
auto value = next_value(argc, argv, i, arg);
if (!value) {
return std::unexpected(value.error());
}
auto endpoint = parse_rtp_endpoint(*value);
if (!endpoint) {
return std::unexpected(endpoint.error());
}
config.outputs.rtp.enabled = true;
config.outputs.rtp.endpoint = std::string(*value);
config.outputs.rtp.host = endpoint->first;
config.outputs.rtp.port = endpoint->second;
continue;
}
if (arg == "--rtp-payload-type") {
auto raw = next_value(argc, argv, i, arg);
if (!raw) {
return std::unexpected(raw.error());
}
auto value = parse_u32(*raw, arg);
if (!value) {
return std::unexpected(value.error());
}
if (*value > std::numeric_limits<std::uint8_t>::max()) {
return std::unexpected("value out of range for --rtp-payload-type: '" + std::string(*raw) + "'");
}
config.outputs.rtp.enabled = true;
config.outputs.rtp.payload_type = static_cast<std::uint8_t>(*value);
continue;
}
if (arg == "--rtp-sdp" || arg == "--sdp") {
auto value = next_value(argc, argv, i, arg);
if (!value) {
return std::unexpected(value.error());
}
if (value->empty()) {
return std::unexpected("invalid RTP config: " + std::string(arg) + " must not be empty");
}
config.outputs.rtp.enabled = true;
config.outputs.rtp.sdp_path = std::string(*value);
continue;
}
if (arg == "--queue-size") {
auto raw = next_value(argc, argv, i, arg);
if (!raw) {
return std::unexpected(raw.error());
}
auto parsed = parse_size(*raw, arg);
if (!parsed) {
return std::unexpected(parsed.error());
}
config.latency.queue_size = *parsed;
continue;
}
if (arg == "--gop") {
auto raw = next_value(argc, argv, i, arg);
if (!raw) {
return std::unexpected(raw.error());
}
auto parsed = parse_u32(*raw, arg);
if (!parsed) {
return std::unexpected(parsed.error());
}
config.latency.gop = *parsed;
continue;
}
if (arg == "--b-frames") {
auto raw = next_value(argc, argv, i, arg);
if (!raw) {
return std::unexpected(raw.error());
}
auto parsed = parse_u32(*raw, arg);
if (!parsed) {
return std::unexpected(parsed.error());
}
config.latency.b_frames = *parsed;
continue;
}
if (arg == "--realtime-sync") {
auto raw = next_value(argc, argv, i, arg);
if (!raw) {
return std::unexpected(raw.error());
}
auto parsed = parse_bool(*raw, arg);
if (!parsed) {
return std::unexpected(parsed.error());
}
config.latency.realtime_sync = *parsed;
continue;
}
if (arg == "--force-idr-on-reset") {
auto raw = next_value(argc, argv, i, arg);
if (!raw) {
return std::unexpected(raw.error());
}
auto parsed = parse_bool(*raw, arg);
if (!parsed) {
return std::unexpected(parsed.error());
}
config.latency.force_idr_on_reset = *parsed;
continue;
}
if (arg == "--ingest-max-frames") {
auto raw = next_value(argc, argv, i, arg);
if (!raw) {
return std::unexpected(raw.error());
}
auto parsed = parse_u32(*raw, arg);
if (!parsed) {
return std::unexpected(parsed.error());
}
config.latency.ingest_max_frames = *parsed;
continue;
}
if (arg == "--ingest-idle-timeout-ms") {
auto raw = next_value(argc, argv, i, arg);
if (!raw) {
return std::unexpected(raw.error());
}
auto parsed = parse_u32(*raw, arg);
if (!parsed) {
return std::unexpected(parsed.error());
}
config.latency.ingest_idle_timeout_ms = *parsed;
continue;
}
if (arg == "--ingest-consumer-delay-ms") {
auto raw = next_value(argc, argv, i, arg);
if (!raw) {
return std::unexpected(raw.error());
}
auto parsed = parse_u32(*raw, arg);
if (!parsed) {
return std::unexpected(parsed.error());
}
config.latency.ingest_consumer_delay_ms = *parsed;
continue;
}
if (arg == "--snapshot-copy-delay-us") {
auto raw = next_value(argc, argv, i, arg);
if (!raw) {
return std::unexpected(raw.error());
}
auto parsed = parse_u32(*raw, arg);
if (!parsed) {
return std::unexpected(parsed.error());
}
config.latency.snapshot_copy_delay_us = *parsed;
continue;
}
if (arg == "--emit-stall-ms") {
auto raw = next_value(argc, argv, i, arg);
if (!raw) {
return std::unexpected(raw.error());
}
auto parsed = parse_u32(*raw, arg);
if (!parsed) {
return std::unexpected(parsed.error());
}
config.latency.emit_stall_ms = *parsed;
continue;
}
if (arg == "--help" || arg == "-h" || arg == "--version") {
continue;
}
return std::unexpected("unknown argument: " + std::string(arg));
}
return config;
}
std::expected<void, std::string> validate_runtime_config(const RuntimeConfig &config) {
if (config.input.shm_name.empty()) {
return std::unexpected("invalid input config: --shm-name must not be empty");
}
if (config.input.zmq_endpoint.empty()) {
return std::unexpected("invalid input config: --zmq-endpoint must not be empty");
}
if (config.outputs.rtmp.enabled && config.outputs.rtmp.urls.empty()) {
return std::unexpected("invalid RTMP config: --rtmp requires at least one --rtmp-url");
}
for (const auto &url : config.outputs.rtmp.urls) {
if (url.empty()) {
return std::unexpected("invalid RTMP config: --rtmp-url must not be empty");
}
}
if (config.outputs.rtmp.mode == RtmpMode::Domestic && config.codec != CodecType::H265) {
return std::unexpected(
"invalid mode matrix: --rtmp-mode domestic requires --codec h265 (h264+domestic is unsupported)");
}
if (config.outputs.rtp.enabled) {
if (!config.outputs.rtp.endpoint || config.outputs.rtp.endpoint->empty()) {
return std::unexpected("invalid RTP config: --rtp requires --rtp-endpoint");
}
auto endpoint_validation = parse_rtp_endpoint(*config.outputs.rtp.endpoint);
if (!endpoint_validation) {
return std::unexpected(endpoint_validation.error());
}
if (config.outputs.rtp.payload_type < 96 || config.outputs.rtp.payload_type > 127) {
return std::unexpected(
"invalid RTP config: --rtp-payload-type must be in dynamic range [96,127]");
}
if (config.outputs.rtp.sdp_path && config.outputs.rtp.sdp_path->empty()) {
return std::unexpected("invalid RTP config: --rtp-sdp/--sdp must not be empty");
}
}
if (config.latency.queue_size == 0) {
return std::unexpected("invalid latency config: --queue-size must be >= 1");
}
if (config.latency.gop == 0) {
return std::unexpected("invalid latency config: --gop must be >= 1");
}
if (config.latency.b_frames > config.latency.gop) {
return std::unexpected("invalid latency config: --b-frames must be <= --gop");
}
if (config.latency.ingest_idle_timeout_ms == 0) {
return std::unexpected("invalid ingest config: --ingest-idle-timeout-ms must be >= 1");
}
return {};
}
std::string summarize_runtime_config(const RuntimeConfig &config) {
std::ostringstream ss;
ss << "input.shm=" << config.input.shm_name;
ss << ", input.zmq=" << config.input.zmq_endpoint;
ss << ", run_mode=" << to_string(config.run_mode);
ss << ", codec=" << to_string(config.codec);
ss << ", rtmp.enabled=" << (config.outputs.rtmp.enabled ? "true" : "false");
ss << ", rtmp.mode=" << to_string(config.outputs.rtmp.mode);
ss << ", rtmp.urls=" << config.outputs.rtmp.urls.size();
ss << ", rtp.enabled=" << (config.outputs.rtp.enabled ? "true" : "false");
ss << ", rtp.endpoint=" << (config.outputs.rtp.endpoint ? *config.outputs.rtp.endpoint : "<unset>");
ss << ", rtp.payload_type=" << static_cast<unsigned>(config.outputs.rtp.payload_type);
ss << ", rtp.sdp=" << (config.outputs.rtp.sdp_path ? *config.outputs.rtp.sdp_path : "<auto>");
ss << ", latency.queue_size=" << config.latency.queue_size;
ss << ", latency.gop=" << config.latency.gop;
ss << ", latency.b_frames=" << config.latency.b_frames;
ss << ", latency.realtime_sync=" << (config.latency.realtime_sync ? "true" : "false");
ss << ", latency.force_idr_on_reset=" << (config.latency.force_idr_on_reset ? "true" : "false");
ss << ", latency.ingest_max_frames=" << config.latency.ingest_max_frames;
ss << ", latency.ingest_idle_timeout_ms=" << config.latency.ingest_idle_timeout_ms;
ss << ", latency.ingest_consumer_delay_ms=" << config.latency.ingest_consumer_delay_ms;
ss << ", latency.snapshot_copy_delay_us=" << config.latency.snapshot_copy_delay_us;
ss << ", latency.emit_stall_ms=" << config.latency.emit_stall_ms;
return ss.str();
}
}