#include "recorder.hpp" #include #include #include #include "math_tools.hpp" #include "tools/logger.hpp" namespace tools { Recorder::Recorder(double fps) : init_(false), fps_(fps), queue_(1), stop_thread_(false) { start_time_ = std::chrono::steady_clock::now(); last_time_ = start_time_; auto folder_path = "records"; auto file_name = fmt::format("{:%Y-%m-%d_%H-%M-%S}", std::chrono::system_clock::now()); text_path_ = fmt::format("{}/{}.txt", folder_path, file_name); video_path_ = fmt::format("{}/{}.avi", folder_path, file_name); std::filesystem::create_directory(folder_path); } Recorder::~Recorder() { stop_thread_ = true; // 退出时给队列中额外推入一个空帧,避免pop一直等待 queue_.push({cv::Mat::zeros(0, 0, 0), {0, 0, 0, 0}, std::chrono::steady_clock::now()}); if (saving_thread_.joinable()) saving_thread_.join(); // 等待视频保存线程结束 if (!init_) return; text_writer_.close(); video_writer_.release(); } void Recorder::save_to_file() { while (!stop_thread_) { FrameData frame; queue_.pop(frame); // 从队列中取出帧数据 if (frame.img.empty()) { tools::logger()->debug("Recorder received empty img. Skip this frame."); continue; } // 写入视频文件 video_writer_.write(frame.img); // 写入文本文件(输出顺序为wxyz) Eigen::Vector4d xyzw = frame.q.coeffs(); auto since_begin = tools::delta_time(frame.timestamp, start_time_); text_writer_ << fmt::format( "{} {} {} {} {}\n", since_begin, xyzw[3], xyzw[0], xyzw[1], xyzw[2]); } } void Recorder::record( const cv::Mat & img, const Eigen::Quaterniond & q, const std::chrono::steady_clock::time_point & timestamp) { if (img.empty()) return; if (!init_) init(img); auto since_last = tools::delta_time(timestamp, last_time_); if (since_last < 1.0 / fps_) return; last_time_ = timestamp; queue_.push({img, q, timestamp}); } void Recorder::init(const cv::Mat & img) { text_writer_.open(text_path_); auto fourcc = cv::VideoWriter::fourcc('M', 'J', 'P', 'G'); video_writer_ = cv::VideoWriter(video_path_, fourcc, fps_, img.size()); saving_thread_ = std::thread(&Recorder::save_to_file, this); // 启动保存线程 init_ = true; } } // namespace tools