Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 36 additions & 16 deletions src/module/orbbec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,18 @@ std::string formatError(Args&&... args) {
return buffer.str();
}

// Get the best available timestamp for a frame (Global > System)
uint64_t getBestTimestampUs(std::shared_ptr<ob::Frame> frame) {
Copy link

@seanavery seanavery Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uint64_t getBestTimestampUs(std::shared_ptr<ob::Frame> frame) {
uint64_t getBestTimestampUs(const std::shared_ptr<ob::Frame>& frame) {

[maybe nit] Should we pass in as const ref here so we do not increment/decrement the shared_pt every time the helper is called?

if (frame == nullptr) {
return 0;
}
uint64_t ts = frame->getGlobalTimeStampUs();
if (ts > 0) {
return ts;
}
return frame->getSystemTimeStampUs();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

according to your description, it sounds like we shouldn't use system timestamp bc it's unreliable

why are we still using it as a fallback when somehow getGlobalTimeStampUs is LEQ 0?

also, how can getGlobalTimeStampUs be LEQ 0? have you observed this happening

could it happen when global timestamp is not enabled on the system?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we log a single, not noisy warning that color-depth timestamp comparisons are not supported when global timestamp isn't available? Instead of falling back to the unreliable system timestamp?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm, another option would be to support system timestamp and put a threshold of perhaps 400ms between frames. My thinking is that it may be better to have that rough measurement than nothing, but totally open to discuss it.

}

// Validate color frame format and timestamp
void validateColorFrame(std::shared_ptr<ob::Frame> color,
const std::optional<DeviceFormat>& device_format_opt,
Expand All @@ -234,7 +246,7 @@ void validateColorFrame(std::shared_ptr<ob::Frame> color,

// Timestamp validation
uint64_t nowUs = getNowUs();
uint64_t diff = timeSinceFrameUs(nowUs, color->getSystemTimeStampUs());
uint64_t diff = timeSinceFrameUs(nowUs, getBestTimestampUs(color));
if (diff > maxFrameAgeUs) {
throw std::runtime_error(formatError("no recent color frame: check connection, diff: ", diff, "us"));
}
Expand Down Expand Up @@ -271,7 +283,7 @@ void validateDepthFrame(std::shared_ptr<ob::Frame> depth,

// Timestamp validation
uint64_t nowUs = getNowUs();
uint64_t diff = timeSinceFrameUs(nowUs, depth->getSystemTimeStampUs());
uint64_t diff = timeSinceFrameUs(nowUs, getBestTimestampUs(depth));
if (diff > maxFrameAgeUs) {
throw std::runtime_error(formatError("no recent depth frame: check connection, diff: ", diff, "us"));
}
Expand Down Expand Up @@ -654,33 +666,33 @@ auto frameCallback(const std::string& serialNumber) {

std::lock_guard<std::mutex> lock(frame_set_by_serial_mu());
uint64_t nowUs = getNowUs();
uint64_t diff = timeSinceFrameUs(nowUs, color->getSystemTimeStampUs());
uint64_t diff = timeSinceFrameUs(nowUs, getBestTimestampUs(color));
if (diff > maxFrameAgeUs) {
std::cerr << "color frame is " << diff << "us older than now, nowUs: " << nowUs << " frameTimeUs "
<< color->getSystemTimeStampUs() << "\n";
std::cerr << "color frame is " << diff << "us older than now, nowUs: " << nowUs << " frameTimeUs " << getBestTimestampUs(color)
<< "\n";
}
diff = timeSinceFrameUs(nowUs, depth->getSystemTimeStampUs());
diff = timeSinceFrameUs(nowUs, getBestTimestampUs(depth));
if (diff > maxFrameAgeUs) {
std::cerr << "depth frame is " << diff << "us older than now, nowUs: " << nowUs << " frameTimeUs "
<< depth->getSystemTimeStampUs() << "\n";
std::cerr << "depth frame is " << diff << "us older than now, nowUs: " << nowUs << " frameTimeUs " << getBestTimestampUs(depth)
<< "\n";
}

auto it = frame_set_by_serial().find(serialNumber);
if (it != frame_set_by_serial().end()) {
std::shared_ptr<ob::Frame> prevColor = it->second->getFrame(OB_FRAME_COLOR);
std::shared_ptr<ob::Frame> prevDepth = it->second->getFrame(OB_FRAME_DEPTH);
if (prevColor != nullptr && prevDepth != nullptr) {
diff = timeSinceFrameUs(color->getSystemTimeStampUs(), prevColor->getSystemTimeStampUs());
diff = timeSinceFrameUs(getBestTimestampUs(color), getBestTimestampUs(prevColor));
if (diff > maxFrameAgeUs) {
std::cerr << "previous color frame is " << diff
<< "us older than current color frame. previousUs: " << prevColor->getSystemTimeStampUs()
<< " currentUs: " << color->getSystemTimeStampUs() << "\n";
<< "us older than current color frame. previousUs: " << getBestTimestampUs(prevColor)
<< " currentUs: " << getBestTimestampUs(color) << "\n";
}
diff = timeSinceFrameUs(depth->getSystemTimeStampUs(), prevDepth->getSystemTimeStampUs());
diff = timeSinceFrameUs(getBestTimestampUs(depth), getBestTimestampUs(prevDepth));
if (diff > maxFrameAgeUs) {
std::cerr << "previous depth frame is " << diff
<< "us older than current depth frame. previousUs: " << prevDepth->getSystemTimeStampUs()
<< " currentUs: " << depth->getSystemTimeStampUs() << "\n";
<< "us older than current depth frame. previousUs: " << getBestTimestampUs(prevDepth)
<< " currentUs: " << getBestTimestampUs(depth) << "\n";
}
}
}
Expand All @@ -700,6 +712,14 @@ void configureDevice(std::string serialNumber, OrbbecModelConfig const& modelCon

std::unique_ptr<ViamOBDevice>& my_dev = search->second;

// Enable global timestamp if supported
if (my_dev->device->isGlobalTimestampSupported()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that GlobalTimstamps are supported on the orbecc module we care about, can we remove the system backup?

my_dev->device->enableGlobalTimestamp(true);
VIAM_SDK_LOG(info) << "[configureDevice] Global timestamp enabled for device " << serialNumber;
} else {
VIAM_SDK_LOG(info) << "[configureDevice] Global timestamp not supported for device " << serialNumber;
}

// Initialize fields if not already set
if (!my_dev->pipe) {
my_dev->pipe = std::make_shared<ob::Pipeline>(my_dev->device);
Expand Down Expand Up @@ -1341,8 +1361,8 @@ vsdk::Camera::image_collection Orbbec::get_images(std::vector<std::string> filte
return response;
}

uint64_t colorTS = color ? color->getSystemTimeStampUs() : 0;
uint64_t depthTS = depth ? depth->getSystemTimeStampUs() : 0;
uint64_t colorTS = color ? getBestTimestampUs(color) : 0;
uint64_t depthTS = depth ? getBestTimestampUs(depth) : 0;
uint64_t timestamp = 0;

if (colorTS > 0 && depthTS > 0) {
Expand Down