feat: add shared TTY progress bars for ZED offline tools
Extract the tqdm-like stderr progress bar into a shared helper and reuse it across zed_svo_to_mp4, zed_svo_grid_to_mp4, and zed_svo_to_mcap. For zed_svo_to_mcap, single-source exports now report exact frame totals and bundled multi-camera exports report exact synced-group totals on TTY. When bundled mode runs without --end-frame, the tool performs a counting pass first so the progress total remains exact instead of estimated. Also document the bundled MCAP progress behavior in the README and record the current third_party dependency state in third_party/README. That note now makes it explicit that CLI11 and proxy are the active submodules, while tomlplusplus and mcap are vendored source drops, and adds a TODO to revisit converting mcap into a submodule later. Verified with the Debug build during implementation, including single-camera and bundled zed_svo_to_mcap runs that rendered clean progress output to a TTY.
This commit is contained in:
@@ -356,6 +356,14 @@ set_target_properties(mcap_replay_tester PROPERTIES
|
||||
set(CVMMAP_STREAMER_INSTALL_TARGETS cvmmap_streamer)
|
||||
|
||||
if (CVMMAP_HAS_ZED_SDK)
|
||||
add_library(
|
||||
cvmmap_streamer_zed_progress_support
|
||||
STATIC
|
||||
src/tools/zed_progress_bar.cpp)
|
||||
target_include_directories(cvmmap_streamer_zed_progress_support
|
||||
PUBLIC
|
||||
"${CMAKE_CURRENT_LIST_DIR}/include"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}")
|
||||
add_executable(
|
||||
zed_svo_to_mcap
|
||||
src/tools/zed_svo_to_mcap.cpp
|
||||
@@ -372,6 +380,7 @@ if (CVMMAP_HAS_ZED_SDK)
|
||||
${CUDA_LIBRARY_DIRS})
|
||||
target_link_libraries(zed_svo_to_mcap
|
||||
PRIVATE
|
||||
cvmmap_streamer_zed_progress_support
|
||||
cvmmap_streamer_record_support
|
||||
CLI11::CLI11
|
||||
tomlplusplus::tomlplusplus
|
||||
@@ -421,6 +430,7 @@ if (CVMMAP_HAS_ZED_SDK)
|
||||
target_link_libraries(zed_svo_to_mp4
|
||||
PRIVATE
|
||||
CLI11::CLI11
|
||||
cvmmap_streamer_zed_progress_support
|
||||
cvmmap_streamer_zed_svo_mp4_support
|
||||
${ZED_LIBRARIES}
|
||||
${CUDA_CUDA_LIBRARY}
|
||||
@@ -453,6 +463,7 @@ if (CVMMAP_HAS_ZED_SDK)
|
||||
target_link_libraries(zed_svo_grid_to_mp4
|
||||
PRIVATE
|
||||
CLI11::CLI11
|
||||
cvmmap_streamer_zed_progress_support
|
||||
cvmmap_streamer_zed_svo_mp4_support
|
||||
${ZED_LIBRARIES}
|
||||
${CUDA_CUDA_LIBRARY}
|
||||
|
||||
@@ -264,6 +264,7 @@ uv run python scripts/zed_batch_svo_to_mcap.py \
|
||||
The batch MCAP wrapper writes `<segment>/<segment>.mcap` by default, skips existing outputs unless told otherwise, and returns a nonzero exit code if any segment fails.
|
||||
The repo includes a minimal pose config at `config/zed_pose_config.toml` so MCAP conversion does not depend on a separate `cv-mmap` checkout.
|
||||
In bundled multi-camera mode, `--end-frame` means the last emitted synced frame-group index from the common start timestamp.
|
||||
When stderr is attached to a TTY, `zed_svo_to_mcap` shows a tqdm-like progress bar. In bundled mode without `--end-frame`, it first counts synced groups to make that progress total exact.
|
||||
|
||||
Use `--probe-existing` to validate existing MCAPs before skipping them. Invalid outputs are treated as missing and requeued:
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace cvmmap_streamer::zed_tools {
|
||||
|
||||
[[nodiscard]]
|
||||
bool stderr_supports_progress_bar();
|
||||
|
||||
class ProgressBar {
|
||||
public:
|
||||
explicit ProgressBar(std::uint64_t total_frames);
|
||||
~ProgressBar();
|
||||
|
||||
[[nodiscard]]
|
||||
bool enabled() const;
|
||||
|
||||
void update(std::uint64_t completed_frames);
|
||||
void finish(std::uint64_t completed_frames, bool success);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_{};
|
||||
};
|
||||
|
||||
} // namespace cvmmap_streamer::zed_tools
|
||||
@@ -65,19 +65,6 @@ std::uint64_t frame_period_ns(float fps);
|
||||
[[nodiscard]]
|
||||
std::filesystem::path derive_output_path(const std::filesystem::path &input_path);
|
||||
|
||||
class ProgressBar {
|
||||
public:
|
||||
explicit ProgressBar(std::uint64_t total_frames);
|
||||
~ProgressBar();
|
||||
|
||||
void update(std::uint64_t completed_frames);
|
||||
void finish(std::uint64_t completed_frames, bool success);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_{};
|
||||
};
|
||||
|
||||
class Mp4Writer {
|
||||
public:
|
||||
Mp4Writer();
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
#include "cvmmap_streamer/tools/zed_progress_bar.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
namespace cvmmap_streamer::zed_tools {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]]
|
||||
std::string format_duration(const double seconds_raw) {
|
||||
const auto seconds = seconds_raw > 0.0 ? static_cast<long long>(std::llround(seconds_raw)) : 0ll;
|
||||
const auto hours = seconds / 3600;
|
||||
const auto minutes = (seconds % 3600) / 60;
|
||||
const auto secs = seconds % 60;
|
||||
|
||||
char buffer[32]{};
|
||||
if (hours > 0) {
|
||||
std::snprintf(buffer, sizeof(buffer), "%02lld:%02lld:%02lld", hours, minutes, secs);
|
||||
} else {
|
||||
std::snprintf(buffer, sizeof(buffer), "%02lld:%02lld", minutes, secs);
|
||||
}
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool stderr_supports_progress_bar() {
|
||||
return ::isatty(STDERR_FILENO) == 1;
|
||||
}
|
||||
|
||||
struct ProgressBar::Impl {
|
||||
using Clock = std::chrono::steady_clock;
|
||||
|
||||
explicit Impl(const std::uint64_t total_frames_arg)
|
||||
: total_frames(total_frames_arg),
|
||||
enabled(stderr_supports_progress_bar()),
|
||||
started_at(Clock::now()),
|
||||
last_render_at(started_at) {}
|
||||
|
||||
void render(const std::uint64_t completed_frames, const bool force) {
|
||||
if (!enabled || total_frames == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto now = Clock::now();
|
||||
if (!force && rendered && now - last_render_at < std::chrono::milliseconds(125)) {
|
||||
return;
|
||||
}
|
||||
last_render_at = now;
|
||||
rendered = true;
|
||||
|
||||
const auto bounded_completed = completed_frames > total_frames ? total_frames : completed_frames;
|
||||
const double ratio = static_cast<double>(bounded_completed) / static_cast<double>(total_frames);
|
||||
const auto filled = static_cast<std::size_t>(std::llround(ratio * 24.0));
|
||||
std::string bar{};
|
||||
bar.reserve(24);
|
||||
for (std::size_t index = 0; index < 24; ++index) {
|
||||
bar.push_back(index < filled ? '#' : '-');
|
||||
}
|
||||
|
||||
const auto elapsed_seconds = std::chrono::duration<double>(now - started_at).count();
|
||||
const auto fps = elapsed_seconds > 0.0 ? static_cast<double>(bounded_completed) / elapsed_seconds : 0.0;
|
||||
const auto eta_seconds = fps > 0.0 ? static_cast<double>(total_frames - bounded_completed) / fps : 0.0;
|
||||
|
||||
char line[256]{};
|
||||
std::snprintf(
|
||||
line,
|
||||
sizeof(line),
|
||||
"\r[%s] %6.2f%% %llu/%llu | %5.1f fps | %s elapsed | %s ETA\x1b[K",
|
||||
bar.c_str(),
|
||||
ratio * 100.0,
|
||||
static_cast<unsigned long long>(bounded_completed),
|
||||
static_cast<unsigned long long>(total_frames),
|
||||
fps,
|
||||
format_duration(elapsed_seconds).c_str(),
|
||||
format_duration(eta_seconds).c_str());
|
||||
std::fprintf(stderr, "%s", line);
|
||||
std::fflush(stderr);
|
||||
}
|
||||
|
||||
std::uint64_t total_frames{0};
|
||||
bool enabled{false};
|
||||
bool rendered{false};
|
||||
Clock::time_point started_at{};
|
||||
Clock::time_point last_render_at{};
|
||||
};
|
||||
|
||||
ProgressBar::ProgressBar(const std::uint64_t total_frames)
|
||||
: impl_(std::make_unique<Impl>(total_frames)) {}
|
||||
|
||||
ProgressBar::~ProgressBar() = default;
|
||||
|
||||
bool ProgressBar::enabled() const {
|
||||
return impl_ != nullptr && impl_->enabled;
|
||||
}
|
||||
|
||||
void ProgressBar::update(const std::uint64_t completed_frames) {
|
||||
impl_->render(completed_frames, false);
|
||||
}
|
||||
|
||||
void ProgressBar::finish(const std::uint64_t completed_frames, const bool success) {
|
||||
if (impl_ == nullptr || !impl_->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(success && impl_->rendered && completed_frames >= impl_->total_frames)) {
|
||||
impl_->render(completed_frames, true);
|
||||
if (!impl_->rendered) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::fprintf(stderr, "%s", success ? "\n" : " [failed]\n");
|
||||
std::fflush(stderr);
|
||||
}
|
||||
|
||||
} // namespace cvmmap_streamer::zed_tools
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
#include "cvmmap_streamer/tools/zed_progress_bar.hpp"
|
||||
#include "cvmmap_streamer/tools/zed_svo_mp4_support.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -11,17 +11,13 @@ extern "C" {
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
namespace cvmmap_streamer::zed_tools {
|
||||
namespace {
|
||||
|
||||
@@ -68,22 +64,6 @@ AVRational frame_rate_rational(const float fps) {
|
||||
return AVRational{scaled, 1000};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::string format_duration(const double seconds_raw) {
|
||||
const auto seconds = seconds_raw > 0.0 ? static_cast<long long>(std::llround(seconds_raw)) : 0ll;
|
||||
const auto hours = seconds / 3600;
|
||||
const auto minutes = (seconds % 3600) / 60;
|
||||
const auto secs = seconds % 60;
|
||||
|
||||
char buffer[32]{};
|
||||
if (hours > 0) {
|
||||
std::snprintf(buffer, sizeof(buffer), "%02lld:%02lld:%02lld", hours, minutes, secs);
|
||||
} else {
|
||||
std::snprintf(buffer, sizeof(buffer), "%02lld:%02lld", minutes, secs);
|
||||
}
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::vector<EncoderCandidate> encoder_candidates(const CodecType codec, const EncoderDeviceType device) {
|
||||
const std::string hardware_name = codec == CodecType::H265 ? "hevc_nvenc" : "h264_nvenc";
|
||||
@@ -320,63 +300,6 @@ std::expected<OpenedEncoder, std::string> open_encoder(
|
||||
|
||||
} // namespace
|
||||
|
||||
struct ProgressBar::Impl {
|
||||
using Clock = std::chrono::steady_clock;
|
||||
|
||||
explicit Impl(const std::uint64_t total_frames_arg)
|
||||
: total_frames(total_frames_arg),
|
||||
enabled(::isatty(STDERR_FILENO) == 1),
|
||||
started_at(Clock::now()),
|
||||
last_render_at(started_at) {}
|
||||
|
||||
void render(const std::uint64_t completed_frames, const bool force) {
|
||||
if (!enabled || total_frames == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto now = Clock::now();
|
||||
if (!force && rendered && now - last_render_at < std::chrono::milliseconds(125)) {
|
||||
return;
|
||||
}
|
||||
last_render_at = now;
|
||||
rendered = true;
|
||||
|
||||
const auto bounded_completed = completed_frames > total_frames ? total_frames : completed_frames;
|
||||
const double ratio = static_cast<double>(bounded_completed) / static_cast<double>(total_frames);
|
||||
const auto filled = static_cast<std::size_t>(std::llround(ratio * 24.0));
|
||||
std::string bar{};
|
||||
bar.reserve(24);
|
||||
for (std::size_t i = 0; i < 24; ++i) {
|
||||
bar.push_back(i < filled ? '#' : '-');
|
||||
}
|
||||
|
||||
const auto elapsed_seconds = std::chrono::duration<double>(now - started_at).count();
|
||||
const auto fps = elapsed_seconds > 0.0 ? static_cast<double>(bounded_completed) / elapsed_seconds : 0.0;
|
||||
const auto eta_seconds = fps > 0.0 ? static_cast<double>(total_frames - bounded_completed) / fps : 0.0;
|
||||
|
||||
char line[256]{};
|
||||
std::snprintf(
|
||||
line,
|
||||
sizeof(line),
|
||||
"\r[%s] %6.2f%% %llu/%llu | %5.1f fps | %s elapsed | %s ETA\x1b[K",
|
||||
bar.c_str(),
|
||||
ratio * 100.0,
|
||||
static_cast<unsigned long long>(bounded_completed),
|
||||
static_cast<unsigned long long>(total_frames),
|
||||
fps,
|
||||
format_duration(elapsed_seconds).c_str(),
|
||||
format_duration(eta_seconds).c_str());
|
||||
std::fprintf(stderr, "%s", line);
|
||||
std::fflush(stderr);
|
||||
}
|
||||
|
||||
std::uint64_t total_frames{0};
|
||||
bool enabled{false};
|
||||
bool rendered{false};
|
||||
Clock::time_point started_at{};
|
||||
Clock::time_point last_render_at{};
|
||||
};
|
||||
|
||||
struct Mp4Writer::Impl {
|
||||
[[nodiscard]]
|
||||
std::expected<void, std::string> open(
|
||||
@@ -726,29 +649,6 @@ std::filesystem::path derive_output_path(const std::filesystem::path &input_path
|
||||
return output_path;
|
||||
}
|
||||
|
||||
ProgressBar::ProgressBar(const std::uint64_t total_frames)
|
||||
: impl_(std::make_unique<Impl>(total_frames)) {}
|
||||
|
||||
ProgressBar::~ProgressBar() = default;
|
||||
|
||||
void ProgressBar::update(const std::uint64_t completed_frames) {
|
||||
impl_->render(completed_frames, false);
|
||||
}
|
||||
|
||||
void ProgressBar::finish(const std::uint64_t completed_frames, const bool success) {
|
||||
if (impl_ == nullptr || !impl_->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
impl_->render(completed_frames, true);
|
||||
if (!impl_->rendered) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::fprintf(stderr, "%s", success ? "\n" : " [failed]\n");
|
||||
std::fflush(stderr);
|
||||
}
|
||||
|
||||
Mp4Writer::Mp4Writer()
|
||||
: impl_(std::make_unique<Impl>()) {}
|
||||
|
||||
|
||||
+132
-16
@@ -9,6 +9,7 @@
|
||||
#include "cvmmap_streamer/encode/encoder_backend.hpp"
|
||||
#include "cvmmap_streamer/ipc/contracts.hpp"
|
||||
#include "cvmmap_streamer/record/mcap_record_sink.hpp"
|
||||
#include "cvmmap_streamer/tools/zed_progress_bar.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -17,6 +18,7 @@
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <expected>
|
||||
#include <filesystem>
|
||||
@@ -32,6 +34,9 @@
|
||||
|
||||
namespace {
|
||||
|
||||
using cvmmap_streamer::zed_tools::ProgressBar;
|
||||
using cvmmap_streamer::zed_tools::stderr_supports_progress_bar;
|
||||
|
||||
volatile std::sig_atomic_t g_signal_count = 0;
|
||||
volatile std::sig_atomic_t g_last_signal = 0;
|
||||
|
||||
@@ -922,7 +927,8 @@ std::expected<void, std::string> register_mcap_streams(
|
||||
[[nodiscard]]
|
||||
std::expected<void, std::string> sync_streams_to_timestamp(
|
||||
std::vector<CameraStream> &streams,
|
||||
const std::uint64_t effective_start_ts) {
|
||||
const std::uint64_t effective_start_ts,
|
||||
const bool log_sync_info = true) {
|
||||
bool shutdown_logged{false};
|
||||
for (auto &stream : streams) {
|
||||
if (log_shutdown_request(shutdown_logged, "multi-camera sync")) {
|
||||
@@ -962,6 +968,7 @@ std::expected<void, std::string> sync_streams_to_timestamp(
|
||||
}
|
||||
}
|
||||
|
||||
if (log_sync_info) {
|
||||
spdlog::info(
|
||||
"ZED_SVO_MCAP_SYNC input={} label={} sync_position={} first_timestamp_ns={} current_timestamp_ns={} next_timestamp_ns={}",
|
||||
stream.source.path.string(),
|
||||
@@ -971,6 +978,7 @@ std::expected<void, std::string> sync_streams_to_timestamp(
|
||||
stream.current_timestamp_ns,
|
||||
stream.has_next ? stream.next_timestamp_ns : 0);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1146,6 +1154,58 @@ std::expected<void, std::string> advance_after_emit(std::vector<CameraStream> &s
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::expected<std::uint64_t, std::string> count_synced_groups(
|
||||
std::vector<CameraStream> &streams,
|
||||
const std::uint64_t tolerance_ns,
|
||||
const std::uint64_t common_end_ts,
|
||||
const std::optional<std::uint64_t> max_groups) {
|
||||
bool shutdown_logged{false};
|
||||
std::uint64_t counted_groups{0};
|
||||
|
||||
while (true) {
|
||||
if (log_shutdown_request(shutdown_logged, "multi-camera progress scan")) {
|
||||
return std::unexpected("interrupted");
|
||||
}
|
||||
auto group_timestamp = next_synced_group_timestamp(streams, tolerance_ns, common_end_ts);
|
||||
if (!group_timestamp) {
|
||||
return std::unexpected(group_timestamp.error());
|
||||
}
|
||||
if (!*group_timestamp) {
|
||||
break;
|
||||
}
|
||||
|
||||
counted_groups += 1;
|
||||
if (max_groups && counted_groups >= *max_groups) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto advance = advance_after_emit(streams);
|
||||
if (!advance) {
|
||||
if (advance.error() == "end-of-svo") {
|
||||
break;
|
||||
}
|
||||
return std::unexpected(advance.error());
|
||||
}
|
||||
}
|
||||
|
||||
return counted_groups;
|
||||
}
|
||||
|
||||
void reset_streams_after_count(std::vector<CameraStream> &streams) {
|
||||
for (auto &stream : streams) {
|
||||
stream.current_tracking = {};
|
||||
stream.next_tracking = {};
|
||||
stream.current_timestamp_ns = 0;
|
||||
stream.next_timestamp_ns = 0;
|
||||
stream.dropped_frames = 0;
|
||||
stream.sync_position = -1;
|
||||
stream.has_next = false;
|
||||
stream.calibration_written = false;
|
||||
stream.last_tracking_state.reset();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
int run_single_source(
|
||||
const CliOptions &options,
|
||||
@@ -1339,6 +1399,8 @@ int run_single_source(
|
||||
? options.end_frame
|
||||
: static_cast<std::uint32_t>(total_frames - 1);
|
||||
const auto nominal_frame_period_ns = frame_period_ns(camera_config.fps);
|
||||
const auto total_frames_to_emit = static_cast<std::uint64_t>(last_frame - options.start_frame + 1);
|
||||
ProgressBar progress{total_frames_to_emit};
|
||||
|
||||
while (options.start_frame + emitted_frames <= last_frame) {
|
||||
if (log_shutdown_request(shutdown_logged, "single-camera export")) {
|
||||
@@ -1350,6 +1412,7 @@ int run_single_source(
|
||||
break;
|
||||
}
|
||||
if (grab_status != sl::ERROR_CODE::SUCCESS) {
|
||||
progress.finish(emitted_frames, false);
|
||||
sink->close();
|
||||
backend->shutdown();
|
||||
close_camera();
|
||||
@@ -1359,6 +1422,7 @@ int run_single_source(
|
||||
|
||||
const auto image_status = camera.retrieveImage(left_frame, sl::VIEW::LEFT_BGR, sl::MEM::CPU);
|
||||
if (image_status != sl::ERROR_CODE::SUCCESS) {
|
||||
progress.finish(emitted_frames, false);
|
||||
sink->close();
|
||||
backend->shutdown();
|
||||
close_camera();
|
||||
@@ -1366,6 +1430,7 @@ int run_single_source(
|
||||
return exit_code(ToolExitCode::RuntimeError);
|
||||
}
|
||||
if (auto valid = validate_u8c3_mat(left_frame, "left image"); !valid) {
|
||||
progress.finish(emitted_frames, false);
|
||||
sink->close();
|
||||
backend->shutdown();
|
||||
close_camera();
|
||||
@@ -1375,6 +1440,7 @@ int run_single_source(
|
||||
|
||||
const auto depth_status = camera.retrieveMeasure(depth_frame, sl::MEASURE::DEPTH_U16_MM, sl::MEM::CPU);
|
||||
if (depth_status != sl::ERROR_CODE::SUCCESS) {
|
||||
progress.finish(emitted_frames, false);
|
||||
sink->close();
|
||||
backend->shutdown();
|
||||
close_camera();
|
||||
@@ -1382,6 +1448,7 @@ int run_single_source(
|
||||
return exit_code(ToolExitCode::RuntimeError);
|
||||
}
|
||||
if (auto valid = validate_u16c1_mat(depth_frame, "depth map"); !valid) {
|
||||
progress.finish(emitted_frames, false);
|
||||
sink->close();
|
||||
backend->shutdown();
|
||||
close_camera();
|
||||
@@ -1410,6 +1477,7 @@ int run_single_source(
|
||||
};
|
||||
|
||||
if (auto push = backend->push_frame(raw_video); !push) {
|
||||
progress.finish(emitted_frames, false);
|
||||
sink->close();
|
||||
backend->shutdown();
|
||||
close_camera();
|
||||
@@ -1419,6 +1487,7 @@ int run_single_source(
|
||||
|
||||
auto drained = backend->drain();
|
||||
if (!drained) {
|
||||
progress.finish(emitted_frames, false);
|
||||
sink->close();
|
||||
backend->shutdown();
|
||||
close_camera();
|
||||
@@ -1426,6 +1495,7 @@ int run_single_source(
|
||||
return exit_code(ToolExitCode::RuntimeError);
|
||||
}
|
||||
if (auto write = write_access_units(*sink, *drained); !write) {
|
||||
progress.finish(emitted_frames, false);
|
||||
sink->close();
|
||||
backend->shutdown();
|
||||
close_camera();
|
||||
@@ -1445,6 +1515,7 @@ int run_single_source(
|
||||
.projection_matrix = projection_matrix,
|
||||
};
|
||||
if (auto write = sink->write_camera_calibration(calibration); !write) {
|
||||
progress.finish(emitted_frames, false);
|
||||
sink->close();
|
||||
backend->shutdown();
|
||||
close_camera();
|
||||
@@ -1457,6 +1528,7 @@ int run_single_source(
|
||||
const auto depth_step_bytes = depth_frame.getStepBytes(sl::MEM::CPU);
|
||||
const auto packed_depth_bytes = static_cast<std::size_t>(width) * sizeof(std::uint16_t);
|
||||
if (depth_step_bytes < packed_depth_bytes) {
|
||||
progress.finish(emitted_frames, false);
|
||||
sink->close();
|
||||
backend->shutdown();
|
||||
close_camera();
|
||||
@@ -1482,6 +1554,7 @@ int run_single_source(
|
||||
.pixels = depth_pixels,
|
||||
};
|
||||
if (auto write = sink->write_depth_map_u16(depth_map); !write) {
|
||||
progress.finish(emitted_frames, false);
|
||||
sink->close();
|
||||
backend->shutdown();
|
||||
close_camera();
|
||||
@@ -1519,6 +1592,7 @@ int run_single_source(
|
||||
},
|
||||
};
|
||||
if (auto write = sink->write_pose(pose_view); !write) {
|
||||
progress.finish(emitted_frames, false);
|
||||
sink->close();
|
||||
backend->shutdown();
|
||||
close_camera();
|
||||
@@ -1529,9 +1603,11 @@ int run_single_source(
|
||||
}
|
||||
|
||||
emitted_frames += 1;
|
||||
progress.update(emitted_frames);
|
||||
}
|
||||
|
||||
if (auto flushed = flush_and_write(*sink, backend); !flushed) {
|
||||
progress.finish(emitted_frames, false);
|
||||
sink->close();
|
||||
backend->shutdown();
|
||||
close_camera();
|
||||
@@ -1544,6 +1620,7 @@ int run_single_source(
|
||||
close_camera();
|
||||
|
||||
if (interrupted) {
|
||||
progress.finish(emitted_frames, false);
|
||||
spdlog::warn(
|
||||
"gracefully stopped after writing {} frame(s) from '{}' to '{}'",
|
||||
emitted_frames,
|
||||
@@ -1552,6 +1629,7 @@ int run_single_source(
|
||||
return interrupted_exit_code();
|
||||
}
|
||||
|
||||
progress.finish(emitted_frames, true);
|
||||
spdlog::info(
|
||||
"wrote {} frame(s) from '{}' to '{}'",
|
||||
emitted_frames,
|
||||
@@ -1628,21 +1706,10 @@ int run_multi_source(
|
||||
common_start_ts,
|
||||
common_end_ts,
|
||||
tolerance_ns);
|
||||
|
||||
auto sink = cvmmap_streamer::record::MultiMcapRecordSink::create(output_path.string(), compression);
|
||||
if (!sink) {
|
||||
close_camera_streams(streams);
|
||||
spdlog::error("failed to create MCAP sink: {}", sink.error());
|
||||
return exit_code(ToolExitCode::RuntimeError);
|
||||
}
|
||||
if (auto registered = register_mcap_streams(*sink, streams, options); !registered) {
|
||||
sink->close();
|
||||
close_camera_streams(streams);
|
||||
spdlog::error("failed to register MCAP streams: {}", registered.error());
|
||||
return exit_code(ToolExitCode::RuntimeError);
|
||||
}
|
||||
if (auto synced = sync_streams_to_timestamp(streams, common_start_ts); !synced) {
|
||||
sink->close();
|
||||
const auto render_progress = stderr_supports_progress_bar();
|
||||
std::uint64_t total_groups_to_emit{0};
|
||||
if (render_progress) {
|
||||
if (auto synced = sync_streams_to_timestamp(streams, common_start_ts, false); !synced) {
|
||||
close_camera_streams(streams);
|
||||
if (synced.error() == "interrupted") {
|
||||
return interrupted_exit_code();
|
||||
@@ -1650,6 +1717,49 @@ int run_multi_source(
|
||||
spdlog::error("{}", synced.error());
|
||||
return exit_code(ToolExitCode::RuntimeError);
|
||||
}
|
||||
if (!options.has_end_frame) {
|
||||
std::fprintf(stderr, "counting synced groups for exact progress...\n");
|
||||
std::fflush(stderr);
|
||||
}
|
||||
const auto max_groups = options.has_end_frame
|
||||
? std::optional<std::uint64_t>{static_cast<std::uint64_t>(options.end_frame) + 1}
|
||||
: std::nullopt;
|
||||
auto counted_groups = count_synced_groups(streams, tolerance_ns, common_end_ts, max_groups);
|
||||
if (!counted_groups) {
|
||||
close_camera_streams(streams);
|
||||
if (counted_groups.error() == "interrupted") {
|
||||
return interrupted_exit_code();
|
||||
}
|
||||
spdlog::error("failed to count synced groups: {}", counted_groups.error());
|
||||
return exit_code(ToolExitCode::RuntimeError);
|
||||
}
|
||||
total_groups_to_emit = *counted_groups;
|
||||
reset_streams_after_count(streams);
|
||||
}
|
||||
if (auto synced = sync_streams_to_timestamp(streams, common_start_ts); !synced) {
|
||||
close_camera_streams(streams);
|
||||
if (synced.error() == "interrupted") {
|
||||
return interrupted_exit_code();
|
||||
}
|
||||
spdlog::error("{}", synced.error());
|
||||
return exit_code(ToolExitCode::RuntimeError);
|
||||
}
|
||||
ProgressBar progress{total_groups_to_emit};
|
||||
|
||||
auto sink = cvmmap_streamer::record::MultiMcapRecordSink::create(output_path.string(), compression);
|
||||
if (!sink) {
|
||||
progress.finish(0, false);
|
||||
close_camera_streams(streams);
|
||||
spdlog::error("failed to create MCAP sink: {}", sink.error());
|
||||
return exit_code(ToolExitCode::RuntimeError);
|
||||
}
|
||||
if (auto registered = register_mcap_streams(*sink, streams, options); !registered) {
|
||||
progress.finish(0, false);
|
||||
sink->close();
|
||||
close_camera_streams(streams);
|
||||
spdlog::error("failed to register MCAP streams: {}", registered.error());
|
||||
return exit_code(ToolExitCode::RuntimeError);
|
||||
}
|
||||
|
||||
std::uint64_t emitted_groups{0};
|
||||
while (true) {
|
||||
@@ -1659,6 +1769,7 @@ int run_multi_source(
|
||||
}
|
||||
auto group_timestamp = next_synced_group_timestamp(streams, tolerance_ns, common_end_ts);
|
||||
if (!group_timestamp) {
|
||||
progress.finish(emitted_groups, false);
|
||||
sink->close();
|
||||
close_camera_streams(streams);
|
||||
if (group_timestamp.error() == "interrupted") {
|
||||
@@ -1672,12 +1783,14 @@ int run_multi_source(
|
||||
}
|
||||
|
||||
if (auto write = encode_and_write_group(*sink, streams, options, **group_timestamp); !write) {
|
||||
progress.finish(emitted_groups, false);
|
||||
sink->close();
|
||||
close_camera_streams(streams);
|
||||
spdlog::error("{}", write.error());
|
||||
return exit_code(ToolExitCode::RuntimeError);
|
||||
}
|
||||
emitted_groups += 1;
|
||||
progress.update(emitted_groups);
|
||||
if (options.has_end_frame && emitted_groups > options.end_frame) {
|
||||
break;
|
||||
}
|
||||
@@ -1687,6 +1800,7 @@ int run_multi_source(
|
||||
if (advance.error() == "end-of-svo") {
|
||||
break;
|
||||
}
|
||||
progress.finish(emitted_groups, false);
|
||||
sink->close();
|
||||
close_camera_streams(streams);
|
||||
spdlog::error("{}", advance.error());
|
||||
@@ -1696,6 +1810,7 @@ int run_multi_source(
|
||||
|
||||
for (auto &stream : streams) {
|
||||
if (auto flushed = flush_and_write(*sink, stream.mcap_stream_id, *stream.backend); !flushed) {
|
||||
progress.finish(emitted_groups, false);
|
||||
sink->close();
|
||||
close_camera_streams(streams);
|
||||
spdlog::error("failed to finalize encoded video for {}: {}", stream.source.label, flushed.error());
|
||||
@@ -1704,6 +1819,7 @@ int run_multi_source(
|
||||
}
|
||||
|
||||
sink->close();
|
||||
progress.finish(emitted_groups, !interrupted);
|
||||
for (const auto &stream : streams) {
|
||||
spdlog::info(
|
||||
"bundled {} dropped_frame(s) for {}",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <sl/Camera.hpp>
|
||||
|
||||
#include "cvmmap_streamer/tools/zed_progress_bar.hpp"
|
||||
#include "cvmmap_streamer/tools/zed_svo_mp4_support.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
Vendored
+7
@@ -8,9 +8,16 @@ Dependencies:
|
||||
- `tomlplusplus` (vendored source drop): TOML parsing, target `tomlplusplus::tomlplusplus`
|
||||
- `mcap` (vendored source drop): MCAP headers, target `mcap::mcap`
|
||||
|
||||
Current submodule state:
|
||||
- Active git submodules: `third_party/CLI11`, `third_party/proxy`
|
||||
- Vendored source drops: `third_party/tomlplusplus`, `third_party/mcap`
|
||||
|
||||
Bootstrap:
|
||||
- `git submodule sync --recursive`
|
||||
- `git submodule update --init --recursive`
|
||||
|
||||
Rule:
|
||||
- New vendored dependencies must live under `third_party/` and be exposed through `third_party/CMakeLists.txt`.
|
||||
|
||||
TODO:
|
||||
- Revisit `third_party/mcap` and convert it from a vendored source drop to a git submodule once the local Zstd fix is either upstreamed or moved into a maintained fork.
|
||||
|
||||
Reference in New Issue
Block a user