feat(streamer): add ffmpeg encoder and mcap recording
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
#define MCAP_IMPLEMENTATION
|
||||
#include <mcap/reader.hpp>
|
||||
|
||||
#include <foxglove/CompressedVideo.pb.h>
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace {
|
||||
|
||||
struct Config {
|
||||
std::string input_path{};
|
||||
std::optional<std::string> expected_topic{};
|
||||
std::optional<std::string> expected_format{};
|
||||
std::uint32_t min_messages{1};
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
std::expected<Config, int> parse_args(int argc, char **argv) {
|
||||
Config config{};
|
||||
CLI::App app{"mcap_reader_tester - validate foxglove.CompressedVideo MCAP output"};
|
||||
app.add_option("input", config.input_path, "Input MCAP path")->required();
|
||||
app.add_option("--expect-topic", config.expected_topic, "Expected MCAP topic");
|
||||
app.add_option("--expect-format", config.expected_format, "Expected CompressedVideo format");
|
||||
app.add_option("--min-messages", config.min_messages, "Minimum expected message count")->check(CLI::PositiveNumber);
|
||||
|
||||
try {
|
||||
app.parse(argc, argv);
|
||||
} catch (const CLI::ParseError &e) {
|
||||
return std::unexpected(app.exit(e));
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
auto config = parse_args(argc, argv);
|
||||
if (!config) {
|
||||
return config.error();
|
||||
}
|
||||
|
||||
mcap::McapReader reader{};
|
||||
const auto open_status = reader.open(config->input_path);
|
||||
if (!open_status.ok()) {
|
||||
spdlog::error("failed to open MCAP file '{}': {}", config->input_path, open_status.message);
|
||||
return 2;
|
||||
}
|
||||
|
||||
std::uint64_t message_count{0};
|
||||
std::uint64_t previous_log_time{0};
|
||||
bool saw_log_time{false};
|
||||
|
||||
auto message_view = reader.readMessages();
|
||||
for (auto it = message_view.begin(); it != message_view.end(); ++it) {
|
||||
if (it->schema == nullptr || it->channel == nullptr) {
|
||||
spdlog::error("MCAP message missing schema or channel metadata");
|
||||
reader.close();
|
||||
return 3;
|
||||
}
|
||||
if (it->schema->encoding != "protobuf" || it->schema->name != "foxglove.CompressedVideo") {
|
||||
continue;
|
||||
}
|
||||
if (it->channel->messageEncoding != "protobuf") {
|
||||
spdlog::error("unexpected MCAP message encoding: {}", it->channel->messageEncoding);
|
||||
reader.close();
|
||||
return 3;
|
||||
}
|
||||
if (config->expected_topic && it->channel->topic != *config->expected_topic) {
|
||||
spdlog::error("unexpected topic: expected '{}' got '{}'", *config->expected_topic, it->channel->topic);
|
||||
reader.close();
|
||||
return 4;
|
||||
}
|
||||
if (saw_log_time && it->message.logTime < previous_log_time) {
|
||||
spdlog::error("non-monotonic logTime detected: {} < {}", it->message.logTime, previous_log_time);
|
||||
reader.close();
|
||||
return 5;
|
||||
}
|
||||
|
||||
foxglove::CompressedVideo message{};
|
||||
if (!message.ParseFromArray(it->message.data, static_cast<int>(it->message.dataSize))) {
|
||||
spdlog::error("failed to parse foxglove.CompressedVideo payload");
|
||||
reader.close();
|
||||
return 6;
|
||||
}
|
||||
if (config->expected_format && message.format() != *config->expected_format) {
|
||||
spdlog::error("unexpected format: expected '{}' got '{}'", *config->expected_format, message.format());
|
||||
reader.close();
|
||||
return 7;
|
||||
}
|
||||
if (message.data().empty()) {
|
||||
spdlog::error("compressed video payload is empty");
|
||||
reader.close();
|
||||
return 8;
|
||||
}
|
||||
|
||||
previous_log_time = it->message.logTime;
|
||||
saw_log_time = true;
|
||||
message_count += 1;
|
||||
}
|
||||
|
||||
reader.close();
|
||||
|
||||
if (message_count < config->min_messages) {
|
||||
spdlog::error("message threshold not met: {} < {}", message_count, config->min_messages);
|
||||
return 9;
|
||||
}
|
||||
|
||||
spdlog::info("validated {} foxglove.CompressedVideo MCAP messages", message_count);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user