Skip to main content

rtc_media/io/sample_builder/
mod.rs

1#[cfg(test)]
2mod sample_builder_test;
3#[cfg(test)]
4mod sample_sequence_location_test;
5
6/// Tracks where a sample sits within the RTP sequence-number space.
7pub mod sample_sequence_location;
8
9use self::sample_sequence_location::{Comparison, SampleSequenceLocation};
10use crate::Sample;
11use bytes::Bytes;
12use rtp::Packet;
13use rtp::packetizer::Depacketizer;
14use shared::time::SystemInstant;
15use std::time::Duration;
16
17/// SampleBuilder buffers packets until media frames are complete.
18pub struct SampleBuilder<T: Depacketizer> {
19    /// how many packets to wait until we get a valid Sample
20    max_late: u16,
21    /// max timestamp between old and new timestamps before dropping packets
22    max_late_timestamp: u32,
23    buffer: Vec<Option<Packet>>,
24    prepared_samples: Vec<Option<Sample>>,
25    last_sample_timestamp: Option<u32>,
26
27    /// Interface that allows us to take RTP packets to samples
28    depacketizer: T,
29
30    /// sample_rate allows us to compute duration of media.SamplecA
31    sample_rate: u32,
32
33    /// filled contains the head/tail of the packets inserted into the buffer
34    filled: SampleSequenceLocation,
35
36    /// active contains the active head/tail of the timestamp being actively processed
37    active: SampleSequenceLocation,
38
39    /// prepared contains the samples that have been processed to date
40    prepared: SampleSequenceLocation,
41
42    /// number of packets forced to be dropped
43    dropped_packets: u16,
44
45    /// number of padding packets detected and dropped. This number will be a subset of
46    /// `dropped_packets`
47    padding_packets: u16,
48}
49
50impl<T: Depacketizer> SampleBuilder<T> {
51    /// Constructs a new SampleBuilder.
52    /// `max_late` is how long to wait until we can construct a completed [`Sample`].
53    /// `max_late` is measured in RTP packet sequence numbers.
54    /// A large max_late will result in less packet loss but higher latency.
55    /// The depacketizer extracts media samples from RTP packets.
56    pub fn new(max_late: u16, depacketizer: T, sample_rate: u32) -> Self {
57        Self {
58            max_late,
59            max_late_timestamp: 0,
60            buffer: vec![None; u16::MAX as usize + 1],
61            prepared_samples: (0..=u16::MAX as usize).map(|_| None).collect(),
62            last_sample_timestamp: None,
63            depacketizer,
64            sample_rate,
65            filled: SampleSequenceLocation::new(),
66            active: SampleSequenceLocation::new(),
67            prepared: SampleSequenceLocation::new(),
68            dropped_packets: 0,
69            padding_packets: 0,
70        }
71    }
72
73    /// Sets how long to wait for a missing packet before giving up on the sample it belongs to.
74    ///
75    /// Bounds head-of-line blocking: without it a single lost packet would stall reassembly
76    /// indefinitely.
77    pub fn with_max_time_delay(mut self, max_late_duration: Duration) -> Self {
78        self.max_late_timestamp =
79            (self.sample_rate as u128 * max_late_duration.as_millis() / 1000) as u32;
80        self
81    }
82
83    fn too_old(&self, location: &SampleSequenceLocation) -> bool {
84        if self.max_late_timestamp == 0 {
85            return false;
86        }
87
88        let mut found_head: Option<u32> = None;
89        let mut found_tail: Option<u32> = None;
90
91        let mut i = location.head;
92        while i != location.tail {
93            if let Some(ref packet) = self.buffer[i as usize] {
94                found_head = Some(packet.header.timestamp);
95                break;
96            }
97            i = i.wrapping_add(1);
98        }
99
100        if found_head.is_none() {
101            return false;
102        }
103
104        let mut i = location.tail.wrapping_sub(1);
105        while i != location.head {
106            if let Some(ref packet) = self.buffer[i as usize] {
107                found_tail = Some(packet.header.timestamp);
108                break;
109            }
110            i = i.wrapping_sub(1);
111        }
112
113        if found_tail.is_none() {
114            return false;
115        }
116
117        found_tail.unwrap().wrapping_sub(found_head.unwrap()) > self.max_late_timestamp
118    }
119
120    /// Returns the timestamp associated with a given sample location
121    fn fetch_timestamp(&self, location: &SampleSequenceLocation) -> Option<u32> {
122        if location.empty() {
123            None
124        } else {
125            Some(
126                (self.buffer[location.head as usize])
127                    .as_ref()?
128                    .header
129                    .timestamp,
130            )
131        }
132    }
133
134    fn release_packet(&mut self, i: u16) {
135        self.buffer[i as usize] = None;
136    }
137
138    /// Clears all buffers that have already been consumed by
139    /// popping.
140    fn purge_consumed_buffers(&mut self) {
141        let active = self.active;
142        self.purge_consumed_location(&active, false);
143    }
144
145    /// Clears all buffers that have already been consumed
146    /// during a sample building method.
147    fn purge_consumed_location(&mut self, consume: &SampleSequenceLocation, force_consume: bool) {
148        if !self.filled.has_data() {
149            return;
150        }
151        match consume.compare(self.filled.head) {
152            Comparison::Inside if force_consume => {
153                self.release_packet(self.filled.head);
154                self.filled.head = self.filled.head.wrapping_add(1);
155            }
156            Comparison::Before => {
157                self.release_packet(self.filled.head);
158                self.filled.head = self.filled.head.wrapping_add(1);
159            }
160            _ => {}
161        }
162    }
163
164    /// Flushes all buffers that are already consumed or those buffers
165    /// that are too late to consume.
166    fn purge_buffers(&mut self) {
167        self.purge_consumed_buffers();
168
169        while (self.too_old(&self.filled) || (self.filled.count() > self.max_late))
170            && self.filled.has_data()
171        {
172            if self.active.empty() {
173                // refill the active based on the filled packets
174                self.active = self.filled;
175            }
176
177            if self.active.has_data() && (self.active.head == self.filled.head) {
178                // attempt to force the active packet to be consumed even though
179                // outstanding data may be pending arrival
180                let err = match self.build_sample(true) {
181                    Ok(_) => continue,
182                    Err(e) => e,
183                };
184
185                if !matches!(err, BuildError::InvalidPartition(_)) {
186                    // In the InvalidPartition case `build_sample` will have already adjusted `dropped_packets`.
187                    self.dropped_packets += 1;
188                }
189
190                // could not build the sample so drop it
191                self.active.head = self.active.head.wrapping_add(1);
192            }
193
194            self.release_packet(self.filled.head);
195            self.filled.head = self.filled.head.wrapping_add(1);
196        }
197    }
198
199    /// Adds an RTP Packet to self's buffer.
200    ///
201    /// Push does not copy the input. If you wish to reuse
202    /// this memory make sure to copy before calling push
203    pub fn push(&mut self, p: Packet) {
204        let sequence_number = p.header.sequence_number;
205        self.buffer[sequence_number as usize] = Some(p);
206        match self.filled.compare(sequence_number) {
207            Comparison::Void => {
208                self.filled.head = sequence_number;
209                self.filled.tail = sequence_number.wrapping_add(1);
210            }
211            Comparison::Before => {
212                self.filled.head = sequence_number;
213            }
214            Comparison::After => {
215                self.filled.tail = sequence_number.wrapping_add(1);
216            }
217            _ => {}
218        }
219        self.purge_buffers();
220    }
221
222    /// Creates a sample from a valid collection of RTP Packets by
223    /// walking forwards building a sample if everything looks good clear and
224    /// update buffer+values
225    fn build_sample(
226        &mut self,
227        purging_buffers: bool,
228    ) -> Result<SampleSequenceLocation, BuildError> {
229        if self.active.empty() {
230            self.active = self.filled;
231        }
232
233        if self.active.empty() {
234            return Err(BuildError::NoActiveSegment);
235        }
236
237        if self.filled.compare(self.active.tail) == Comparison::Inside {
238            self.active.tail = self.filled.tail;
239        }
240
241        let mut consume = SampleSequenceLocation::new();
242
243        let mut i = self.active.head;
244        // `self.active` isn't modified in the loop, fetch the timestamp once and cache it.
245        let head_timestamp = self.fetch_timestamp(&self.active);
246        while let Some(ref packet) = self.buffer[i as usize] {
247            if self.active.compare(i) == Comparison::After {
248                break;
249            }
250            let is_same_timestamp = head_timestamp.map(|t| packet.header.timestamp == t);
251            let is_different_timestamp = is_same_timestamp.map(std::ops::Not::not);
252            let is_partition_tail = self
253                .depacketizer
254                .is_partition_tail(packet.header.marker, &packet.payload);
255
256            // If the timestamp is not the same it might be because the next packet is both a start
257            // and end of the next partition in which case a sample should be generated now. This
258            // can happen when padding packets are used .e.g:
259            //
260            // p1(t=1), p2(t=1), p3(t=1), p4(t=2, marker=true, start=true)
261            //
262            // In thic case the generated sample should be p1 through p3, but excluding p4 which is
263            // its own sample.
264            if is_partition_tail && is_same_timestamp.unwrap_or(true) {
265                consume.head = self.active.head;
266                consume.tail = i.wrapping_add(1);
267                break;
268            }
269
270            if is_different_timestamp.unwrap_or(false) {
271                consume.head = self.active.head;
272                consume.tail = i;
273                break;
274            }
275            i = i.wrapping_add(1);
276        }
277
278        if consume.empty() {
279            return Err(BuildError::NothingToConsume);
280        }
281
282        if !purging_buffers && self.buffer[consume.tail as usize].is_none() {
283            // wait for the next packet after this set of packets to arrive
284            // to ensure at least one post sample timestamp is known
285            // (unless we have to release right now)
286            return Err(BuildError::PendingTimestampPacket);
287        }
288
289        let sample_timestamp = self.fetch_timestamp(&self.active).unwrap_or(0);
290        let mut after_timestamp = sample_timestamp;
291
292        // scan for any packet after the current and use that time stamp as the diff point
293        for i in consume.tail..self.active.tail {
294            if let Some(ref packet) = self.buffer[i as usize] {
295                after_timestamp = packet.header.timestamp;
296                break;
297            }
298        }
299
300        // prior to decoding all the packets, check if this packet
301        // would end being disposed anyway
302        let head_payload = self.buffer[consume.head as usize]
303            .as_ref()
304            .map(|p| &p.payload)
305            .ok_or(BuildError::GapInSegment)?;
306        if !self.depacketizer.is_partition_head(head_payload) {
307            // libWebRTC will sometimes send several empty padding packets to smooth out send
308            // rate. These packets don't carry any media payloads.
309            let is_padding = consume.range(&self.buffer).all(|p| {
310                p.map(|p| {
311                    self.last_sample_timestamp == Some(p.header.timestamp) && p.payload.is_empty()
312                })
313                .unwrap_or(false)
314            });
315
316            self.dropped_packets += consume.count();
317            if is_padding {
318                self.padding_packets += consume.count();
319            }
320            self.purge_consumed_location(&consume, true);
321            self.purge_consumed_buffers();
322
323            self.active.head = consume.tail;
324            return Err(BuildError::InvalidPartition(consume));
325        }
326
327        // the head set of packets is now fully consumed
328        self.active.head = consume.tail;
329
330        // Assemble the sample payload from the consumed packets. For the common
331        // single-packet sample (all audio, and any frame that fits one RTP
332        // packet) hand back the depacketized `Bytes` directly — it is
333        // refcounted, so no copy is needed. Only multi-packet samples require
334        // concatenation, and even then `Bytes::from(data)` reuses the `Vec`'s
335        // buffer instead of copying it a second time.
336        let sample_data: Bytes = if consume.count() == 1 {
337            let payload = self.buffer[consume.head as usize]
338                .as_ref()
339                .map(|p| &p.payload)
340                .ok_or(BuildError::GapInSegment)?;
341            self.depacketizer
342                .depacketize(payload)
343                .map_err(|_| BuildError::DepacketizerFailed)?
344        } else {
345            let mut data: Vec<u8> = Vec::new();
346            let mut i = consume.head;
347            while i != consume.tail {
348                let payload = self.buffer[i as usize]
349                    .as_ref()
350                    .map(|p| &p.payload)
351                    .ok_or(BuildError::GapInSegment)?;
352
353                let p = self
354                    .depacketizer
355                    .depacketize(payload)
356                    .map_err(|_| BuildError::DepacketizerFailed)?;
357
358                data.extend_from_slice(&p);
359                i = i.wrapping_add(1);
360            }
361            Bytes::from(data)
362        };
363        let samples = after_timestamp.wrapping_sub(sample_timestamp);
364
365        let sample = Sample {
366            data: sample_data,
367            timestamp: SystemInstant::now(),
368            duration: Duration::from_secs_f64((samples as f64) / (self.sample_rate as f64)),
369            packet_timestamp: sample_timestamp,
370            prev_dropped_packets: self.dropped_packets,
371            prev_padding_packets: self.padding_packets,
372        };
373
374        self.dropped_packets = 0;
375        self.padding_packets = 0;
376        self.last_sample_timestamp = Some(sample_timestamp);
377
378        self.prepared_samples[self.prepared.tail as usize] = Some(sample);
379        self.prepared.tail = self.prepared.tail.wrapping_add(1);
380
381        self.purge_consumed_location(&consume, true);
382        self.purge_consumed_buffers();
383
384        Ok(consume)
385    }
386
387    /// Compiles pushed RTP packets into media samples and then
388    /// returns the next valid sample (or None if no sample is compiled).
389    pub fn pop(&mut self) -> Option<Sample> {
390        let _ = self.build_sample(false);
391
392        if self.prepared.empty() {
393            return None;
394        }
395        let result = self.prepared_samples[self.prepared.head as usize].take();
396        self.prepared.head = self.prepared.head.wrapping_add(1);
397        result
398    }
399
400    /// Compiles pushed RTP packets into media samples and then
401    /// returns the next valid sample with its associated RTP timestamp (or `None` if
402    /// no sample is compiled).
403    pub fn pop_with_timestamp(&mut self) -> Option<(Sample, u32)> {
404        if let Some(sample) = self.pop() {
405            let timestamp = sample.packet_timestamp;
406            Some((sample, timestamp))
407        } else {
408            None
409        }
410    }
411}
412
413// Computes the distance between two sequence numbers
414/*pub(crate) fn seqnum_distance(head: u16, tail: u16) -> u16 {
415    if head > tail {
416        head.wrapping_add(tail)
417    } else {
418        tail - head
419    }
420}*/
421
422pub(crate) fn seqnum_distance(x: u16, y: u16) -> u16 {
423    let diff = x.wrapping_sub(y);
424    if diff > 0xFFFF / 2 {
425        0xFFFF - diff + 1
426    } else {
427        diff
428    }
429}
430
431#[derive(Debug)]
432enum BuildError {
433    /// There's no active segment of RTP packets to consider yet.
434    NoActiveSegment,
435
436    /// No sample partition could be found in the active segment.
437    NothingToConsume,
438
439    /// A segment to consume was identified, but a subsequent packet is needed to determine the
440    /// duration of the sample.
441    PendingTimestampPacket,
442
443    /// The active segment's head was not aligned with a sample partition head. Some packets were
444    /// dropped.
445    InvalidPartition(SampleSequenceLocation),
446
447    /// There was a gap in the active segment because of one or more missing RTP packets.
448    GapInSegment,
449
450    /// We failed to depacketize an RTP packet.
451    DepacketizerFailed,
452}