Skip to content

Commit d1b0d5c

Browse files
committed
use a mapping of rtp timestamp to user timestamp on subscriber side too
1 parent a742917 commit d1b0d5c

6 files changed

Lines changed: 55 additions & 40 deletions

File tree

libwebrtc/src/native/user_timestamp.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,14 @@ impl UserTimestampHandler {
149149
}
150150
}
151151

152-
/// Pop the next received user timestamp from the receive queue.
153-
/// Returns None if the queue is empty.
152+
/// Lookup the user timestamp for a given RTP timestamp (receiver side).
153+
/// Returns None if no timestamp was found for this RTP timestamp.
154+
/// The entry is removed from the map after a successful lookup.
154155
///
155-
/// Each decoded frame should call this once to get its matching
156-
/// timestamp, maintaining 1:1 correspondence between received
157-
/// encoded frames and decoded video frames.
158-
pub fn pop_user_timestamp(&self) -> Option<i64> {
159-
let ts = self.sys_handle.pop_user_timestamp();
156+
/// Use the RTP timestamp from the decoded video frame to correlate
157+
/// it with the user timestamp that was embedded in the encoded frame.
158+
pub fn lookup_user_timestamp(&self, rtp_timestamp: u32) -> Option<i64> {
159+
let ts = self.sys_handle.lookup_user_timestamp(rtp_timestamp);
160160
if ts >= 0 {
161161
Some(ts)
162162
} else {
@@ -190,7 +190,9 @@ pub fn create_sender_handler(
190190
/// Create a receiver-side user timestamp handler.
191191
///
192192
/// This handler will extract user timestamps from received frames
193-
/// and make them available via `last_user_timestamp()`.
193+
/// and store them in a map keyed by RTP timestamp. Use
194+
/// `lookup_user_timestamp(rtp_timestamp)` to retrieve the user
195+
/// timestamp for a specific decoded frame.
194196
pub fn create_receiver_handler(
195197
peer_factory: &PeerConnectionFactory,
196198
store: &UserTimestampStore,

libwebrtc/src/native/video_stream.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,12 @@ struct VideoTrackObserver {
9696

9797
impl sys_vt::VideoSink for VideoTrackObserver {
9898
fn on_frame(&self, frame: UniquePtr<webrtc_sys::video_frame::ffi::VideoFrame>) {
99+
let rtp_timestamp = frame.timestamp();
99100
let user_timestamp_us = self
100101
.user_timestamp_handler
101102
.lock()
102103
.as_ref()
103-
.and_then(|h| h.pop_user_timestamp());
104+
.and_then(|h| h.lookup_user_timestamp(rtp_timestamp));
104105

105106
let _ = self.frame_tx.send(VideoFrame {
106107
rotation: frame.rotation().into(),

libwebrtc/src/video_frame.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,6 @@ where
6060
pub rotation: VideoRotation,
6161
pub timestamp_us: i64, // When the frame was captured in microseconds
6262
/// Optional user timestamp in microseconds, if available.
63-
/// This is typically a hardware or device timestamp supplied by the
64-
/// application that can be propagated end-to-end through the media
65-
/// pipeline.
6663
pub user_timestamp_us: Option<i64>,
6764
pub buffer: T,
6865
}

webrtc-sys/include/livekit/user_timestamp.h

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,10 @@ class UserTimestampTransformer : public webrtc::FrameTransformerInterface {
110110
/// Get the last received user timestamp (receiver side only)
111111
std::optional<int64_t> last_user_timestamp() const;
112112

113-
/// Pop the next received user timestamp from the receive queue.
114-
/// Returns the user timestamp if available, nullopt otherwise.
115-
/// Each decoded frame should call this once to get its matching timestamp.
116-
std::optional<int64_t> pop_user_timestamp();
113+
/// Lookup the user timestamp associated with a given RTP timestamp.
114+
/// Returns the user timestamp if found, nullopt otherwise.
115+
/// The entry is removed from the map after lookup.
116+
std::optional<int64_t> lookup_user_timestamp(uint32_t rtp_timestamp);
117117

118118
private:
119119
void TransformSend(
@@ -148,11 +148,14 @@ class UserTimestampTransformer : public webrtc::FrameTransformerInterface {
148148
mutable webrtc::Mutex send_cache_mutex_;
149149
mutable int64_t last_sent_user_timestamp_{0};
150150

151-
// Receive-side FIFO queue: one entry per received encoded frame, popped
152-
// one-to-one as decoded frames are delivered to the video sink.
153-
mutable webrtc::Mutex recv_queue_mutex_;
154-
mutable std::deque<int64_t> recv_queue_;
155-
static constexpr size_t kMaxRecvQueueEntries = 300;
151+
// Receive-side map: RTP timestamp -> user timestamp.
152+
// Keyed by RTP timestamp so decoded frames can look up their user
153+
// timestamp regardless of frame drops or reordering.
154+
mutable webrtc::Mutex recv_map_mutex_;
155+
mutable std::unordered_map<uint32_t, int64_t> recv_map_;
156+
// Track insertion order for pruning old entries.
157+
mutable std::deque<uint32_t> recv_map_order_;
158+
static constexpr size_t kMaxRecvMapEntries = 300;
156159
};
157160

158161
/// Wrapper class for Rust FFI that manages user timestamp transformers.
@@ -178,9 +181,9 @@ class UserTimestampHandler {
178181
/// Returns -1 if no timestamp has been received yet
179182
int64_t last_user_timestamp() const;
180183

181-
/// Pop the next received user timestamp from the receive queue.
182-
/// Returns -1 if the queue is empty.
183-
int64_t pop_user_timestamp() const;
184+
/// Lookup the user timestamp for a given RTP timestamp (receiver side).
185+
/// Returns -1 if not found.
186+
int64_t lookup_user_timestamp(uint32_t rtp_timestamp) const;
184187

185188
/// Check if a user timestamp has been received
186189
bool has_user_timestamp() const;

webrtc-sys/src/user_timestamp.cpp

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,17 @@ void UserTimestampTransformer::TransformReceive(
253253
last_user_timestamp_.store(user_ts.value());
254254
has_last_user_timestamp_.store(true);
255255

256-
// Also push to the receive queue so decoded frames can pop 1:1
256+
// Store in the receive map keyed by RTP timestamp so decoded frames
257+
// can look up their user timestamp regardless of frame drops.
257258
{
258-
webrtc::MutexLock lock(&recv_queue_mutex_);
259-
if (recv_queue_.size() >= kMaxRecvQueueEntries) {
260-
recv_queue_.pop_front();
259+
webrtc::MutexLock lock(&recv_map_mutex_);
260+
// Evict oldest entry if at capacity
261+
while (recv_map_.size() >= kMaxRecvMapEntries && !recv_map_order_.empty()) {
262+
recv_map_.erase(recv_map_order_.front());
263+
recv_map_order_.pop_front();
261264
}
262-
recv_queue_.push_back(user_ts.value());
265+
recv_map_[rtp_timestamp] = user_ts.value();
266+
recv_map_order_.push_back(rtp_timestamp);
263267
}
264268

265269
// Update frame with stripped data
@@ -416,13 +420,22 @@ std::optional<int64_t> UserTimestampTransformer::last_user_timestamp()
416420
return last_user_timestamp_.load();
417421
}
418422

419-
std::optional<int64_t> UserTimestampTransformer::pop_user_timestamp() {
420-
webrtc::MutexLock lock(&recv_queue_mutex_);
421-
if (recv_queue_.empty()) {
423+
std::optional<int64_t> UserTimestampTransformer::lookup_user_timestamp(
424+
uint32_t rtp_timestamp) {
425+
webrtc::MutexLock lock(&recv_map_mutex_);
426+
auto it = recv_map_.find(rtp_timestamp);
427+
if (it == recv_map_.end()) {
422428
return std::nullopt;
423429
}
424-
int64_t ts = recv_queue_.front();
425-
recv_queue_.pop_front();
430+
int64_t ts = it->second;
431+
recv_map_.erase(it);
432+
// Remove from insertion-order tracker (linear scan is fine for bounded size)
433+
for (auto oit = recv_map_order_.begin(); oit != recv_map_order_.end(); ++oit) {
434+
if (*oit == rtp_timestamp) {
435+
recv_map_order_.erase(oit);
436+
break;
437+
}
438+
}
426439
return ts;
427440
}
428441

@@ -461,8 +474,8 @@ int64_t UserTimestampHandler::last_user_timestamp() const {
461474
return ts.value_or(-1);
462475
}
463476

464-
int64_t UserTimestampHandler::pop_user_timestamp() const {
465-
auto ts = transformer_->pop_user_timestamp();
477+
int64_t UserTimestampHandler::lookup_user_timestamp(uint32_t rtp_timestamp) const {
478+
auto ts = transformer_->lookup_user_timestamp(rtp_timestamp);
466479
return ts.value_or(-1);
467480
}
468481

webrtc-sys/src/user_timestamp.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,9 @@ pub mod ffi {
6767
/// Returns -1 if no timestamp has been received yet.
6868
fn last_user_timestamp(self: &UserTimestampHandler) -> i64;
6969

70-
/// Pop the next received user timestamp from the receive queue.
71-
/// Returns -1 if the queue is empty.
72-
/// Each decoded frame should call this once to get its matching timestamp.
73-
fn pop_user_timestamp(self: &UserTimestampHandler) -> i64;
70+
/// Lookup the user timestamp for a given RTP timestamp (receiver side).
71+
/// Returns -1 if not found. The entry is removed after lookup.
72+
fn lookup_user_timestamp(self: &UserTimestampHandler, rtp_timestamp: u32) -> i64;
7473

7574
/// Check if a user timestamp has been received.
7675
fn has_user_timestamp(self: &UserTimestampHandler) -> bool;

0 commit comments

Comments
 (0)