···3434 */
3535struct euroc_player_playback_config
3636{
3737- bool stereo; //!< Whether to stream both left and right sinks or only left
3737+ int cam_count; //!< Number of cameras to stream from the dataset
3838 bool color; //!< If RGB available but this is false, images will be loaded in grayscale
3939 bool gt; //!< Whether to send groundtruth data (if available) to the SLAM tracker
4040 bool skip_perc; //!< Whether @ref skip_first represents percentage or seconds
···5757struct euroc_player_dataset_info
5858{
5959 char path[256];
6060- bool is_stereo;
6060+ int cam_count;
6161 bool is_colored;
6262 bool has_gt; //!< Whether this dataset has groundtruth data available
6363 const char *gt_device_name;
+152-127
src/xrt/drivers/euroc/euroc_player.cpp
···77 * @ingroup drv_euroc
88 */
991010+#include "xrt/xrt_frame.h"
1011#include "xrt/xrt_tracking.h"
1112#include "xrt/xrt_frameserver.h"
1213#include "os/os_threading.h"
···2526#include <algorithm>
2627#include <chrono>
2728#include <cstring>
2929+#include <stdint.h>
2830#include <stdio.h>
2931#include <fstream>
3032#include <future>
···3335//! @see euroc_player_playback_config
3436DEBUG_GET_ONCE_LOG_OPTION(euroc_log, "EUROC_LOG", U_LOGGING_WARN)
3537DEBUG_GET_ONCE_OPTION(gt_device_name, "EUROC_GT_DEVICE_NAME", nullptr)
3636-DEBUG_GET_ONCE_OPTION(stereo, "EUROC_STEREO", nullptr)
3838+DEBUG_GET_ONCE_OPTION(cam_count, "EUROC_CAM_COUNT", nullptr)
3739DEBUG_GET_ONCE_OPTION(color, "EUROC_COLOR", nullptr)
3840DEBUG_GET_ONCE_OPTION(gt, "EUROC_GT", nullptr)
3941DEBUG_GET_ONCE_OPTION(skip_first, "EUROC_SKIP_FIRST", "0%")
···48504951#define EUROC_PLAYER_STR "Euroc Player"
50525353+//! Match max cameras to slam sinks max camera count
5454+#define EUROC_MAX_CAMS XRT_TRACKING_MAX_SLAM_CAMS
5555+5156using std::async;
5257using std::find_if;
5358using std::ifstream;
5459using std::is_same_v;
5560using std::launch;
6161+using std::max_element;
5662using std::pair;
5763using std::stof;
5864using std::string;
6565+using std::to_string;
5966using std::vector;
60676168using img_sample = pair<timepoint_ns, string>;
···8592 struct xrt_frame_node node;
86938794 // Sinks
8888- struct xrt_frame_sink left_sink; //!< Intermediate sink for left camera frames
8989- struct xrt_frame_sink right_sink; //!< Intermediate sink for right camera frames
9090- struct xrt_imu_sink imu_sink; //!< Intermediate sink for IMU samples
9191- struct xrt_slam_sinks in_sinks; //!< Pointers to intermediate sinks
9292- struct xrt_slam_sinks out_sinks; //!< Pointers to downstream sinks
9595+ struct xrt_frame_sink cam_sinks[EUROC_MAX_CAMS]; //!< Intermediate sink for each camera frames
9696+ struct xrt_imu_sink imu_sink; //!< Intermediate sink for IMU samples
9797+ struct xrt_slam_sinks in_sinks; //!< Pointers to intermediate sinks
9898+ struct xrt_slam_sinks out_sinks; //!< Pointers to downstream sinks
939994100 enum u_logging_level log_level; //!< Log messages with this priority and onwards
95101 struct euroc_player_dataset_info dataset; //!< Contains information about the source dataset
···99105 timepoint_ns last_pause_ts; //!< Last time the stream was paused
100106 struct os_thread_helper play_thread;
101107102102- //! Next frame number to use, index in `left_imgs` and `right_imgs`.
108108+ //! Next frame number to use, index in `imgs[i]`.
103109 //! Note that this expects that both cameras provide the same amount of frames.
104110 //! Furthermore, it is also expected that their timestamps match.
105105- uint64_t img_seq; //!< Next frame number to use, index in `{left, right}_imgs`
106106- uint64_t imu_seq; //!< Next imu sample number to use, index in `imus`
107107- imu_samples *imus; //!< List of all IMU samples read from the dataset
108108- img_samples *left_imgs; //!< List of all image names to read from the dataset
109109- img_samples *right_imgs; //!< List of all image names to read from the dataset
110110- gt_trajectory *gt; //!< List of all groundtruth poses read from the dataset
111111+ uint64_t img_seq; //!< Next frame number to use, index in `imgs[i]`
112112+ uint64_t imu_seq; //!< Next imu sample number to use, index in `imus`
113113+ imu_samples *imus; //!< List of all IMU samples read from the dataset
114114+ vector<img_samples> *imgs; //!< List of all image names to read from the dataset per camera
115115+ gt_trajectory *gt; //!< List of all groundtruth poses read from the dataset
111116112117 // Timestamp correction fields (can be disabled through `use_source_ts`)
113118 timepoint_ns base_ts; //!< First sample timestamp, stream timestamps are relative to this
···119124 struct u_var_button start_btn;
120125 struct u_var_button pause_btn;
121126 char progress_text[128];
122122- struct u_sink_debug ui_left_sink; //!< Sink to display left frames in UI
123123- struct u_sink_debug ui_right_sink; //!< Sink to display right frames in UI
124124- struct m_ff_vec3_f32 *gyro_ff; //!< Used for displaying IMU data
125125- struct m_ff_vec3_f32 *accel_ff; //!< Same as `gyro_ff`
127127+ struct u_sink_debug ui_cam_sinks[EUROC_MAX_CAMS]; //!< Sinks to display cam frames in UI
128128+ struct m_ff_vec3_f32 *gyro_ff; //!< Used for displaying IMU data
129129+ struct m_ff_vec3_f32 *accel_ff; //!< Same as `gyro_ff`
126130};
127131128132static void
···235239//! If read_n > 0, read at most that amount of samples
236240//! Returns whether the appropriate data.csv file could be opened
237241static bool
238238-euroc_player_preload_img_data(const string &dataset_path, img_samples *samples, bool is_left, int64_t read_n = -1)
242242+euroc_player_preload_img_data(const string &dataset_path, img_samples &samples, size_t cam_id, int64_t read_n = -1)
239243{
240244 // Parse image data, assumes data.csv is well formed
241241- string cam_name = is_left ? "cam0" : "cam1";
245245+ string cam_name = "cam" + to_string(cam_id);
242246 string imgs_path = dataset_path + "/mav0/" + cam_name + "/data";
243247 string csv_filename = dataset_path + "/mav0/" + cam_name + "/data.csv";
244248 ifstream fin{csv_filename};
···260264261265 string img_name = imgs_path + "/" + img_name_tail;
262266 img_sample sample{timestamp, img_name};
263263- samples->push_back(sample);
267267+ samples.push_back(sample);
264268 }
265269 return true;
266270}
267271268268-//! Trims left and right sequences so that they start and end at the same sample
269269-//! Note that this function does not guarantee that the dataset is free of framedrops.
272272+//! Trims cameras sequences so that they all start and end at the same sample
273273+//! Note that this function does not guarantee that the dataset is free of framedrops
274274+//! and it assumes it is properly formatted with monotonically increasing timestamps.
270275static void
271271-euroc_player_match_stereo_seqs(struct euroc_player *ep)
276276+euroc_player_match_cams_seqs(struct euroc_player *ep)
272277{
273273- img_samples ls = *ep->left_imgs;
274274- img_samples rs = *ep->right_imgs;
278278+ // Find newest first timestamp and oldest last timestamp
279279+ timepoint_ns first_ts = INT64_MIN;
280280+ timepoint_ns last_ts = INT64_MAX;
281281+ for (const img_samples &imgs : *ep->imgs) {
282282+ EUROC_ASSERT(!imgs.empty(), "Camera with no samples");
275283276276- // Assumes dataset is properly formatted with monotonically increasing timestamps
277277- timepoint_ns first_ts = MAX(ls.at(0).first, rs.at(0).first);
278278- timepoint_ns last_ts = MIN(ls.back().first, rs.back().first);
284284+ timepoint_ns cam_first_ts = imgs.front().first;
285285+ if (cam_first_ts > first_ts) {
286286+ first_ts = cam_first_ts;
287287+ }
288288+289289+ timepoint_ns cam_last_ts = imgs.back().first;
290290+ if (cam_last_ts < last_ts) {
291291+ last_ts = cam_last_ts;
292292+ }
293293+ }
279294280295 auto is_first = [first_ts](const img_sample &s) { return s.first == first_ts; };
281296 auto is_last = [last_ts](const img_sample &s) { return s.first == last_ts; };
282297283283- img_samples::iterator lfirst = find_if(ls.begin(), ls.end(), is_first);
284284- img_samples::iterator llast = find_if(ls.begin(), ls.end(), is_last);
285285- EUROC_ASSERT_(lfirst != ls.end() && llast != ls.end());
286286-287287- img_samples::iterator rfirst = find_if(rs.begin(), rs.end(), is_first);
288288- img_samples::iterator rlast = find_if(rs.begin(), rs.end(), is_last);
289289- EUROC_ASSERT_(rfirst != rs.end() && rlast != rs.end());
290290-291291- ep->left_imgs->assign(lfirst, llast + 1);
292292- ep->right_imgs->assign(rfirst, rlast + 1);
298298+ for (img_samples &imgs : *ep->imgs) {
299299+ img_samples::iterator new_first = find_if(imgs.begin(), imgs.end(), is_first);
300300+ img_samples::iterator new_last = find_if(imgs.begin(), imgs.end(), is_last);
301301+ EUROC_ASSERT_(new_first != imgs.end() && new_last != imgs.end());
302302+ imgs.assign(new_first, new_last + 1);
303303+ }
293304}
294305295306static void
···298309 ep->imus->clear();
299310 euroc_player_preload_imu_data(ep->dataset.path, ep->imus);
300311301301- ep->left_imgs->clear();
302302- euroc_player_preload_img_data(ep->dataset.path, ep->left_imgs, true);
312312+ for (size_t i = 0; i < ep->imgs->size(); i++) {
313313+ ep->imgs->at(i).clear();
314314+ euroc_player_preload_img_data(ep->dataset.path, ep->imgs->at(i), i);
315315+ }
303316304304- if (ep->dataset.is_stereo) {
305305- ep->right_imgs->clear();
306306- euroc_player_preload_img_data(ep->dataset.path, ep->right_imgs, false);
307307-308308- euroc_player_match_stereo_seqs(ep);
309309- }
317317+ euroc_player_match_cams_seqs(ep);
310318311319 if (ep->dataset.has_gt) {
312320 ep->gt->clear();
···322330 float skip_first_s = 0;
323331 if (ep->playback.skip_perc) {
324332 float skip_percentage = ep->playback.skip_first;
325325- timepoint_ns last_ts = MAX(ep->left_imgs->back().first, ep->imus->back().timestamp_ns);
333333+ timepoint_ns last_ts = MAX(ep->imgs->at(0).back().first, ep->imus->back().timestamp_ns);
326334 double dataset_length_s = (last_ts - ep->base_ts) / U_TIME_1S_IN_NS;
327335 skip_first_s = dataset_length_s * skip_percentage / 100.0f;
328336 } else {
···336344 ep->imu_seq++;
337345 }
338346339339- while (ep->img_seq < ep->left_imgs->size() && ep->left_imgs->at(ep->img_seq).first < skipped_ts) {
347347+ while (ep->img_seq < ep->imgs->at(0).size() && ep->imgs->at(0).at(ep->img_seq).first < skipped_ts) {
340348 ep->img_seq++;
341349 }
342350···348356static void
349357euroc_player_fill_dataset_info(const char *path, euroc_player_dataset_info *dataset)
350358{
351351- snprintf(dataset->path, sizeof(dataset->path), "%s", path);
359359+ (void)snprintf(dataset->path, sizeof(dataset->path), "%s", path);
352360 img_samples samples;
353361 imu_samples _1;
354362 gt_trajectory _2;
355355- bool has_right_camera = euroc_player_preload_img_data(dataset->path, &samples, false, 0);
356356- bool has_left_camera = euroc_player_preload_img_data(dataset->path, &samples, true, 1);
363363+364364+ size_t i = 0;
365365+ bool has_camera = euroc_player_preload_img_data(dataset->path, samples, i, 1);
366366+ while ((has_camera = euroc_player_preload_img_data(dataset->path, samples, ++i, 0))) {
367367+ }
368368+ size_t cam_count = i;
369369+ EUROC_ASSERT(cam_count <= EUROC_MAX_CAMS, "Increase EUROC_MAX_CAMS (dataset with %zu cams)", cam_count);
370370+357371 bool has_imu = euroc_player_preload_imu_data(dataset->path, &_1, 0);
358372 bool has_gt = euroc_player_preload_gt_data(dataset->path, &dataset->gt_device_name, &_2, 0);
359359- bool is_valid_dataset = has_left_camera && has_imu;
373373+ bool is_valid_dataset = cam_count > 0 && has_imu;
360374 EUROC_ASSERT(is_valid_dataset, "Invalid dataset %s", path);
361375362362- cv::Mat first_left_img = cv::imread(samples[0].second, cv::IMREAD_ANYCOLOR);
363363- dataset->is_stereo = has_right_camera;
364364- dataset->is_colored = first_left_img.channels() == 3;
376376+ cv::Mat first_cam0_img = cv::imread(samples[0].second, cv::IMREAD_ANYCOLOR);
377377+ dataset->cam_count = (int)cam_count;
378378+ dataset->is_colored = first_cam0_img.channels() == 3;
365379 dataset->has_gt = has_gt;
366366- dataset->width = first_left_img.cols;
367367- dataset->height = first_left_img.rows;
380380+ dataset->width = first_cam0_img.cols;
381381+ dataset->height = first_cam0_img.rows;
368382}
369383370384···408422}
409423410424static void
411411-euroc_player_load_next_frame(struct euroc_player *ep, bool is_left, struct xrt_frame *&xf)
425425+euroc_player_load_next_frame(struct euroc_player *ep, int cam_index, struct xrt_frame *&xf)
412426{
413427 using xrt::auxiliary::tracking::FrameMat;
414414- img_sample sample = is_left ? ep->left_imgs->at(ep->img_seq) : ep->right_imgs->at(ep->img_seq);
428428+ img_sample sample = ep->imgs->at(cam_index).at(ep->img_seq);
415429 ep->playback.scale = CLAMP(ep->playback.scale, 1.0 / 16, 4);
416430417431 // Load will be influenced by these playback options
···421435 // Load image from disk
422436 timepoint_ns timestamp = euroc_player_mapped_playback_ts(ep, sample.first);
423437 string img_name = sample.second;
424424- EUROC_TRACE(ep, "%s img t = %ld filename = %s", is_left ? "left" : "right", timestamp, img_name.c_str());
438438+ EUROC_TRACE(ep, "cam%d img t = %ld filename = %s", cam_index, timestamp, img_name.c_str());
425439 cv::ImreadModes read_mode = allow_color ? cv::IMREAD_ANYCOLOR : cv::IMREAD_GRAYSCALE;
426440 cv::Mat img = cv::imread(img_name, read_mode); // If colored, reads in BGR order
427441···450464static void
451465euroc_player_push_next_frame(struct euroc_player *ep)
452466{
453453- bool stereo = ep->playback.stereo;
467467+ int cam_count = ep->playback.cam_count;
468468+469469+ vector<xrt_frame *> xfs(cam_count, nullptr);
470470+ for (int i = 0; i < cam_count; i++) {
471471+ euroc_player_load_next_frame(ep, i, xfs[i]);
472472+ }
454473455455- struct xrt_frame *left_xf = NULL;
456456- struct xrt_frame *right_xf = NULL;
457457- euroc_player_load_next_frame(ep, true, left_xf);
458458- if (stereo) {
459459- // TODO: Some SLAM systems expect synced frames, but that's not an
460460- // EuRoC requirement. Adapt to work with unsynced datasets too.
461461- euroc_player_load_next_frame(ep, false, right_xf);
462462- EUROC_ASSERT(left_xf->timestamp == right_xf->timestamp, "Unsynced stereo frames");
474474+ // TODO: Some SLAM systems expect synced frames, but that's not an
475475+ // EuRoC requirement. Adapt to work with unsynced datasets too.
476476+ for (int i = 1; i < cam_count; i++) {
477477+ EUROC_ASSERT(xfs[i - 1]->timestamp == xfs[i]->timestamp, "Unsynced frames");
463478 }
479479+464480 ep->img_seq++;
465481466466- xrt_sink_push_frame(ep->in_sinks.cams[0], left_xf);
467467- if (stereo) {
468468- xrt_sink_push_frame(ep->in_sinks.cams[1], right_xf);
482482+ for (int i = 0; i < cam_count; i++) {
483483+ xrt_sink_push_frame(ep->in_sinks.cams[i], xfs[i]);
469484 }
470485471471- xrt_frame_reference(&left_xf, NULL);
472472- xrt_frame_reference(&right_xf, NULL);
486486+ for (int i = 0; i < cam_count; i++) {
487487+ xrt_frame_reference(&xfs[i], NULL);
488488+ }
473489474474- snprintf(ep->progress_text, sizeof(ep->progress_text), "Frames %lu/%lu - IMUs %lu/%lu", ep->img_seq,
475475- ep->left_imgs->size(), ep->imu_seq, ep->imus->size());
490490+ size_t fcount = ep->imgs->at(0).size();
491491+ (void)snprintf(ep->progress_text, sizeof(ep->progress_text), "Playback %.2f%% - Frame %lu/%lu - IMU %lu/%lu",
492492+ float(ep->img_seq) / float(fcount) * 100, ep->img_seq, fcount, ep->imu_seq, ep->imus->size());
476493477494 if (ep->playback.print_progress) {
478478- printf("Playback %.2f%% - Frame %lu/%lu\r", float(ep->img_seq) / float(ep->left_imgs->size()) * 100,
479479- ep->img_seq, ep->left_imgs->size());
480480- fflush(stdout);
495495+ printf("%s\r", ep->progress_text);
496496+ (void)fflush(stdout);
481497 }
482498}
483499···509525 if constexpr (is_same_v<SamplesType, imu_samples>) {
510526 return ep->imus->at(ep->imu_seq).timestamp_ns;
511527 } else {
512512- return ep->left_imgs->at(ep->img_seq).first;
528528+ return ep->imgs->at(0).at(ep->img_seq).first;
513529 }
514530}
515531···550566 if constexpr (is_imu) {
551567 samples = ep->imus;
552568 } else {
553553- samples = ep->left_imgs;
569569+ samples = &ep->imgs->at(0);
554570 }
555571 uint64_t *sample_seq = is_imu ? &ep->imu_seq : &ep->img_seq;
556572 auto push_next_sample = is_imu ? euroc_player_push_next_imu : euroc_player_push_next_frame;
···588604 EUROC_INFO(ep, "Starting euroc playback");
589605590606 euroc_player_preload(ep);
591591- ep->base_ts = MIN(ep->left_imgs->at(0).first, ep->imus->at(0).timestamp_ns);
607607+ ep->base_ts = MIN(ep->imgs->at(0).at(0).first, ep->imus->at(0).timestamp_ns);
592608 ep->start_ts = os_monotonic_get_ts();
593609 euroc_player_user_skip(ep);
594610···656672 return false;
657673}
658674659659-static void
660660-receive_left_frame(struct xrt_frame_sink *sink, struct xrt_frame *xf)
661661-{
662662- struct euroc_player *ep = container_of(sink, struct euroc_player, left_sink);
663663- EUROC_TRACE(ep, "left img t=%ld source_t=%ld", xf->timestamp, xf->source_timestamp);
664664- u_sink_debug_push_frame(&ep->ui_left_sink, xf);
665665- if (ep->out_sinks.cams[0]) {
666666- xrt_sink_push_frame(ep->out_sinks.cams[0], xf);
675675+#define DEFINE_RECEIVE_CAM(cam_id) \
676676+ static void receive_cam##cam_id(struct xrt_frame_sink *sink, struct xrt_frame *xf) \
677677+ { \
678678+ struct euroc_player *ep = container_of(sink, struct euroc_player, cam_sinks[cam_id]); \
679679+ EUROC_TRACE(ep, "cam%d img t=%ld source_t=%ld", cam_id, xf->timestamp, xf->source_timestamp); \
680680+ u_sink_debug_push_frame(&ep->ui_cam_sinks[cam_id], xf); \
681681+ if (ep->out_sinks.cams[cam_id]) { \
682682+ xrt_sink_push_frame(ep->out_sinks.cams[cam_id], xf); \
683683+ } \
667684 }
668668-}
685685+686686+687687+DEFINE_RECEIVE_CAM(0)
688688+DEFINE_RECEIVE_CAM(1)
689689+DEFINE_RECEIVE_CAM(2)
690690+DEFINE_RECEIVE_CAM(3)
691691+DEFINE_RECEIVE_CAM(4)
669692670670-static void
671671-receive_right_frame(struct xrt_frame_sink *sink, struct xrt_frame *xf)
672672-{
673673- struct euroc_player *ep = container_of(sink, struct euroc_player, right_sink);
674674- EUROC_TRACE(ep, "right img t=%ld source_t=%ld", xf->timestamp, xf->source_timestamp);
675675- u_sink_debug_push_frame(&ep->ui_right_sink, xf);
676676- if (ep->out_sinks.cams[1]) {
677677- xrt_sink_push_frame(ep->out_sinks.cams[1], xf);
678678- }
679679-}
693693+//! Be sure to define the same number of definition as EUROC_MAX_CAMS and to add them to `receive_cam`.
694694+static void (*receive_cam[EUROC_MAX_CAMS])(struct xrt_frame_sink *, struct xrt_frame *) = {
695695+ receive_cam0, //
696696+ receive_cam1, //
697697+ receive_cam2, //
698698+ receive_cam3, //
699699+ receive_cam4, //
700700+};
680701681702static void
682703receive_imu_sample(struct xrt_imu_sink *sink, struct xrt_imu_sample *s)
···715736 if (xs == NULL && capture_type == XRT_FS_CAPTURE_TYPE_TRACKING) {
716737 EUROC_INFO(ep, "Starting Euroc Player in tracking mode");
717738 if (ep->out_sinks.cams[0] == NULL) {
718718- EUROC_WARN(ep, "No left sink provided, will keep running but tracking is unlikely to work");
739739+ EUROC_WARN(ep, "No cam0 sink provided, will keep running but tracking is unlikely to work");
719740 }
720741 if (ep->playback.play_from_start) {
721742 euroc_player_start_btn_cb(ep);
722743 }
723744 } else if (xs != NULL && capture_type == XRT_FS_CAPTURE_TYPE_CALIBRATION) {
724724- EUROC_INFO(ep, "Starting Euroc Player in calibration mode, will stream only left frames right away");
745745+ EUROC_INFO(ep, "Starting Euroc Player in calibration mode, will stream only cam0 frames right away");
725746 ep->out_sinks.cams[0] = xs;
726747 euroc_player_start_btn_cb(ep);
727748 } else {
···777798778799 delete ep->gt;
779800 delete ep->imus;
780780- delete ep->left_imgs;
781781- delete ep->right_imgs;
801801+ delete ep->imgs;
782802783803 u_var_remove_root(ep);
784784- u_sink_debug_destroy(&ep->ui_left_sink);
785785- u_sink_debug_destroy(&ep->ui_right_sink);
804804+ for (int i = 0; i < ep->dataset.cam_count; i++) {
805805+ u_sink_debug_destroy(&ep->ui_cam_sinks[i]);
806806+ }
786807 m_ff_vec3_f32_free(&ep->gyro_ff);
787808 m_ff_vec3_f32_free(&ep->accel_ff);
788809···854875euroc_player_setup_gui(struct euroc_player *ep)
855876{
856877 // Set sinks to display in UI
857857- u_sink_debug_init(&ep->ui_left_sink);
858858- u_sink_debug_init(&ep->ui_right_sink);
878878+ for (int i = 0; i < ep->dataset.cam_count; i++) {
879879+ u_sink_debug_init(&ep->ui_cam_sinks[i]);
880880+ }
859881 m_ff_vec3_f32_alloc(&ep->gyro_ff, 1000);
860882 m_ff_vec3_f32_alloc(&ep->accel_ff, 1000);
861883···876898877899 u_var_add_gui_header(ep, NULL, "Playback Options");
878900 u_var_add_ro_text(ep, "Set these before starting the stream", "Note");
879879- u_var_add_bool(ep, &ep->playback.stereo, "Stereo (if available)");
901901+ u_var_add_i32(ep, &ep->playback.cam_count, "Use N cams (if available)");
880902 u_var_add_bool(ep, &ep->playback.color, "Color (if available)");
881903 u_var_add_bool(ep, &ep->playback.gt, "Groundtruth (if available)");
882904 u_var_add_bool(ep, &ep->playback.skip_perc, "Skip percentage, otherwise skips seconds");
···890912 u_var_add_gui_header(ep, NULL, "Streams");
891913 u_var_add_ro_ff_vec3_f32(ep, ep->gyro_ff, "Gyroscope");
892914 u_var_add_ro_ff_vec3_f32(ep, ep->accel_ff, "Accelerometer");
893893- u_var_add_sink_debug(ep, &ep->ui_left_sink, "Left Camera");
894894- u_var_add_sink_debug(ep, &ep->ui_right_sink, "Right Camera");
915915+ for (int i = 0; i < ep->dataset.cam_count; i++) {
916916+ char label[] = "Camera NNNN";
917917+ (void)snprintf(label, sizeof(label), "Camera %d", i);
918918+ u_var_add_sink_debug(ep, &ep->ui_cam_sinks[i], label);
919919+ }
895920}
896921897922extern "C" void
···902927 euroc_player_fill_dataset_info(dataset_path, &dataset);
903928904929 struct euroc_player_playback_config playback = {};
905905- const char *stereo = debug_get_option_stereo();
930930+ const char *cam_count = debug_get_option_cam_count();
906931 const char *color = debug_get_option_color();
907932 const char *gt = debug_get_option_gt();
908933 const char *skip_option = debug_get_option_skip_first();
909909- playback.stereo = stereo == nullptr ? dataset.is_stereo : debug_string_to_bool(stereo);
934934+ playback.cam_count = (int)debug_string_to_num(cam_count, dataset.cam_count);
910935 playback.color = color == nullptr ? dataset.is_colored : debug_string_to_bool(color);
911936 playback.gt = gt == nullptr ? dataset.has_gt : debug_string_to_bool(gt);
912937 playback.skip_perc = string(skip_option).back() == '%';
···955980 // xrt_fs interface as it will be managed through two different sinks.
956981 XRT_STEREO_FORMAT_NONE,
957982 };
958958- EUROC_INFO(ep, "dataset information\n\tpath: %s\n\tis_stereo: %d, is_colored: %d, width: %d, height: %d",
959959- ep->dataset.path, ep->dataset.is_stereo, ep->dataset.is_colored, ep->dataset.width,
983983+ EUROC_INFO(ep, "dataset information\n\tpath: %s\n\tcam_count: %d, is_colored: %d, width: %d, height: %d",
984984+ ep->dataset.path, ep->dataset.cam_count, ep->dataset.is_colored, ep->dataset.width,
960985 ep->dataset.height);
961986962987 // Using pointers to not mix vector with a C-compatible struct
963988 ep->gt = new gt_trajectory{};
964989 ep->imus = new imu_samples{};
965965- ep->left_imgs = new img_samples{};
966966- ep->right_imgs = new img_samples{};
990990+ ep->imgs = new vector<img_samples>(ep->dataset.cam_count);
967991968992 euroc_player_setup_gui(ep);
969993970970- ep->left_sink.push_frame = receive_left_frame;
971971- ep->right_sink.push_frame = receive_right_frame;
994994+ EUROC_ASSERT(receive_cam[ARRAY_SIZE(receive_cam) - 1] != nullptr, "See `receive_cam` docs");
995995+ ep->in_sinks.cam_count = ep->dataset.cam_count;
996996+ for (int i = 0; i < ep->dataset.cam_count; i++) {
997997+ ep->cam_sinks[i].push_frame = receive_cam[i];
998998+ ep->in_sinks.cams[i] = &ep->cam_sinks[i];
999999+ }
9721000 ep->imu_sink.push_imu = receive_imu_sample;
973973- ep->in_sinks.cam_count = 2;
974974- ep->in_sinks.cams[0] = &ep->left_sink;
975975- ep->in_sinks.cams[1] = &ep->right_sink;
9761001 ep->in_sinks.imu = &ep->imu_sink;
97710029781003 struct xrt_fs *xfs = &ep->base;
···9831008 xfs->stream_stop = euroc_player_stream_stop;
9841009 xfs->is_running = euroc_player_is_running;
9851010986986- snprintf(xfs->name, sizeof(xfs->name), EUROC_PLAYER_STR);
987987- snprintf(xfs->product, sizeof(xfs->product), EUROC_PLAYER_STR " Product");
988988- snprintf(xfs->manufacturer, sizeof(xfs->manufacturer), EUROC_PLAYER_STR " Manufacturer");
989989- snprintf(xfs->serial, sizeof(xfs->serial), EUROC_PLAYER_STR " Serial");
10111011+ (void)snprintf(xfs->name, sizeof(xfs->name), EUROC_PLAYER_STR);
10121012+ (void)snprintf(xfs->product, sizeof(xfs->product), EUROC_PLAYER_STR " Product");
10131013+ (void)snprintf(xfs->manufacturer, sizeof(xfs->manufacturer), EUROC_PLAYER_STR " Manufacturer");
10141014+ (void)snprintf(xfs->serial, sizeof(xfs->serial), EUROC_PLAYER_STR " Serial");
9901015 xfs->source_id = 0xECD0FEED;
99110169921017 struct xrt_frame_node *xfn = &ep->node;
+1
src/xrt/drivers/euroc/euroc_runner.c
···9292{
9393 struct euroc_player_config *ep_config = make_euroc_player_config(euroc_path);
9494 struct t_slam_tracker_config *st_config = make_slam_tracker_config(slam_config, output_path);
9595+ st_config->cam_count = ep_config->dataset.cam_count;
95969697 // Frame context that will manage SLAM tracker and euroc player lifetimes
9798 struct xrt_frame_context xfctx = {0};