refactor(input): consume cvmmap-core package
This commit is contained in:
+4
-6
@@ -13,6 +13,7 @@ if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/lib/CLI11/CMakeLists.txt")
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(cppzmq QUIET)
|
find_package(cppzmq QUIET)
|
||||||
|
find_package(cvmmap-core CONFIG REQUIRED)
|
||||||
find_package(ZeroMQ QUIET)
|
find_package(ZeroMQ QUIET)
|
||||||
find_package(spdlog REQUIRED)
|
find_package(spdlog REQUIRED)
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
@@ -37,8 +38,6 @@ add_library(cvmmap_streamer_common STATIC
|
|||||||
src/core/ingest_runtime.cpp
|
src/core/ingest_runtime.cpp
|
||||||
src/ipc/contracts.cpp
|
src/ipc/contracts.cpp
|
||||||
src/protocol/wire_codec.cpp
|
src/protocol/wire_codec.cpp
|
||||||
lib/cvmmap-client-cpp/app_cvmmap_client.cpp
|
|
||||||
lib/cvmmap-client-cpp/app_cvmmap_parser.cpp
|
|
||||||
src/metrics/latency_tracker.cpp
|
src/metrics/latency_tracker.cpp
|
||||||
src/pipeline/pipeline_runtime.cpp
|
src/pipeline/pipeline_runtime.cpp
|
||||||
src/protocol/rtmp_publisher.cpp
|
src/protocol/rtmp_publisher.cpp
|
||||||
@@ -46,10 +45,7 @@ add_library(cvmmap_streamer_common STATIC
|
|||||||
|
|
||||||
target_include_directories(cvmmap_streamer_common
|
target_include_directories(cvmmap_streamer_common
|
||||||
PUBLIC
|
PUBLIC
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/include"
|
"${CMAKE_CURRENT_LIST_DIR}/include")
|
||||||
PRIVATE
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/lib/cvmmap-client-cpp"
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/lib/cvmmap-client-cpp/include")
|
|
||||||
set(CVMAP_STREAMER_LINK_DEPS Threads::Threads)
|
set(CVMAP_STREAMER_LINK_DEPS Threads::Threads)
|
||||||
|
|
||||||
if (TARGET cppzmq::cppzmq)
|
if (TARGET cppzmq::cppzmq)
|
||||||
@@ -90,6 +86,8 @@ if (TARGET CLI11::CLI11)
|
|||||||
list(APPEND CVMAP_STREAMER_LINK_DEPS CLI11::CLI11)
|
list(APPEND CVMAP_STREAMER_LINK_DEPS CLI11::CLI11)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
list(APPEND CVMAP_STREAMER_LINK_DEPS cvmmap::client)
|
||||||
|
|
||||||
target_link_libraries(cvmmap_streamer_common PUBLIC ${CVMAP_STREAMER_LINK_DEPS})
|
target_link_libraries(cvmmap_streamer_common PUBLIC ${CVMAP_STREAMER_LINK_DEPS})
|
||||||
|
|
||||||
function(add_cvmmap_binary target source)
|
function(add_cvmmap_binary target source)
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "cvmmap_streamer/ipc/contracts.hpp"
|
|
||||||
|
|
||||||
namespace cvmmap_streamer {
|
namespace cvmmap_streamer {
|
||||||
|
|
||||||
enum class CodecType {
|
enum class CodecType {
|
||||||
@@ -22,11 +20,6 @@ enum class RunMode {
|
|||||||
Ingest,
|
Ingest,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class InputMode {
|
|
||||||
Real,
|
|
||||||
Dummy,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class RtmpMode {
|
enum class RtmpMode {
|
||||||
Enhanced,
|
Enhanced,
|
||||||
Domestic,
|
Domestic,
|
||||||
@@ -37,21 +30,6 @@ struct InputConfig {
|
|||||||
std::string zmq_endpoint{"ipc:///tmp/cvmmap_default"};
|
std::string zmq_endpoint{"ipc:///tmp/cvmmap_default"};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DummyInputConfig {
|
|
||||||
std::uint32_t frames{0};
|
|
||||||
std::uint32_t fps{60};
|
|
||||||
std::uint16_t width{64};
|
|
||||||
std::uint16_t height{48};
|
|
||||||
std::optional<std::uint32_t> emit_reset_at{};
|
|
||||||
std::optional<std::uint32_t> emit_reset_every{};
|
|
||||||
std::string label{"dummy"};
|
|
||||||
std::uint8_t channels{3};
|
|
||||||
ipc::Depth depth{ipc::Depth::U8};
|
|
||||||
ipc::PixelFormat pixel_format{ipc::PixelFormat::BGR};
|
|
||||||
std::uint64_t start_timestamp_ns{1'000'000'000ull};
|
|
||||||
std::uint32_t startup_delay_ms{100};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RtmpOutputConfig {
|
struct RtmpOutputConfig {
|
||||||
bool enabled{false};
|
bool enabled{false};
|
||||||
std::vector<std::string> urls{};
|
std::vector<std::string> urls{};
|
||||||
@@ -87,8 +65,6 @@ struct LatencyConfig {
|
|||||||
|
|
||||||
struct RuntimeConfig {
|
struct RuntimeConfig {
|
||||||
InputConfig input{};
|
InputConfig input{};
|
||||||
DummyInputConfig dummy{};
|
|
||||||
InputMode input_mode{InputMode::Real};
|
|
||||||
RunMode run_mode{RunMode::Pipeline};
|
RunMode run_mode{RunMode::Pipeline};
|
||||||
CodecType codec{CodecType::H264};
|
CodecType codec{CodecType::H264};
|
||||||
OutputsConfig outputs{};
|
OutputsConfig outputs{};
|
||||||
@@ -99,7 +75,6 @@ struct RuntimeConfig {
|
|||||||
|
|
||||||
std::string_view to_string(CodecType codec);
|
std::string_view to_string(CodecType codec);
|
||||||
std::string_view to_string(RunMode mode);
|
std::string_view to_string(RunMode mode);
|
||||||
std::string_view to_string(InputMode mode);
|
|
||||||
std::string_view to_string(RtmpMode mode);
|
std::string_view to_string(RtmpMode mode);
|
||||||
|
|
||||||
std::expected<RuntimeConfig, std::string> parse_runtime_config(int argc, char **argv);
|
std::expected<RuntimeConfig, std::string> parse_runtime_config(int argc, char **argv);
|
||||||
|
|||||||
@@ -42,20 +42,10 @@ std::string normalize_cli_error(std::string raw_message) {
|
|||||||
return "unknown argument";
|
return "unknown argument";
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::array<std::string_view, 27> kFlags{"--codec",
|
constexpr std::array<std::string_view, 17> kFlags{"--codec",
|
||||||
"--run-mode",
|
"--run-mode",
|
||||||
"--input-mode",
|
|
||||||
"--shm-name",
|
"--shm-name",
|
||||||
"--zmq-endpoint",
|
"--zmq-endpoint",
|
||||||
"--dummy-frames",
|
|
||||||
"--dummy-fps",
|
|
||||||
"--dummy-width",
|
|
||||||
"--dummy-height",
|
|
||||||
"--dummy-reset-at",
|
|
||||||
"--dummy-reset-every",
|
|
||||||
"--dummy-label",
|
|
||||||
"--dummy-start-timestamp-ns",
|
|
||||||
"--dummy-startup-delay-ms",
|
|
||||||
"--rtmp-url",
|
"--rtmp-url",
|
||||||
"--rtmp-mode",
|
"--rtmp-mode",
|
||||||
"--rtp-endpoint",
|
"--rtp-endpoint",
|
||||||
@@ -95,19 +85,6 @@ parse_u32(std::string_view raw, std::string_view flag_name) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<std::uint64_t, std::string>
|
|
||||||
parse_u64(std::string_view raw, std::string_view flag_name) {
|
|
||||||
std::uint64_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>
|
std::expected<std::uint16_t, std::string>
|
||||||
parse_u16(std::string_view raw, std::string_view flag_name) {
|
parse_u16(std::string_view raw, std::string_view flag_name) {
|
||||||
std::uint16_t value{0};
|
std::uint16_t value{0};
|
||||||
@@ -173,17 +150,6 @@ std::expected<RunMode, std::string> parse_run_mode(std::string_view raw) {
|
|||||||
"' (expected: pipeline|ingest)");
|
"' (expected: pipeline|ingest)");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<InputMode, std::string> parse_input_mode(std::string_view raw) {
|
|
||||||
if (raw == "real") {
|
|
||||||
return InputMode::Real;
|
|
||||||
}
|
|
||||||
if (raw == "dummy") {
|
|
||||||
return InputMode::Dummy;
|
|
||||||
}
|
|
||||||
return std::unexpected("invalid input mode: '" + std::string(raw) +
|
|
||||||
"' (expected: real|dummy)");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::expected<RtmpMode, std::string> parse_rtmp_mode(std::string_view raw) {
|
std::expected<RtmpMode, std::string> parse_rtmp_mode(std::string_view raw) {
|
||||||
if (raw == "enhanced") {
|
if (raw == "enhanced") {
|
||||||
return RtmpMode::Enhanced;
|
return RtmpMode::Enhanced;
|
||||||
@@ -254,17 +220,6 @@ std::string_view to_string(RunMode mode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view to_string(InputMode mode) {
|
|
||||||
switch (mode) {
|
|
||||||
case InputMode::Real:
|
|
||||||
return "real";
|
|
||||||
case InputMode::Dummy:
|
|
||||||
return "dummy";
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view to_string(RtmpMode mode) {
|
std::string_view to_string(RtmpMode mode) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case RtmpMode::Enhanced:
|
case RtmpMode::Enhanced:
|
||||||
@@ -282,7 +237,6 @@ std::expected<RuntimeConfig, std::string> parse_runtime_config(int argc,
|
|||||||
|
|
||||||
std::string codec_raw;
|
std::string codec_raw;
|
||||||
std::string run_mode_raw;
|
std::string run_mode_raw;
|
||||||
std::string input_mode_raw;
|
|
||||||
std::string shm_name_raw;
|
std::string shm_name_raw;
|
||||||
std::string zmq_endpoint_raw;
|
std::string zmq_endpoint_raw;
|
||||||
std::vector<std::string> rtmp_urls_raw;
|
std::vector<std::string> rtmp_urls_raw;
|
||||||
@@ -300,16 +254,6 @@ std::expected<RuntimeConfig, std::string> parse_runtime_config(int argc,
|
|||||||
std::string ingest_consumer_delay_raw;
|
std::string ingest_consumer_delay_raw;
|
||||||
std::string snapshot_copy_delay_raw;
|
std::string snapshot_copy_delay_raw;
|
||||||
std::string emit_stall_raw;
|
std::string emit_stall_raw;
|
||||||
std::string dummy_frames_raw;
|
|
||||||
std::string dummy_fps_raw;
|
|
||||||
std::string dummy_width_raw;
|
|
||||||
std::string dummy_height_raw;
|
|
||||||
std::string dummy_reset_at_raw;
|
|
||||||
std::string dummy_reset_every_raw;
|
|
||||||
std::string dummy_label_raw;
|
|
||||||
std::string dummy_start_timestamp_ns_raw;
|
|
||||||
std::string dummy_startup_delay_ms_raw;
|
|
||||||
|
|
||||||
bool rtmp_enabled{false};
|
bool rtmp_enabled{false};
|
||||||
bool rtp_enabled{false};
|
bool rtp_enabled{false};
|
||||||
bool version_requested{false};
|
bool version_requested{false};
|
||||||
@@ -320,18 +264,8 @@ std::expected<RuntimeConfig, std::string> parse_runtime_config(int argc,
|
|||||||
|
|
||||||
app.add_option("--codec", codec_raw);
|
app.add_option("--codec", codec_raw);
|
||||||
app.add_option("--run-mode", run_mode_raw);
|
app.add_option("--run-mode", run_mode_raw);
|
||||||
app.add_option("--input-mode", input_mode_raw);
|
|
||||||
app.add_option("--shm-name", shm_name_raw);
|
app.add_option("--shm-name", shm_name_raw);
|
||||||
app.add_option("--zmq-endpoint", zmq_endpoint_raw);
|
app.add_option("--zmq-endpoint", zmq_endpoint_raw);
|
||||||
app.add_option("--dummy-frames", dummy_frames_raw);
|
|
||||||
app.add_option("--dummy-fps", dummy_fps_raw);
|
|
||||||
app.add_option("--dummy-width", dummy_width_raw);
|
|
||||||
app.add_option("--dummy-height", dummy_height_raw);
|
|
||||||
app.add_option("--dummy-reset-at", dummy_reset_at_raw);
|
|
||||||
app.add_option("--dummy-reset-every", dummy_reset_every_raw);
|
|
||||||
app.add_option("--dummy-label", dummy_label_raw);
|
|
||||||
app.add_option("--dummy-start-timestamp-ns", dummy_start_timestamp_ns_raw);
|
|
||||||
app.add_option("--dummy-startup-delay-ms", dummy_startup_delay_ms_raw);
|
|
||||||
app.add_flag("--rtmp", rtmp_enabled);
|
app.add_flag("--rtmp", rtmp_enabled);
|
||||||
app.add_option("--rtmp-url", rtmp_urls_raw);
|
app.add_option("--rtmp-url", rtmp_urls_raw);
|
||||||
app.add_option("--rtmp-mode", rtmp_mode_raw);
|
app.add_option("--rtmp-mode", rtmp_mode_raw);
|
||||||
@@ -378,14 +312,6 @@ std::expected<RuntimeConfig, std::string> parse_runtime_config(int argc,
|
|||||||
config.run_mode = *run_mode;
|
config.run_mode = *run_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!input_mode_raw.empty()) {
|
|
||||||
auto input_mode = parse_input_mode(input_mode_raw);
|
|
||||||
if (!input_mode) {
|
|
||||||
return std::unexpected(input_mode.error());
|
|
||||||
}
|
|
||||||
config.input_mode = *input_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!shm_name_raw.empty()) {
|
if (!shm_name_raw.empty()) {
|
||||||
config.input.shm_name = shm_name_raw;
|
config.input.shm_name = shm_name_raw;
|
||||||
}
|
}
|
||||||
@@ -394,84 +320,6 @@ std::expected<RuntimeConfig, std::string> parse_runtime_config(int argc,
|
|||||||
config.input.zmq_endpoint = zmq_endpoint_raw;
|
config.input.zmq_endpoint = zmq_endpoint_raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dummy_frames_raw.empty()) {
|
|
||||||
auto parsed = parse_u32(dummy_frames_raw, "--dummy-frames");
|
|
||||||
if (!parsed) {
|
|
||||||
return std::unexpected(parsed.error());
|
|
||||||
}
|
|
||||||
config.dummy.frames = *parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dummy_fps_raw.empty()) {
|
|
||||||
auto parsed = parse_u32(dummy_fps_raw, "--dummy-fps");
|
|
||||||
if (!parsed) {
|
|
||||||
return std::unexpected(parsed.error());
|
|
||||||
}
|
|
||||||
config.dummy.fps = *parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dummy_width_raw.empty()) {
|
|
||||||
auto parsed = parse_u32(dummy_width_raw, "--dummy-width");
|
|
||||||
if (!parsed) {
|
|
||||||
return std::unexpected(parsed.error());
|
|
||||||
}
|
|
||||||
if (*parsed > std::numeric_limits<std::uint16_t>::max()) {
|
|
||||||
return std::unexpected("value out of range for --dummy-width: '" +
|
|
||||||
dummy_width_raw + "'");
|
|
||||||
}
|
|
||||||
config.dummy.width = static_cast<std::uint16_t>(*parsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dummy_height_raw.empty()) {
|
|
||||||
auto parsed = parse_u32(dummy_height_raw, "--dummy-height");
|
|
||||||
if (!parsed) {
|
|
||||||
return std::unexpected(parsed.error());
|
|
||||||
}
|
|
||||||
if (*parsed > std::numeric_limits<std::uint16_t>::max()) {
|
|
||||||
return std::unexpected("value out of range for --dummy-height: '" +
|
|
||||||
dummy_height_raw + "'");
|
|
||||||
}
|
|
||||||
config.dummy.height = static_cast<std::uint16_t>(*parsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dummy_reset_at_raw.empty()) {
|
|
||||||
auto parsed = parse_u32(dummy_reset_at_raw, "--dummy-reset-at");
|
|
||||||
if (!parsed) {
|
|
||||||
return std::unexpected(parsed.error());
|
|
||||||
}
|
|
||||||
config.dummy.emit_reset_at = *parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dummy_reset_every_raw.empty()) {
|
|
||||||
auto parsed = parse_u32(dummy_reset_every_raw, "--dummy-reset-every");
|
|
||||||
if (!parsed) {
|
|
||||||
return std::unexpected(parsed.error());
|
|
||||||
}
|
|
||||||
config.dummy.emit_reset_every = *parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dummy_label_raw.empty()) {
|
|
||||||
config.dummy.label = dummy_label_raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dummy_start_timestamp_ns_raw.empty()) {
|
|
||||||
auto parsed =
|
|
||||||
parse_u64(dummy_start_timestamp_ns_raw, "--dummy-start-timestamp-ns");
|
|
||||||
if (!parsed) {
|
|
||||||
return std::unexpected(parsed.error());
|
|
||||||
}
|
|
||||||
config.dummy.start_timestamp_ns = *parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dummy_startup_delay_ms_raw.empty()) {
|
|
||||||
auto parsed =
|
|
||||||
parse_u32(dummy_startup_delay_ms_raw, "--dummy-startup-delay-ms");
|
|
||||||
if (!parsed) {
|
|
||||||
return std::unexpected(parsed.error());
|
|
||||||
}
|
|
||||||
config.dummy.startup_delay_ms = *parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.outputs.rtmp.enabled = rtmp_enabled;
|
config.outputs.rtmp.enabled = rtmp_enabled;
|
||||||
if (!rtmp_urls_raw.empty()) {
|
if (!rtmp_urls_raw.empty()) {
|
||||||
config.outputs.rtmp.enabled = true;
|
config.outputs.rtmp.enabled = true;
|
||||||
@@ -609,13 +457,6 @@ validate_runtime_config(const RuntimeConfig &config) {
|
|||||||
"invalid input config: --shm-name must not be empty");
|
"invalid input config: --shm-name must not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.input_mode == InputMode::Dummy &&
|
|
||||||
config.input.shm_name.starts_with("cvmmap://")) {
|
|
||||||
return std::unexpected(
|
|
||||||
"invalid input config: --input-mode dummy requires POSIX --shm-name, "
|
|
||||||
"not cvmmap:// URI");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.input.zmq_endpoint.empty()) {
|
if (config.input.zmq_endpoint.empty()) {
|
||||||
return std::unexpected(
|
return std::unexpected(
|
||||||
"invalid input config: --zmq-endpoint must not be empty");
|
"invalid input config: --zmq-endpoint must not be empty");
|
||||||
@@ -680,39 +521,6 @@ validate_runtime_config(const RuntimeConfig &config) {
|
|||||||
"invalid ingest config: --ingest-idle-timeout-ms must be >= 1");
|
"invalid ingest config: --ingest-idle-timeout-ms must be >= 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.dummy.width == 0 || config.dummy.height == 0) {
|
|
||||||
return std::unexpected(
|
|
||||||
"invalid dummy config: --dummy-width and --dummy-height must be >= 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.dummy.channels == 0) {
|
|
||||||
return std::unexpected("invalid dummy config: channels must be >= 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.dummy.label.empty()) {
|
|
||||||
return std::unexpected("invalid dummy config: --dummy-label must not be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.dummy.label.size() > ipc::kLabelLenMax) {
|
|
||||||
return std::unexpected("invalid dummy config: --dummy-label exceeds 24 bytes");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.dummy.emit_reset_at && *config.dummy.emit_reset_at == 0) {
|
|
||||||
return std::unexpected(
|
|
||||||
"invalid dummy config: --dummy-reset-at must be >= 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.dummy.frames > 0 && config.dummy.emit_reset_at &&
|
|
||||||
*config.dummy.emit_reset_at > config.dummy.frames) {
|
|
||||||
return std::unexpected("invalid dummy config: --dummy-reset-at must be <= "
|
|
||||||
"--dummy-frames when --dummy-frames > 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.dummy.emit_reset_every && *config.dummy.emit_reset_every == 0) {
|
|
||||||
return std::unexpected(
|
|
||||||
"invalid dummy config: --dummy-reset-every must be >= 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -721,12 +529,6 @@ std::string summarize_runtime_config(const RuntimeConfig &config) {
|
|||||||
ss << "input.shm=" << config.input.shm_name;
|
ss << "input.shm=" << config.input.shm_name;
|
||||||
ss << ", input.zmq=" << config.input.zmq_endpoint;
|
ss << ", input.zmq=" << config.input.zmq_endpoint;
|
||||||
ss << ", run_mode=" << to_string(config.run_mode);
|
ss << ", run_mode=" << to_string(config.run_mode);
|
||||||
ss << ", input_mode=" << to_string(config.input_mode);
|
|
||||||
ss << ", dummy.frames=" << config.dummy.frames;
|
|
||||||
ss << ", dummy.fps=" << config.dummy.fps;
|
|
||||||
ss << ", dummy.size=" << config.dummy.width << "x" << config.dummy.height;
|
|
||||||
ss << ", dummy.label=" << config.dummy.label;
|
|
||||||
ss << ", dummy.start_timestamp_ns=" << config.dummy.start_timestamp_ns;
|
|
||||||
ss << ", codec=" << to_string(config.codec);
|
ss << ", codec=" << to_string(config.codec);
|
||||||
ss << ", rtmp.enabled=" << (config.outputs.rtmp.enabled ? "true" : "false");
|
ss << ", rtmp.enabled=" << (config.outputs.rtmp.enabled ? "true" : "false");
|
||||||
ss << ", rtmp.mode=" << to_string(config.outputs.rtmp.mode);
|
ss << ", rtmp.mode=" << to_string(config.outputs.rtmp.mode);
|
||||||
|
|||||||
+9
-321
@@ -1,39 +1,21 @@
|
|||||||
#include "cvmmap_streamer/core/frame_source.hpp"
|
#include "cvmmap_streamer/core/frame_source.hpp"
|
||||||
|
|
||||||
#include "cvmmap_streamer/ipc/contracts.hpp"
|
#include <cvmmap/client.hpp>
|
||||||
#include "cvmmap_streamer/protocol/wire_codec.hpp"
|
|
||||||
|
|
||||||
#include <app/cvmmap/cvmmap_client.hpp>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <thread>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <zmq.hpp>
|
|
||||||
|
|
||||||
namespace cvmmap_streamer::core {
|
namespace cvmmap_streamer::core {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
namespace ipc = cvmmap_streamer::ipc;
|
|
||||||
namespace wire_codec = cvmmap_streamer::protocol::wire_codec;
|
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
std::string resolve_client_target(const RuntimeConfig &config) {
|
std::string resolve_client_target(const RuntimeConfig &config) {
|
||||||
if (config.input.shm_name.starts_with("cvmmap://")) {
|
if (config.input.shm_name.starts_with("cvmmap://")) {
|
||||||
@@ -48,123 +30,18 @@ std::string resolve_client_target(const RuntimeConfig &config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
std::string_view to_string(app::cvmmap::ModuleStatus status) {
|
std::string_view to_string(cvmmap::ModuleStatus status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case app::cvmmap::ModuleStatus::Online:
|
case cvmmap::ModuleStatus::Online:
|
||||||
return "online";
|
return "online";
|
||||||
case app::cvmmap::ModuleStatus::Offline:
|
case cvmmap::ModuleStatus::Offline:
|
||||||
return "offline";
|
return "offline";
|
||||||
case app::cvmmap::ModuleStatus::StreamReset:
|
case cvmmap::ModuleStatus::StreamReset:
|
||||||
return "stream_reset";
|
return "stream_reset";
|
||||||
}
|
}
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
std::string normalized_shm_name(std::string_view raw_name) {
|
|
||||||
if (!raw_name.empty() && raw_name.front() == '/') {
|
|
||||||
return std::string(raw_name);
|
|
||||||
}
|
|
||||||
return "/" + std::string(raw_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanup_zmq_ipc_endpoint_path(std::string_view endpoint) {
|
|
||||||
constexpr std::string_view kPrefix{"ipc://"};
|
|
||||||
if (!endpoint.starts_with(kPrefix)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto path = endpoint.substr(kPrefix.size());
|
|
||||||
if (!path.empty()) {
|
|
||||||
unlink(std::string(path).c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SharedMemoryRegion {
|
|
||||||
public:
|
|
||||||
static std::expected<SharedMemoryRegion, std::string> create(const std::string &name, std::size_t bytes) {
|
|
||||||
const int fd = shm_open(name.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
|
||||||
if (fd < 0) {
|
|
||||||
return std::unexpected("shm_open failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ftruncate(fd, static_cast<off_t>(bytes)) != 0) {
|
|
||||||
close(fd);
|
|
||||||
shm_unlink(name.c_str());
|
|
||||||
return std::unexpected("ftruncate failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto *mapped = static_cast<std::uint8_t *>(mmap(nullptr, bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
|
||||||
if (mapped == MAP_FAILED) {
|
|
||||||
close(fd);
|
|
||||||
shm_unlink(name.c_str());
|
|
||||||
return std::unexpected("mmap failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::memset(mapped, 0, bytes);
|
|
||||||
return SharedMemoryRegion(name, fd, mapped, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedMemoryRegion() = default;
|
|
||||||
|
|
||||||
SharedMemoryRegion(const SharedMemoryRegion &) = delete;
|
|
||||||
SharedMemoryRegion &operator=(const SharedMemoryRegion &) = delete;
|
|
||||||
|
|
||||||
SharedMemoryRegion(SharedMemoryRegion &&other) noexcept
|
|
||||||
: name_(std::move(other.name_)),
|
|
||||||
fd_(std::exchange(other.fd_, -1)),
|
|
||||||
ptr_(std::exchange(other.ptr_, nullptr)),
|
|
||||||
bytes_(std::exchange(other.bytes_, 0)) {}
|
|
||||||
|
|
||||||
SharedMemoryRegion &operator=(SharedMemoryRegion &&other) noexcept {
|
|
||||||
if (this == &other) {
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
cleanup();
|
|
||||||
name_ = std::move(other.name_);
|
|
||||||
fd_ = std::exchange(other.fd_, -1);
|
|
||||||
ptr_ = std::exchange(other.ptr_, nullptr);
|
|
||||||
bytes_ = std::exchange(other.bytes_, 0);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~SharedMemoryRegion() {
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
std::span<std::uint8_t> metadata() {
|
|
||||||
return std::span<std::uint8_t>(ptr_, ipc::kShmPayloadOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
std::span<std::uint8_t> payload(std::size_t bytes) {
|
|
||||||
return std::span<std::uint8_t>(ptr_ + ipc::kShmPayloadOffset, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
SharedMemoryRegion(std::string name, int fd, std::uint8_t *ptr, std::size_t bytes)
|
|
||||||
: name_(std::move(name)), fd_(fd), ptr_(ptr), bytes_(bytes) {}
|
|
||||||
|
|
||||||
void cleanup() {
|
|
||||||
if (ptr_ != nullptr && bytes_ > 0) {
|
|
||||||
munmap(ptr_, bytes_);
|
|
||||||
ptr_ = nullptr;
|
|
||||||
}
|
|
||||||
if (fd_ >= 0) {
|
|
||||||
close(fd_);
|
|
||||||
fd_ = -1;
|
|
||||||
}
|
|
||||||
if (!name_.empty()) {
|
|
||||||
shm_unlink(name_.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name_{};
|
|
||||||
int fd_{-1};
|
|
||||||
std::uint8_t *ptr_{nullptr};
|
|
||||||
std::size_t bytes_{0};
|
|
||||||
};
|
|
||||||
|
|
||||||
class RealFrameSource final : public FrameSource {
|
class RealFrameSource final : public FrameSource {
|
||||||
public:
|
public:
|
||||||
explicit RealFrameSource(const RuntimeConfig &config)
|
explicit RealFrameSource(const RuntimeConfig &config)
|
||||||
@@ -193,15 +70,15 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto client = std::make_unique<app::cvmmap::CvMmapClient>(client_target_);
|
auto client = std::make_unique<cvmmap::CvMmapClient>(client_target_);
|
||||||
client->SetEventCallback([this](app::cvmmap::ModuleStatus status) {
|
client->SetEventCallback([this](cvmmap::ModuleStatus status) {
|
||||||
const auto events = observed_events_.fetch_add(1, std::memory_order_relaxed) + 1;
|
const auto events = observed_events_.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||||
spdlog::info(
|
spdlog::info(
|
||||||
"real source event status={} total_events={}",
|
"real source event status={} total_events={}",
|
||||||
to_string(status),
|
to_string(status),
|
||||||
events);
|
events);
|
||||||
});
|
});
|
||||||
client->SetFrameCallback([this](const app::cvmmap::frame_metadata_t &metadata, std::span<const std::uint8_t> payload) {
|
client->SetFrameCallback([this](const cvmmap::frame_metadata_t &metadata, std::span<const std::uint8_t> payload) {
|
||||||
const auto frames = observed_frames_.fetch_add(1, std::memory_order_relaxed) + 1;
|
const auto frames = observed_frames_.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||||
if (frames <= 1 || (frames % 60) == 0) {
|
if (frames <= 1 || (frames % 60) == 0) {
|
||||||
spdlog::debug(
|
spdlog::debug(
|
||||||
@@ -227,205 +104,16 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::string client_target_;
|
std::string client_target_;
|
||||||
mutable std::unique_ptr<app::cvmmap::CvMmapClient> client_{nullptr};
|
mutable std::unique_ptr<cvmmap::CvMmapClient> client_{nullptr};
|
||||||
mutable std::atomic_bool prepared_{false};
|
mutable std::atomic_bool prepared_{false};
|
||||||
mutable std::atomic<std::uint64_t> observed_frames_{0};
|
mutable std::atomic<std::uint64_t> observed_frames_{0};
|
||||||
mutable std::atomic<std::uint64_t> observed_events_{0};
|
mutable std::atomic<std::uint64_t> observed_events_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
class DummyFrameSource final : public FrameSource {
|
|
||||||
public:
|
|
||||||
explicit DummyFrameSource(const RuntimeConfig &config)
|
|
||||||
: config_(config),
|
|
||||||
shm_name_(normalized_shm_name(config.input.shm_name)),
|
|
||||||
zmq_endpoint_(config.input.zmq_endpoint) {}
|
|
||||||
|
|
||||||
~DummyFrameSource() override {
|
|
||||||
shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
std::string_view backend_name() const override {
|
|
||||||
return "dummy";
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
std::expected<void, std::string> prepare_runtime() const override {
|
|
||||||
if (prepared_.exchange(true, std::memory_order_acq_rel)) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto payload_bytes = static_cast<std::size_t>(config_.dummy.width) *
|
|
||||||
static_cast<std::size_t>(config_.dummy.height) *
|
|
||||||
static_cast<std::size_t>(config_.dummy.channels);
|
|
||||||
auto shm_region = SharedMemoryRegion::create(shm_name_, ipc::kShmPayloadOffset + payload_bytes);
|
|
||||||
if (!shm_region) {
|
|
||||||
prepared_.store(false, std::memory_order_release);
|
|
||||||
return std::unexpected("dummy shm setup failed: " + shm_region.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup_zmq_ipc_endpoint_path(zmq_endpoint_);
|
|
||||||
try {
|
|
||||||
zmq_context_.emplace(1);
|
|
||||||
publisher_.emplace(*zmq_context_, zmq::socket_type::pub);
|
|
||||||
publisher_->bind(zmq_endpoint_);
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
publisher_.reset();
|
|
||||||
zmq_context_.reset();
|
|
||||||
cleanup_zmq_ipc_endpoint_path(zmq_endpoint_);
|
|
||||||
prepared_.store(false, std::memory_order_release);
|
|
||||||
return std::unexpected(std::string("dummy zmq bind failed: ") + e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
shm_.emplace(std::move(*shm_region));
|
|
||||||
stop_requested_.store(false, std::memory_order_release);
|
|
||||||
|
|
||||||
try {
|
|
||||||
producer_thread_ = std::thread([this]() {
|
|
||||||
run_producer_loop();
|
|
||||||
});
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
publisher_.reset();
|
|
||||||
zmq_context_.reset();
|
|
||||||
shm_.reset();
|
|
||||||
cleanup_zmq_ipc_endpoint_path(zmq_endpoint_);
|
|
||||||
prepared_.store(false, std::memory_order_release);
|
|
||||||
return std::unexpected(std::string("dummy producer thread start failed: ") + e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
spdlog::info(
|
|
||||||
"dummy source backend initialized in-process: shm='{}' zmq='{}' label='{}'",
|
|
||||||
shm_name_,
|
|
||||||
zmq_endpoint_,
|
|
||||||
config_.dummy.label);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void shutdown() const {
|
|
||||||
if (!prepared_.exchange(false, std::memory_order_acq_rel)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stop_requested_.store(true, std::memory_order_release);
|
|
||||||
if (producer_thread_.joinable()) {
|
|
||||||
producer_thread_.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
publisher_.reset();
|
|
||||||
zmq_context_.reset();
|
|
||||||
shm_.reset();
|
|
||||||
cleanup_zmq_ipc_endpoint_path(zmq_endpoint_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sleep_with_stop(std::chrono::nanoseconds duration) const {
|
|
||||||
constexpr auto kSlice = std::chrono::milliseconds(10);
|
|
||||||
auto remaining = duration;
|
|
||||||
while (remaining.count() > 0 && !stop_requested_.load(std::memory_order_relaxed)) {
|
|
||||||
auto step = std::min(remaining, std::chrono::duration_cast<std::chrono::nanoseconds>(kSlice));
|
|
||||||
std::this_thread::sleep_for(step);
|
|
||||||
remaining -= step;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void send_status(
|
|
||||||
std::array<std::uint8_t, wire_codec::kModuleStatusMessageBytes> &buffer,
|
|
||||||
ipc::ModuleStatus status,
|
|
||||||
std::uint32_t frame_count) const {
|
|
||||||
wire_codec::write_module_status_message(buffer, config_.dummy.label, status);
|
|
||||||
publisher_->send(zmq::buffer(buffer), zmq::send_flags::none);
|
|
||||||
if (status == ipc::ModuleStatus::Offline || status == ipc::ModuleStatus::Online) {
|
|
||||||
spdlog::info("dummy source status={} frame_count={}", static_cast<std::int32_t>(status), frame_count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_producer_loop() const {
|
|
||||||
std::array<std::uint8_t, wire_codec::kSyncMessageBytes> sync_buffer{};
|
|
||||||
std::array<std::uint8_t, wire_codec::kModuleStatusMessageBytes> status_buffer{};
|
|
||||||
|
|
||||||
const auto payload_bytes = static_cast<std::size_t>(config_.dummy.width) *
|
|
||||||
static_cast<std::size_t>(config_.dummy.height) *
|
|
||||||
static_cast<std::size_t>(config_.dummy.channels);
|
|
||||||
const auto tick_ns =
|
|
||||||
(config_.dummy.fps == 0)
|
|
||||||
? 0ull
|
|
||||||
: std::max<std::uint64_t>(1ull, 1'000'000'000ull / static_cast<std::uint64_t>(config_.dummy.fps));
|
|
||||||
std::uint64_t timestamp_ns = config_.dummy.start_timestamp_ns;
|
|
||||||
std::uint32_t frame_count = 0;
|
|
||||||
|
|
||||||
if (config_.dummy.startup_delay_ms > 0) {
|
|
||||||
sleep_with_stop(std::chrono::milliseconds(config_.dummy.startup_delay_ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
send_status(status_buffer, ipc::ModuleStatus::Online, 0);
|
|
||||||
|
|
||||||
while (!stop_requested_.load(std::memory_order_relaxed)) {
|
|
||||||
frame_count += 1;
|
|
||||||
auto payload = shm_->payload(payload_bytes);
|
|
||||||
wire_codec::write_deterministic_payload(payload, frame_count, config_.dummy.width, config_.dummy.height, config_.dummy.channels);
|
|
||||||
|
|
||||||
wire_codec::write_frame_metadata(
|
|
||||||
shm_->metadata(),
|
|
||||||
ipc::FrameInfo{
|
|
||||||
.width = config_.dummy.width,
|
|
||||||
.height = config_.dummy.height,
|
|
||||||
.channels = config_.dummy.channels,
|
|
||||||
.depth = config_.dummy.depth,
|
|
||||||
.pixel_format = config_.dummy.pixel_format,
|
|
||||||
.buffer_size = static_cast<std::uint32_t>(payload_bytes)},
|
|
||||||
frame_count,
|
|
||||||
timestamp_ns);
|
|
||||||
|
|
||||||
wire_codec::write_sync_message(sync_buffer, config_.dummy.label, frame_count, timestamp_ns);
|
|
||||||
publisher_->send(zmq::buffer(sync_buffer), zmq::send_flags::none);
|
|
||||||
|
|
||||||
if (config_.dummy.emit_reset_at && *config_.dummy.emit_reset_at == frame_count) {
|
|
||||||
send_status(status_buffer, ipc::ModuleStatus::StreamReset, frame_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config_.dummy.emit_reset_every && *config_.dummy.emit_reset_every > 0 &&
|
|
||||||
(frame_count % *config_.dummy.emit_reset_every) == 0) {
|
|
||||||
send_status(status_buffer, ipc::ModuleStatus::StreamReset, frame_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
timestamp_ns += tick_ns;
|
|
||||||
if (config_.dummy.fps > 0) {
|
|
||||||
sleep_with_stop(std::chrono::nanoseconds(tick_ns));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
spdlog::error("dummy source producer failed: {}", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (publisher_) {
|
|
||||||
send_status(status_buffer, ipc::ModuleStatus::Offline, frame_count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RuntimeConfig config_;
|
|
||||||
std::string shm_name_;
|
|
||||||
std::string zmq_endpoint_;
|
|
||||||
|
|
||||||
mutable std::optional<SharedMemoryRegion> shm_{};
|
|
||||||
mutable std::optional<zmq::context_t> zmq_context_{};
|
|
||||||
mutable std::optional<zmq::socket_t> publisher_{};
|
|
||||||
mutable std::thread producer_thread_{};
|
|
||||||
mutable std::atomic_bool prepared_{false};
|
|
||||||
mutable std::atomic_bool stop_requested_{false};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<std::unique_ptr<FrameSource>, std::string> make_frame_source(const RuntimeConfig &config) {
|
std::expected<std::unique_ptr<FrameSource>, std::string> make_frame_source(const RuntimeConfig &config) {
|
||||||
switch (config.input_mode) {
|
|
||||||
case InputMode::Real:
|
|
||||||
return std::unique_ptr<FrameSource>(new RealFrameSource(config));
|
return std::unique_ptr<FrameSource>(new RealFrameSource(config));
|
||||||
case InputMode::Dummy:
|
|
||||||
return std::unique_ptr<FrameSource>(new DummyFrameSource(config));
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::unexpected("unsupported input mode");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#include "cvmmap_streamer/core/frame_source.hpp"
|
#include "cvmmap_streamer/core/frame_source.hpp"
|
||||||
#include "cvmmap_streamer/ipc/contracts.hpp"
|
#include "cvmmap_streamer/ipc/contracts.hpp"
|
||||||
|
|
||||||
#include "include/app/cvmmap/cvmmap_client.hpp"
|
#include <cvmmap/client.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@@ -47,12 +47,12 @@ namespace {
|
|||||||
.zmq_endpoint = config.input.zmq_endpoint,
|
.zmq_endpoint = config.input.zmq_endpoint,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (config.input_mode != InputMode::Real || !config.input.shm_name.starts_with("cvmmap://")) {
|
if (!config.input.shm_name.starts_with("cvmmap://")) {
|
||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto target = app::cvmmap::resolve_cvmmap_target_or_throw(config.input.shm_name);
|
auto target = cvmmap::resolve_cvmmap_target_or_throw(config.input.shm_name);
|
||||||
resolved.shm_name = target.shm_name;
|
resolved.shm_name = target.shm_name;
|
||||||
resolved.zmq_endpoint = target.zmq_addr;
|
resolved.zmq_endpoint = target.zmq_addr;
|
||||||
spdlog::info(
|
spdlog::info(
|
||||||
|
|||||||
+42
-12
@@ -1,5 +1,7 @@
|
|||||||
#include "cvmmap_streamer/ipc/contracts.hpp"
|
#include "cvmmap_streamer/ipc/contracts.hpp"
|
||||||
|
|
||||||
|
#include <cvmmap/parser.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
@@ -109,6 +111,40 @@ namespace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
ParseError map_core_parser_error(const std::string_view error) {
|
||||||
|
if (error.contains("magic")) {
|
||||||
|
return ParseError::InvalidMagic;
|
||||||
|
}
|
||||||
|
if (error.contains("version")) {
|
||||||
|
return ParseError::UnsupportedVersion;
|
||||||
|
}
|
||||||
|
if (error.contains("depth")) {
|
||||||
|
return ParseError::InvalidDepth;
|
||||||
|
}
|
||||||
|
if (error.contains("pixel_format")) {
|
||||||
|
return ParseError::InvalidPixelFormat;
|
||||||
|
}
|
||||||
|
return ParseError::InvalidSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
FrameMetadata from_core_metadata(const cvmmap::frame_metadata_t &metadata) {
|
||||||
|
FrameMetadata converted{};
|
||||||
|
std::copy_n(metadata.magic, converted.magic.size(), converted.magic.begin());
|
||||||
|
converted.versions_major = metadata.versions_major;
|
||||||
|
converted.versions_minor = metadata.versions_minor;
|
||||||
|
converted.frame_count = metadata.frame_count;
|
||||||
|
converted.timestamp_ns = metadata.timestamp_ns;
|
||||||
|
converted.info.width = metadata.info.width;
|
||||||
|
converted.info.height = metadata.info.height;
|
||||||
|
converted.info.channels = metadata.info.channels;
|
||||||
|
converted.info.depth = static_cast<Depth>(metadata.info.depth);
|
||||||
|
converted.info.pixel_format = static_cast<PixelFormat>(metadata.info.pixel_format);
|
||||||
|
converted.info.buffer_size = metadata.info.buffer_size;
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view to_string(ParseError error) {
|
std::string_view to_string(ParseError error) {
|
||||||
@@ -301,22 +337,16 @@ std::expected<ValidatedShmView, ParseError> validate_shm_region(std::span<const
|
|||||||
return std::unexpected(ParseError::BufferTooSmall);
|
return std::unexpected(ParseError::BufferTooSmall);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto metadata_result = parse_frame_metadata(shm_region);
|
auto metadata_result = cvmmap::parse_frame_metadata_regions(
|
||||||
|
shm_region.first(kShmPayloadOffset),
|
||||||
|
shm_region.subspan(kShmPayloadOffset));
|
||||||
if (!metadata_result) {
|
if (!metadata_result) {
|
||||||
return std::unexpected(metadata_result.error());
|
return std::unexpected(map_core_parser_error(metadata_result.error()));
|
||||||
}
|
|
||||||
|
|
||||||
const auto payload_size = static_cast<std::size_t>(metadata_result->info.buffer_size);
|
|
||||||
if (payload_size > std::numeric_limits<std::size_t>::max() - kShmPayloadOffset) {
|
|
||||||
return std::unexpected(ParseError::InvalidSize);
|
|
||||||
}
|
|
||||||
if (payload_size > shm_region.size() - kShmPayloadOffset) {
|
|
||||||
return std::unexpected(ParseError::InvalidSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ValidatedShmView{
|
return ValidatedShmView{
|
||||||
.metadata = *metadata_result,
|
.metadata = from_core_metadata(metadata_result->normalized_metadata),
|
||||||
.payload = shm_region.subspan(kShmPayloadOffset, payload_size)};
|
.payload = metadata_result->left_plane};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<CoherentSnapshot, SnapshotError> read_coherent_snapshot(
|
std::expected<CoherentSnapshot, SnapshotError> read_coherent_snapshot(
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ namespace {
|
|||||||
"",
|
"",
|
||||||
"Examples:",
|
"Examples:",
|
||||||
" cvmmap_streamer --help",
|
" cvmmap_streamer --help",
|
||||||
" cvmmap_streamer --run-mode pipeline --input-mode dummy --help",
|
" cvmmap_streamer --run-mode pipeline --shm-name cvmmap://default --help",
|
||||||
" rtp_receiver_tester --help"};
|
" rtp_receiver_tester --help"};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "cvmmap_streamer/config/runtime_config.hpp"
|
#include "cvmmap_streamer/config/runtime_config.hpp"
|
||||||
#include "cvmmap_streamer/ipc/contracts.hpp"
|
#include "cvmmap_streamer/ipc/contracts.hpp"
|
||||||
|
|
||||||
#include "include/app/cvmmap/cvmmap_client.hpp"
|
#include <cvmmap/client.hpp>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -63,12 +63,12 @@ namespace {
|
|||||||
.zmq_endpoint = config.input.zmq_endpoint,
|
.zmq_endpoint = config.input.zmq_endpoint,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (config.input_mode != InputMode::Real || !config.input.shm_name.starts_with("cvmmap://")) {
|
if (!config.input.shm_name.starts_with("cvmmap://")) {
|
||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto target = app::cvmmap::resolve_cvmmap_target_or_throw(config.input.shm_name);
|
auto target = cvmmap::resolve_cvmmap_target_or_throw(config.input.shm_name);
|
||||||
resolved.shm_name = target.shm_name;
|
resolved.shm_name = target.shm_name;
|
||||||
resolved.zmq_endpoint = target.zmq_addr;
|
resolved.zmq_endpoint = target.zmq_addr;
|
||||||
spdlog::info(
|
spdlog::info(
|
||||||
|
|||||||
Reference in New Issue
Block a user