Skip to content

Commit

Permalink
ndisrc: Keep track of audio/video and timestamp/timecode observations…
Browse files Browse the repository at this point in the history
… separately

Audio/video are in practice not always from the same clock and can have
different behaviours with regards to clock rate and jitter. Handling
them separately generally gives better results for the timestamps output
by the source element.
  • Loading branch information
sdroege committed Oct 12, 2022
1 parent b82acb9 commit a3c7528
Showing 1 changed file with 32 additions and 24 deletions.
56 changes: 32 additions & 24 deletions net/ndi/src/receiver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,9 @@ pub struct ReceiverInner {
queue: ReceiverQueue,
max_queue_length: usize,

observations: Observations,
// Audio/video time observations
observations_timestamp: [Observations; 2],
observations_timecode: [Observations; 2],

element: glib::WeakRef<gst_base::BaseSrc>,
timestamp_mode: TimestampMode,
Expand Down Expand Up @@ -227,6 +229,7 @@ struct ReceiverQueueInner {
const WINDOW_LENGTH: u64 = 512;
const WINDOW_DURATION: u64 = 2_000_000_000;

#[derive(Default)]
struct Observations(AtomicRefCell<ObservationsInner>);

struct ObservationsInner {
Expand Down Expand Up @@ -254,26 +257,21 @@ impl Default for ObservationsInner {
}

impl Observations {
fn new() -> Self {
Self(AtomicRefCell::new(ObservationsInner::default()))
}

// Based on the algorithm used in GStreamer's rtpjitterbuffer, which comes from
// Fober, Orlarey and Letz, 2005, "Real Time Clock Skew Estimation over Network Delays":
// http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.102.1546
fn process(
&self,
element: &gst_base::BaseSrc,
time: (Option<gst::ClockTime>, gst::ClockTime),
remote_time: Option<gst::ClockTime>,
local_time: gst::ClockTime,
duration: Option<gst::ClockTime>,
) -> (gst::ClockTime, Option<gst::ClockTime>, bool) {
if time.0.is_none() {
return (time.1, duration, false);
}

let time = (time.0.unwrap(), time.1);
let remote_time = time.0.nseconds();
let local_time = time.1.nseconds();
let remote_time = match remote_time {
None => return (local_time, duration, false),
Some(remote_time) => remote_time.nseconds(),
};
let local_time = local_time.nseconds();

gst_trace!(
CAT,
Expand Down Expand Up @@ -488,7 +486,8 @@ impl Receiver {
Condvar::new(),
))),
max_queue_length,
observations: Observations::new(),
observations_timestamp: Default::default(),
observations_timecode: Default::default(),
element: element.downgrade(),
timestamp_mode,
timeout,
Expand Down Expand Up @@ -780,6 +779,7 @@ impl Receiver {
fn calculate_timestamp(
&self,
element: &gst_base::BaseSrc,
is_audio: bool,
timestamp: i64,
timecode: i64,
duration: Option<gst::ClockTime>,
Expand All @@ -805,17 +805,23 @@ impl Receiver {
real_time_now,
);

let res_timestamp = self.0.observations_timestamp[if is_audio { 0 } else { 1 }].process(
element,
timestamp,
receive_time,
duration,
);

let res_timecode = self.0.observations_timecode[if is_audio { 0 } else { 1 }].process(
element,
Some(timecode),
receive_time,
duration,
);

let (pts, duration, discont) = match self.0.timestamp_mode {
TimestampMode::ReceiveTimeTimecode => {
self.0
.observations
.process(element, (Some(timecode), receive_time), duration)
}
TimestampMode::ReceiveTimeTimestamp => {
self.0
.observations
.process(element, (timestamp, receive_time), duration)
}
TimestampMode::ReceiveTimeTimecode => res_timecode,
TimestampMode::ReceiveTimeTimestamp => res_timestamp,
TimestampMode::Timecode => (timecode, duration, false),
TimestampMode::Timestamp if timestamp.is_none() => (receive_time, duration, false),
TimestampMode::Timestamp => {
Expand Down Expand Up @@ -888,6 +894,7 @@ impl Receiver {

self.calculate_timestamp(
element,
false,
video_frame.timestamp(),
video_frame.timecode(),
duration,
Expand Down Expand Up @@ -1438,6 +1445,7 @@ impl Receiver {

self.calculate_timestamp(
element,
true,
audio_frame.timestamp(),
audio_frame.timecode(),
duration,
Expand Down

0 comments on commit a3c7528

Please sign in to comment.