ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
RSBagReader.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 #if FMT_VERSION >= 100000
11 #include <fmt/std.h>
12 #endif
13 #include <json/json.h>
14 
15 #include <chrono>
16 #include <iomanip>
17 #include <iostream>
18 #include <mutex>
19 #include <sstream>
20 #include <tuple>
21 
24 
25 namespace cloudViewer {
26 namespace t {
27 namespace io {
28 
29 // If DEFAULT_BUFFER_SIZE is odr-uses, a definition is required.
30 // For Fedora33, GCC10, CUDA11.2:
31 // Fix undefined symble error in pybind-*-linux-gnu.so.
32 // See: https://github.com/isl-org/Open3D/issues/3141
34 
35 RSBagReader::RSBagReader(size_t buffer_size)
36  : frame_buffer_(buffer_size),
37  frame_position_us_(buffer_size),
38  pipe_(nullptr) {}
39 
41  if (IsOpened()) Close();
42 }
43 
44 bool RSBagReader::Open(const std::string &filename) {
45  if (IsOpened()) {
46  Close();
47  }
48  try {
49  rs2::config cfg;
50  cfg.enable_device_from_file(filename, false); // Do not repeat playback
51  cfg.disable_all_streams();
52  cfg.enable_stream(RS2_STREAM_DEPTH);
53  cfg.enable_stream(RS2_STREAM_COLOR);
54  pipe_.reset(new rs2::pipeline);
55  auto profile = pipe_->start(cfg); // Open file in read mode
56  // do not drop frames: Causes deadlock after 4 frames on macOS/Linux
57  // https://github.com/IntelRealSense/librealsense/issues/7547#issuecomment-706984376
58  /* profile.get_device().as<rs2::playback>().set_real_time(false); */
59  metadata_.ConvertFromJsonValue(
62  if (seek_to_ == UINT64_MAX) {
63  utility::LogInfo("File {} opened", filename);
64  }
65  } catch (const rs2::error &) {
66  utility::LogWarning("Unable to open file {}", filename);
67  return false;
68  }
69  filename_ = filename;
70  is_eof_ = false;
71  is_opened_ = true;
72  // Launch thread to keep frame_buffer full
73  frame_reader_thread_ = std::thread{&RSBagReader::fill_frame_buffer, this};
74  return true;
75 }
76 
78  is_opened_ = false;
79  need_frames_.notify_one();
80  frame_reader_thread_.join();
81  pipe_->stop();
82 }
83 
84 void RSBagReader::fill_frame_buffer() try {
85  std::mutex frame_buffer_mutex;
86  std::unique_lock<std::mutex> lock(frame_buffer_mutex);
87  const unsigned int RS2_PLAYBACK_TIMEOUT_MS =
88  static_cast<unsigned int>(10 * 1000.0 / metadata_.fps_);
89  rs2::frameset frames;
90  rs2::playback rs_device =
91  pipe_->get_active_profile().get_device().as<rs2::playback>();
92  rs2::align align_to_color(rs2_stream::RS2_STREAM_COLOR);
93  head_fid_ = 0;
94  tail_fid_ = 0;
95  uint64_t next_dev_color_fid = 0;
96  uint64_t dev_color_fid = 0;
97 
98  while (is_opened_) {
99  rs_device.resume();
101  "frame_reader_thread_ start reading tail_fid_={}, "
102  "head_fid_={}.",
103  tail_fid_, head_fid_);
104  while (!is_eof_ && head_fid_ < tail_fid_ + frame_buffer_.size()) {
105  if (seek_to_ < UINT64_MAX) {
106  utility::LogDebug("frame_reader_thread_ seek to {}us",
107  seek_to_);
108  rs_device.seek(std::chrono::microseconds(seek_to_));
109  tail_fid_.store(
110  head_fid_.load()); // atomic: Invalidate buffer.
111  next_dev_color_fid = dev_color_fid = 0;
112  seek_to_ = UINT64_MAX;
113  }
114  // Ensure next frameset is not a repeat
115  while (next_dev_color_fid == dev_color_fid &&
116  pipe_->try_wait_for_frames(&frames,
117  RS2_PLAYBACK_TIMEOUT_MS)) {
118  next_dev_color_fid =
119  frames.get_color_frame().get_frame_number();
120  }
121  if (next_dev_color_fid != dev_color_fid) {
122  dev_color_fid = next_dev_color_fid;
123  auto &current_frame =
124  frame_buffer_[head_fid_ % frame_buffer_.size()];
125 
126  frames = align_to_color.process(frames);
127  const auto &color_frame = frames.get_color_frame();
128  // Copy frame data to Tensors
129  current_frame.color_ = core::Tensor(
130  static_cast<const uint8_t *>(color_frame.get_data()),
131  {color_frame.get_height(), color_frame.get_width(),
132  metadata_.color_channels_},
133  metadata_.color_dt_);
134  const auto &depth_frame = frames.get_depth_frame();
135  current_frame.depth_ = core::Tensor(
136  static_cast<const uint16_t *>(depth_frame.get_data()),
137  {depth_frame.get_height(), depth_frame.get_width()},
138  metadata_.depth_dt_);
139  frame_position_us_[head_fid_ % frame_buffer_.size()] =
140  rs_device.get_position() /
141  1000; // Convert nanoseconds -> microseconds
142  ++head_fid_; // atomic
143  } else {
144  utility::LogDebug("frame_reader_thread EOF. Join.");
145  is_eof_ = true;
146  return;
147  }
148  if (!is_opened_) break; // exit if Close()
149  }
150  rs_device.pause(); // Pause playback to prevent frame drops
152  "frame_reader_thread pause reading tail_fid_={}, head_fid_={}",
153  tail_fid_, head_fid_);
154  need_frames_.wait(lock, [this] {
155  return !is_opened_ ||
156  head_fid_ < tail_fid_ + frame_buffer_.size() /
157  BUFFER_REFILL_FACTOR;
158  });
159  }
160 } catch (const rs2::error &e) {
161  utility::LogError("Realsense error: {}: {}",
162  rs2_exception_type_to_string(e.get_type()), e.what());
163 } catch (const std::exception &e) {
164  utility::LogError("Error in reading RealSense bag file: {}", e.what());
165 }
166 
167 bool RSBagReader::IsEOF() const { return is_eof_ && tail_fid_ == head_fid_; }
168 
170  if (!IsOpened()) {
171  utility::LogError("Null file handler. Please call Open().");
172  }
173  if (!is_eof_ &&
174  head_fid_ < tail_fid_ + frame_buffer_.size() / BUFFER_REFILL_FACTOR)
175  need_frames_.notify_one();
176 
177  while (!is_eof_ &&
178  tail_fid_ ==
179  head_fid_) { // (rare) spin wait for frame_reader_thread_
180  std::this_thread::sleep_for(
181  std::chrono::duration<double>(1 / metadata_.fps_));
182  }
183  if (is_eof_ && tail_fid_ == head_fid_) { // no more frames
184  utility::LogInfo("EOF reached");
185  return t::geometry::RGBDImage();
186  } else {
187  return frame_buffer_[(tail_fid_++) % // atomic
188  frame_buffer_.size()];
189  }
190 }
191 
192 bool RSBagReader::SeekTimestamp(uint64_t timestamp) {
193  if (!IsOpened()) {
194  utility::LogWarning("Null file handler. Please call Open().");
195  return false;
196  }
197  if (timestamp >= metadata_.stream_length_usec_) {
198  utility::LogWarning("Timestamp {} exceeds maximum {} (us).", timestamp,
199  metadata_.stream_length_usec_);
200  return false;
201  }
202  seek_to_ = timestamp; // atomic
203  if (is_eof_) {
204  Open(filename_); // EOF requires restarting pipeline.
205  } else {
206  need_frames_.notify_one();
207  }
208  return true;
209 }
210 
211 uint64_t RSBagReader::GetTimestamp() const {
212  if (!IsOpened()) {
213  utility::LogWarning("Null file handler. Please call Open().");
214  return UINT64_MAX;
215  }
216  return tail_fid_ == 0
217  ? 0
218  : frame_position_us_[(tail_fid_ - 1) % frame_buffer_.size()];
219 }
220 
221 } // namespace io
222 } // namespace t
223 } // namespace cloudViewer
std::string filename
CloudViewerScene::LightingProfile profile
RGBDImage A pair of color and depth images.
Definition: RGBDImage.h:21
core::Dtype depth_dt_
Pixel Dtype for depth data.
bool ConvertFromJsonValue(const Json::Value &value) override
core::Dtype color_dt_
Pixel Dtype for color data.
uint64_t stream_length_usec_
Length of the video (usec). 0 for live capture.
virtual bool SeekTimestamp(uint64_t timestamp) override
static const size_t DEFAULT_BUFFER_SIZE
Definition: RSBagReader.h:51
RSBagReader(size_t buffer_size=DEFAULT_BUFFER_SIZE)
Definition: RSBagReader.cpp:35
virtual bool IsEOF() const override
Check if the RSBag file is all read.
virtual bool IsOpened() const override
Check If the RSBag file is opened.
Definition: RSBagReader.h:64
virtual uint64_t GetTimestamp() const override
Get current timestamp (in us).
virtual bool Open(const std::string &filename) override
Definition: RSBagReader.cpp:44
virtual t::geometry::RGBDImage NextFrame() override
Copy next frame from the bag file and return the RGBDImage object.
virtual void Close() override
Close the opened RSBag playback.
Definition: RSBagReader.cpp:77
static void GetPixelDtypes(const rs2::pipeline_profile &profile, RGBDVideoMetadata &metadata)
static Json::Value GetMetadataJson(const rs2::pipeline_profile &profile)
Get metadata for a streaming RealSense camera or bag file.
#define LogWarning(...)
Definition: Logging.h:72
#define LogInfo(...)
Definition: Logging.h:81
#define LogError(...)
Definition: Logging.h:60
#define LogDebug(...)
Definition: Logging.h:90
static void error(char *msg)
Definition: lsd.c:159
Generic file read and write utility for python interface.