Skip to main content

oxideav_mpegts/
packet.rs

1//! 188-byte MPEG-TS packet parser per ISO/IEC 13818-1 §2.4.3.
2//!
3//! Wire layout (Table 2-2):
4//!
5//! ```text
6//! byte 0       sync_byte (= 0x47)
7//! byte 1..3    transport_error_indicator (1) | payload_unit_start_indicator (1) |
8//!              transport_priority (1) | PID (13) |
9//!              transport_scrambling_control (2) | adaptation_field_control (2) |
10//!              continuity_counter (4)
11//! byte 4..     optional adaptation field + payload bytes
12//! ```
13//!
14//! Adaptation field layout (§2.4.3.4, Table 2-6):
15//!
16//! ```text
17//! adaptation_field_length (8)
18//! flags (8): discontinuity_indicator | random_access_indicator |
19//!            elementary_stream_priority_indicator | PCR_flag | OPCR_flag |
20//!            splicing_point_flag | transport_private_data_flag |
21//!            adaptation_field_extension_flag
22//! optional PCR  (48 bits = 33-bit base + 6 reserved + 9-bit extension)
23//! optional OPCR (48 bits, coded like PCR)
24//! optional splice_countdown (8, tcimsbf)
25//! optional transport_private_data: length (8) + N data bytes
26//! optional adaptation_field_extension:
27//!     length (8)
28//!     ltw_flag (1) | piecewise_rate_flag (1) | seamless_splice_flag (1) | reserved (5)
29//!     if ltw_flag: ltw_valid_flag (1) | ltw_offset (15)
30//!     if piecewise_rate_flag: reserved (2) | piecewise_rate (22)
31//!     if seamless_splice_flag: splice_type (4) | DTS_next_AU (3 × 15-bit pieces
32//!         interleaved with marker bits, 5 bytes total)
33//! stuffing bytes (0xFF)
34//! ```
35
36use crate::TsError;
37
38/// Fixed size of a transport-stream packet (188 bytes).
39pub const TS_PACKET_LEN: usize = 188;
40
41/// Spec-defined sync byte at offset 0 of every TS packet.
42pub const TS_SYNC_BYTE: u8 = 0x47;
43
44/// Parsed adaptation field accompanying a TS packet.
45#[derive(Debug, Clone, Copy)]
46pub struct AdaptationField<'a> {
47    /// Value of the `adaptation_field_length` byte. Counts the bytes
48    /// that follow it within the adaptation field (so the AF occupies
49    /// `length + 1` bytes of the packet payload area).
50    pub length: u8,
51    /// `discontinuity_indicator` flag.
52    pub discontinuity_indicator: bool,
53    /// `random_access_indicator` flag.
54    pub random_access_indicator: bool,
55    /// `elementary_stream_priority_indicator` flag.
56    pub elementary_stream_priority_indicator: bool,
57    /// `PCR_flag` — when set, [`Self::pcr_base`] and
58    /// [`Self::pcr_extension`] are populated.
59    pub pcr_flag: bool,
60    /// `OPCR_flag`.
61    pub opcr_flag: bool,
62    /// `splicing_point_flag`.
63    pub splicing_point_flag: bool,
64    /// `transport_private_data_flag`.
65    pub transport_private_data_flag: bool,
66    /// `adaptation_field_extension_flag`.
67    pub adaptation_field_extension_flag: bool,
68    /// 33-bit `program_clock_reference_base`, when [`Self::pcr_flag`].
69    pub pcr_base: Option<u64>,
70    /// 9-bit `program_clock_reference_extension`, when
71    /// [`Self::pcr_flag`].
72    pub pcr_extension: Option<u16>,
73    /// 33-bit `original_program_clock_reference_base`, when
74    /// [`Self::opcr_flag`].
75    pub opcr_base: Option<u64>,
76    /// 9-bit `original_program_clock_reference_extension`, when
77    /// [`Self::opcr_flag`].
78    pub opcr_extension: Option<u16>,
79    /// Signed 8-bit `splice_countdown` (§2.4.3.5), present when
80    /// [`Self::splicing_point_flag`].
81    pub splice_countdown: Option<i8>,
82    /// `private_data_byte` slice, when
83    /// [`Self::transport_private_data_flag`]. Length equals the
84    /// `transport_private_data_length` byte on the wire.
85    pub transport_private_data: Option<&'a [u8]>,
86    /// Decoded adaptation field extension body, when
87    /// [`Self::adaptation_field_extension_flag`].
88    pub adaptation_field_extension: Option<AdaptationFieldExtension>,
89    /// Raw adaptation-field bytes including the
90    /// `adaptation_field_length` byte itself.
91    pub raw: &'a [u8],
92}
93
94/// Parsed `adaptation_field_extension` body (§2.4.3.5, Table 2-6).
95#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
96pub struct AdaptationFieldExtension {
97    /// Value of the `adaptation_field_extension_length` byte (count of
98    /// bytes after it, including reserved padding).
99    pub length: u8,
100    /// `ltw_flag`.
101    pub ltw_flag: bool,
102    /// `piecewise_rate_flag`.
103    pub piecewise_rate_flag: bool,
104    /// `seamless_splice_flag`.
105    pub seamless_splice_flag: bool,
106    /// `ltw_valid_flag`, when [`Self::ltw_flag`].
107    pub ltw_valid_flag: Option<bool>,
108    /// 15-bit `ltw_offset`, when [`Self::ltw_flag`]. Meaningful only if
109    /// `ltw_valid_flag == Some(true)` per §2.4.3.5.
110    pub ltw_offset: Option<u16>,
111    /// 22-bit `piecewise_rate` (units of 50 bytes/s · 8 bits/byte ÷ R
112    /// per §2.4.3.5), when [`Self::piecewise_rate_flag`].
113    pub piecewise_rate: Option<u32>,
114    /// 4-bit `splice_type` (§2.4.3.5 / Tables 2-7..2-16), when
115    /// [`Self::seamless_splice_flag`].
116    pub splice_type: Option<u8>,
117    /// 33-bit `DTS_next_AU` (90 kHz units), when
118    /// [`Self::seamless_splice_flag`].
119    pub dts_next_au: Option<u64>,
120}
121
122/// A parsed 188-byte TS packet.
123#[derive(Debug, Clone, Copy)]
124pub struct TsPacket<'a> {
125    /// 13-bit PID identifying the packet's elementary stream / PSI
126    /// section.
127    pub pid: u16,
128    /// `payload_unit_start_indicator` — first byte of payload is the
129    /// start of a new PES packet or a PSI section's `pointer_field`.
130    pub payload_unit_start: bool,
131    /// `transport_error_indicator` — at least one uncorrectable bit
132    /// error exists in the packet.
133    pub transport_error: bool,
134    /// `transport_priority` — payload has higher priority than other
135    /// packets with the same PID.
136    pub transport_priority: bool,
137    /// 2-bit `transport_scrambling_control` field.
138    pub transport_scrambling_control: u8,
139    /// 4-bit `continuity_counter`.
140    pub continuity_counter: u8,
141    /// Optional parsed adaptation field. `Some` exactly when
142    /// `adaptation_field_control & 0b10 != 0`.
143    pub adaptation_field: Option<AdaptationField<'a>>,
144    /// Payload slice — bytes after the header and optional adaptation
145    /// field. Empty when `adaptation_field_control & 0b01 == 0`.
146    pub payload: &'a [u8],
147    /// Raw 188-byte packet, including header.
148    pub bytes: &'a [u8],
149}
150
151impl<'a> TsPacket<'a> {
152    /// Parse a 188-byte transport-stream packet.
153    ///
154    /// Returns [`TsError::Truncated`] if `bytes.len() != 188` and
155    /// [`TsError::BadSyncByte`] if byte 0 is not `0x47`.
156    pub fn parse(bytes: &'a [u8]) -> Result<Self, TsError> {
157        if bytes.len() < TS_PACKET_LEN {
158            return Err(TsError::Truncated {
159                what: "TS packet",
160                have: bytes.len(),
161                need: TS_PACKET_LEN,
162            });
163        }
164        let bytes = &bytes[..TS_PACKET_LEN];
165        if bytes[0] != TS_SYNC_BYTE {
166            return Err(TsError::BadSyncByte(bytes[0]));
167        }
168
169        let b1 = bytes[1];
170        let b2 = bytes[2];
171        let b3 = bytes[3];
172
173        let transport_error = (b1 & 0b1000_0000) != 0;
174        let payload_unit_start = (b1 & 0b0100_0000) != 0;
175        let transport_priority = (b1 & 0b0010_0000) != 0;
176        let pid = (((b1 & 0b0001_1111) as u16) << 8) | (b2 as u16);
177        let transport_scrambling_control = (b3 >> 6) & 0b11;
178        let adaptation_field_control = (b3 >> 4) & 0b11;
179        let continuity_counter = b3 & 0b1111;
180
181        let has_af = (adaptation_field_control & 0b10) != 0;
182        let has_payload = (adaptation_field_control & 0b01) != 0;
183
184        // Cursor into the packet, starting just after the 4-byte
185        // header.
186        let mut cursor: usize = 4;
187        let mut adaptation_field = None;
188
189        if has_af {
190            // AF occupies `af_len + 1` bytes (the length byte itself
191            // plus `af_len` payload bytes). Cap at the remaining
192            // packet size — a malformed AF length is treated as
193            // covering the rest of the packet.
194            let af_len = bytes[cursor] as usize;
195            let af_total = af_len + 1;
196            let af_end = cursor.checked_add(af_total).ok_or(TsError::Truncated {
197                what: "TS adaptation_field",
198                have: bytes.len() - cursor,
199                need: af_total,
200            })?;
201            if af_end > TS_PACKET_LEN {
202                return Err(TsError::Truncated {
203                    what: "TS adaptation_field",
204                    have: TS_PACKET_LEN - cursor,
205                    need: af_total,
206                });
207            }
208
209            let af_raw = &bytes[cursor..af_end];
210            adaptation_field = Some(parse_adaptation_field(af_raw)?);
211            cursor = af_end;
212        }
213
214        let payload = if has_payload {
215            &bytes[cursor..]
216        } else {
217            &[][..]
218        };
219
220        Ok(Self {
221            pid,
222            payload_unit_start,
223            transport_error,
224            transport_priority,
225            transport_scrambling_control,
226            continuity_counter,
227            adaptation_field,
228            payload,
229            bytes,
230        })
231    }
232}
233
234fn parse_adaptation_field(raw: &[u8]) -> Result<AdaptationField<'_>, TsError> {
235    // raw[0] is adaptation_field_length; raw.len() == length + 1.
236    let length = raw[0];
237    if length == 0 {
238        return Ok(AdaptationField {
239            length: 0,
240            discontinuity_indicator: false,
241            random_access_indicator: false,
242            elementary_stream_priority_indicator: false,
243            pcr_flag: false,
244            opcr_flag: false,
245            splicing_point_flag: false,
246            transport_private_data_flag: false,
247            adaptation_field_extension_flag: false,
248            pcr_base: None,
249            pcr_extension: None,
250            opcr_base: None,
251            opcr_extension: None,
252            splice_countdown: None,
253            transport_private_data: None,
254            adaptation_field_extension: None,
255            raw,
256        });
257    }
258    if raw.len() < 2 {
259        return Err(TsError::Truncated {
260            what: "TS adaptation_field flags",
261            have: raw.len(),
262            need: 2,
263        });
264    }
265    let flags = raw[1];
266    let discontinuity_indicator = (flags & 0b1000_0000) != 0;
267    let random_access_indicator = (flags & 0b0100_0000) != 0;
268    let elementary_stream_priority_indicator = (flags & 0b0010_0000) != 0;
269    let pcr_flag = (flags & 0b0001_0000) != 0;
270    let opcr_flag = (flags & 0b0000_1000) != 0;
271    let splicing_point_flag = (flags & 0b0000_0100) != 0;
272    let transport_private_data_flag = (flags & 0b0000_0010) != 0;
273    let adaptation_field_extension_flag = (flags & 0b0000_0001) != 0;
274
275    let mut cursor = 2usize;
276
277    // PCR (§2.4.3.5).
278    let (pcr_base, pcr_extension) = if pcr_flag {
279        let (b, e) = parse_clock_reference(raw, cursor, "TS adaptation_field PCR")?;
280        cursor += 6;
281        (Some(b), Some(e))
282    } else {
283        (None, None)
284    };
285
286    // OPCR (§2.4.3.5) — same wire shape as PCR.
287    let (opcr_base, opcr_extension) = if opcr_flag {
288        let (b, e) = parse_clock_reference(raw, cursor, "TS adaptation_field OPCR")?;
289        cursor += 6;
290        (Some(b), Some(e))
291    } else {
292        (None, None)
293    };
294
295    // splice_countdown — single signed byte (§2.4.3.5).
296    let splice_countdown = if splicing_point_flag {
297        if cursor + 1 > raw.len() {
298            return Err(TsError::Truncated {
299                what: "TS adaptation_field splice_countdown",
300                have: raw.len() - cursor,
301                need: 1,
302            });
303        }
304        let v = raw[cursor] as i8;
305        cursor += 1;
306        Some(v)
307    } else {
308        None
309    };
310
311    // transport_private_data — 8-bit length + N bytes (§2.4.3.5).
312    let transport_private_data = if transport_private_data_flag {
313        if cursor + 1 > raw.len() {
314            return Err(TsError::Truncated {
315                what: "TS adaptation_field transport_private_data_length",
316                have: raw.len() - cursor,
317                need: 1,
318            });
319        }
320        let n = raw[cursor] as usize;
321        cursor += 1;
322        let end = cursor.checked_add(n).ok_or(TsError::Truncated {
323            what: "TS adaptation_field private_data",
324            have: raw.len() - cursor,
325            need: n,
326        })?;
327        if end > raw.len() {
328            return Err(TsError::Truncated {
329                what: "TS adaptation_field private_data",
330                have: raw.len() - cursor,
331                need: n,
332            });
333        }
334        let slice = &raw[cursor..end];
335        cursor = end;
336        Some(slice)
337    } else {
338        None
339    };
340
341    // adaptation_field_extension — variable length, with optional ltw /
342    // piecewise_rate / seamless_splice sub-fields (§2.4.3.5).
343    let adaptation_field_extension = if adaptation_field_extension_flag {
344        Some(parse_extension(raw, &mut cursor)?)
345    } else {
346        None
347    };
348
349    // Trailing bytes (up to raw.len()) are stuffing_byte (0xFF) per
350    // Table 2-6 — silently accepted.
351    let _ = cursor;
352
353    Ok(AdaptationField {
354        length,
355        discontinuity_indicator,
356        random_access_indicator,
357        elementary_stream_priority_indicator,
358        pcr_flag,
359        opcr_flag,
360        splicing_point_flag,
361        transport_private_data_flag,
362        adaptation_field_extension_flag,
363        pcr_base,
364        pcr_extension,
365        opcr_base,
366        opcr_extension,
367        splice_countdown,
368        transport_private_data,
369        adaptation_field_extension,
370        raw,
371    })
372}
373
374/// Decode a 6-byte program_clock_reference (33-bit base + 6 reserved +
375/// 9-bit extension) at `raw[cursor..]`.
376fn parse_clock_reference(
377    raw: &[u8],
378    cursor: usize,
379    what: &'static str,
380) -> Result<(u64, u16), TsError> {
381    if cursor + 6 > raw.len() {
382        return Err(TsError::Truncated {
383            what,
384            have: raw.len() - cursor,
385            need: 6,
386        });
387    }
388    let p = &raw[cursor..cursor + 6];
389    let base: u64 = ((p[0] as u64) << 25)
390        | ((p[1] as u64) << 17)
391        | ((p[2] as u64) << 9)
392        | ((p[3] as u64) << 1)
393        | (((p[4] >> 7) & 0b1) as u64);
394    let ext: u16 = (((p[4] & 0b0000_0001) as u16) << 8) | (p[5] as u16);
395    Ok((base, ext))
396}
397
398fn parse_extension(raw: &[u8], cursor: &mut usize) -> Result<AdaptationFieldExtension, TsError> {
399    if *cursor + 1 > raw.len() {
400        return Err(TsError::Truncated {
401            what: "TS adaptation_field_extension_length",
402            have: raw.len() - *cursor,
403            need: 1,
404        });
405    }
406    let ext_len = raw[*cursor] as usize;
407    *cursor += 1;
408    let ext_end = cursor.checked_add(ext_len).ok_or(TsError::Truncated {
409        what: "TS adaptation_field_extension body",
410        have: raw.len() - *cursor,
411        need: ext_len,
412    })?;
413    if ext_end > raw.len() {
414        return Err(TsError::Truncated {
415            what: "TS adaptation_field_extension body",
416            have: raw.len() - *cursor,
417            need: ext_len,
418        });
419    }
420    // The flag byte counts toward ext_len; need at least 1.
421    if ext_len < 1 {
422        // Spec-permitted zero-length extension: just yield the header
423        // sentinel; everything below stays defaulted.
424        return Ok(AdaptationFieldExtension {
425            length: 0,
426            ..Default::default()
427        });
428    }
429    let flags = raw[*cursor];
430    *cursor += 1;
431    let body_end = ext_end; // hard ceiling for the sub-fields below.
432    let ltw_flag = (flags & 0b1000_0000) != 0;
433    let piecewise_rate_flag = (flags & 0b0100_0000) != 0;
434    let seamless_splice_flag = (flags & 0b0010_0000) != 0;
435
436    let (ltw_valid_flag, ltw_offset) = if ltw_flag {
437        if *cursor + 2 > body_end {
438            return Err(TsError::Truncated {
439                what: "TS adaptation_field ltw",
440                have: body_end - *cursor,
441                need: 2,
442            });
443        }
444        let b0 = raw[*cursor];
445        let b1 = raw[*cursor + 1];
446        *cursor += 2;
447        let valid = (b0 & 0b1000_0000) != 0;
448        let offset = (((b0 & 0b0111_1111) as u16) << 8) | (b1 as u16);
449        (Some(valid), Some(offset))
450    } else {
451        (None, None)
452    };
453
454    let piecewise_rate = if piecewise_rate_flag {
455        if *cursor + 3 > body_end {
456            return Err(TsError::Truncated {
457                what: "TS adaptation_field piecewise_rate",
458                have: body_end - *cursor,
459                need: 3,
460            });
461        }
462        let p0 = raw[*cursor] & 0b0011_1111;
463        let p1 = raw[*cursor + 1];
464        let p2 = raw[*cursor + 2];
465        *cursor += 3;
466        Some(((p0 as u32) << 16) | ((p1 as u32) << 8) | (p2 as u32))
467    } else {
468        None
469    };
470
471    let (splice_type, dts_next_au) = if seamless_splice_flag {
472        if *cursor + 5 > body_end {
473            return Err(TsError::Truncated {
474                what: "TS adaptation_field seamless_splice",
475                have: body_end - *cursor,
476                need: 5,
477            });
478        }
479        // Layout: splice_type (4) | DTS[32..30] (3) | marker (1)
480        //         DTS[29..22] (8)
481        //         DTS[21..15] (7) | marker (1)
482        //         DTS[14..7]  (8)
483        //         DTS[6..0]   (7) | marker (1)
484        let b0 = raw[*cursor];
485        let b1 = raw[*cursor + 1];
486        let b2 = raw[*cursor + 2];
487        let b3 = raw[*cursor + 3];
488        let b4 = raw[*cursor + 4];
489        *cursor += 5;
490        let stype = (b0 >> 4) & 0b1111;
491        let dts: u64 = (((b0 >> 1) & 0b0000_0111) as u64) << 30
492            | (b1 as u64) << 22
493            | (((b2 >> 1) & 0b0111_1111) as u64) << 15
494            | (b3 as u64) << 7
495            | (((b4 >> 1) & 0b0111_1111) as u64);
496        (Some(stype), Some(dts))
497    } else {
498        (None, None)
499    };
500
501    // Reserved padding fills the rest of the extension body — skip it.
502    *cursor = body_end;
503
504    Ok(AdaptationFieldExtension {
505        length: ext_len as u8,
506        ltw_flag,
507        piecewise_rate_flag,
508        seamless_splice_flag,
509        ltw_valid_flag,
510        ltw_offset,
511        piecewise_rate,
512        splice_type,
513        dts_next_au,
514    })
515}
516
517/// Iterator over a contiguous sequence of 188-byte TS packets.
518#[derive(Debug)]
519pub struct TsPacketIter<'a> {
520    rest: &'a [u8],
521    halted: bool,
522}
523
524impl<'a> Iterator for TsPacketIter<'a> {
525    type Item = Result<TsPacket<'a>, TsError>;
526
527    fn next(&mut self) -> Option<Self::Item> {
528        if self.halted || self.rest.len() < TS_PACKET_LEN {
529            return None;
530        }
531        let (head, tail) = self.rest.split_at(TS_PACKET_LEN);
532        self.rest = tail;
533        match TsPacket::parse(head) {
534            Ok(pkt) => Some(Ok(pkt)),
535            Err(e) => {
536                // Stop iteration after the first malformed packet —
537                // the caller decided this slice was contiguous TS,
538                // so a bad sync byte means we've lost alignment.
539                self.halted = true;
540                Some(Err(e))
541            }
542        }
543    }
544}
545
546/// Walk a contiguous byte slice of 188-byte TS packets.
547///
548/// Stops cleanly at a truncated tail (returns no further items) and
549/// halts at the first malformed sync byte after yielding the
550/// [`TsError::BadSyncByte`] error.
551pub fn iter_packets(bytes: &[u8]) -> TsPacketIter<'_> {
552    TsPacketIter {
553        rest: bytes,
554        halted: false,
555    }
556}
557
558#[cfg(test)]
559mod tests {
560    use super::*;
561
562    /// Build a 188-byte packet from a 4-byte header and a tail. The
563    /// tail is padded with `0xFF` up to 184 bytes.
564    fn make_packet(header: [u8; 4], tail: &[u8]) -> Vec<u8> {
565        assert!(tail.len() <= 184);
566        let mut v = Vec::with_capacity(TS_PACKET_LEN);
567        v.extend_from_slice(&header);
568        v.extend_from_slice(tail);
569        v.resize(TS_PACKET_LEN, 0xFF);
570        v
571    }
572
573    #[test]
574    fn bare_audio_packet_payload_only() {
575        // PID = 0x1100, PUSI = 1, AF control = 0b01 (payload only),
576        // CC = 5.
577        let header = [
578            0x47,
579            0b0100_0001, // PUSI=1, PID hi = 0x01
580            0x00,        // PID lo = 0x00 → PID = 0x100
581            0b0001_0101, // scrambling=0, AF=0b01, CC=5
582        ];
583        let payload = [0xAA, 0xBB, 0xCC, 0xDD];
584        let bytes = make_packet(header, &payload);
585        let pkt = TsPacket::parse(&bytes).unwrap();
586        assert_eq!(pkt.pid, 0x0100);
587        assert!(pkt.payload_unit_start);
588        assert!(!pkt.transport_error);
589        assert_eq!(pkt.continuity_counter, 5);
590        assert!(pkt.adaptation_field.is_none());
591        // Payload covers bytes 4..188.
592        assert_eq!(pkt.payload.len(), 184);
593        assert_eq!(&pkt.payload[..4], &payload);
594    }
595
596    #[test]
597    fn packet_with_adaptation_field_and_pcr() {
598        // AF carries a PCR. We choose:
599        //   PCR base = 0x1_2345_6789  (33 bits, fits in u64)
600        //   PCR ext  = 0x0AB           (9 bits, fits in u16)
601        let base: u64 = 0x1_2345_6789;
602        let ext: u16 = 0x0AB;
603        // 6 PCR bytes: base[32..25] base[24..17] base[16..9] base[8..1]
604        // [base0 (1)|reserved 0b111111|ext_hi (1)]  [ext_lo (8)]
605        let p0 = ((base >> 25) & 0xFF) as u8;
606        let p1 = ((base >> 17) & 0xFF) as u8;
607        let p2 = ((base >> 9) & 0xFF) as u8;
608        let p3 = ((base >> 1) & 0xFF) as u8;
609        let p4 = (((base & 0b1) as u8) << 7) | 0b0111_1110 | (((ext >> 8) & 0b1) as u8);
610        let p5 = (ext & 0xFF) as u8;
611
612        // AF: length=7 (1 flags byte + 6 PCR bytes), flags=0x50 (RAI=1,
613        // PCR=1), then 6 PCR bytes.
614        let af_len: u8 = 1 + 6;
615        let af = [af_len, 0b0101_0000, p0, p1, p2, p3, p4, p5];
616
617        // Header: PID = 0x100, PUSI=0, AF control = 0b11 (AF + payload),
618        // CC = 0xA.
619        let header = [0x47, 0x01, 0x00, 0b0011_1010];
620
621        // Payload tail: a few "PES start" bytes for flavour.
622        let mut tail = Vec::new();
623        tail.extend_from_slice(&af);
624        tail.extend_from_slice(&[0x00, 0x00, 0x01, 0xE0]);
625
626        let bytes = make_packet(header, &tail);
627        let pkt = TsPacket::parse(&bytes).unwrap();
628        assert_eq!(pkt.pid, 0x0100);
629        assert!(!pkt.payload_unit_start);
630        assert_eq!(pkt.continuity_counter, 0xA);
631        let af = pkt.adaptation_field.expect("af present");
632        assert_eq!(af.length, 7);
633        assert!(af.random_access_indicator);
634        assert!(af.pcr_flag);
635        assert_eq!(af.pcr_base, Some(base));
636        assert_eq!(af.pcr_extension, Some(ext));
637        // Payload starts after header (4 bytes) + AF (8 bytes).
638        assert_eq!(pkt.payload.len(), TS_PACKET_LEN - 4 - 8);
639        assert_eq!(&pkt.payload[..4], &[0x00, 0x00, 0x01, 0xE0]);
640    }
641
642    #[test]
643    fn af_only_packet_has_empty_payload() {
644        // AF control = 0b10 (AF only, no payload). AF length 183 ⇒ AF
645        // occupies all of bytes 4..188 (length byte + 183 stuffing).
646        let header = [0x47, 0x01, 0x00, 0b0010_0000];
647        // AF: length=183, flags=0, then 182 stuffing bytes 0xFF.
648        let mut tail = Vec::new();
649        tail.push(183);
650        tail.push(0);
651        tail.extend(std::iter::repeat(0xFF).take(182));
652        let bytes = make_packet(header, &tail);
653        let pkt = TsPacket::parse(&bytes).unwrap();
654        assert!(pkt.adaptation_field.is_some());
655        assert_eq!(pkt.adaptation_field.unwrap().length, 183);
656        assert!(pkt.payload.is_empty());
657    }
658
659    #[test]
660    fn wrong_sync_byte_iterator_halts() {
661        let good_header = [0x47, 0x01, 0x00, 0b0001_0000];
662        let good = make_packet(good_header, &[]);
663        let mut bad = good.clone();
664        bad[0] = 0x48; // wrong sync
665        let mut buf = Vec::new();
666        buf.extend_from_slice(&good);
667        buf.extend_from_slice(&bad);
668        buf.extend_from_slice(&good);
669
670        let mut it = iter_packets(&buf);
671        let first = it.next().unwrap().unwrap();
672        assert_eq!(first.pid, 0x0100);
673        let second = it.next().unwrap();
674        match second {
675            Err(TsError::BadSyncByte(0x48)) => {}
676            other => panic!("expected BadSyncByte(0x48), got {other:?}"),
677        }
678        // After a sync break the iterator yields no more items.
679        assert!(it.next().is_none());
680    }
681
682    #[test]
683    fn truncated_tail_yields_nothing_extra() {
684        let good_header = [0x47, 0x00, 0x00, 0b0001_0000];
685        let good = make_packet(good_header, &[]);
686        let mut buf = Vec::new();
687        buf.extend_from_slice(&good);
688        buf.extend_from_slice(&[0x47, 0x00]); // 2 stray bytes
689        let mut it = iter_packets(&buf);
690        assert!(it.next().unwrap().is_ok());
691        assert!(it.next().is_none());
692    }
693
694    /// Encode a 6-byte program_clock_reference (33-bit base + 6 reserved
695    /// ones + 9-bit extension) per §2.4.3.5.
696    fn encode_clock_reference(base: u64, ext: u16) -> [u8; 6] {
697        let b0 = ((base >> 25) & 0xFF) as u8;
698        let b1 = ((base >> 17) & 0xFF) as u8;
699        let b2 = ((base >> 9) & 0xFF) as u8;
700        let b3 = ((base >> 1) & 0xFF) as u8;
701        let b4 = (((base & 0b1) as u8) << 7) | 0b0111_1110 | (((ext >> 8) & 0b1) as u8);
702        let b5 = (ext & 0xFF) as u8;
703        [b0, b1, b2, b3, b4, b5]
704    }
705
706    /// Wrap a custom adaptation-field payload (everything after the
707    /// flags byte) into a 188-byte TS packet whose AF_control = 0b11.
708    fn ts_packet_with_af(af_flags: u8, af_tail: &[u8]) -> Vec<u8> {
709        // af_len counts the flags byte + tail.
710        let af_len = (1 + af_tail.len()) as u8;
711        let header = [0x47, 0x01, 0x00, 0b0011_0000];
712        let mut tail = Vec::new();
713        tail.push(af_len);
714        tail.push(af_flags);
715        tail.extend_from_slice(af_tail);
716        make_packet(header, &tail)
717    }
718
719    #[test]
720    fn af_opcr_unpacked() {
721        // PCR + OPCR — same wire shape, different base/ext values.
722        let pcr_base: u64 = 0x0_0000_0001;
723        let pcr_ext: u16 = 0x000;
724        let opcr_base: u64 = 0x1_FEDC_BA98;
725        let opcr_ext: u16 = 0x1FE;
726        let mut tail = Vec::new();
727        tail.extend_from_slice(&encode_clock_reference(pcr_base, pcr_ext));
728        tail.extend_from_slice(&encode_clock_reference(opcr_base, opcr_ext));
729        // flags: PCR + OPCR.
730        let bytes = ts_packet_with_af(0b0001_1000, &tail);
731        let pkt = TsPacket::parse(&bytes).unwrap();
732        let af = pkt.adaptation_field.expect("af");
733        assert_eq!(af.pcr_base, Some(pcr_base));
734        assert_eq!(af.pcr_extension, Some(pcr_ext));
735        assert_eq!(af.opcr_base, Some(opcr_base));
736        assert_eq!(af.opcr_extension, Some(opcr_ext));
737    }
738
739    #[test]
740    fn af_splice_countdown_signed() {
741        // splicing_point_flag only — single signed byte.
742        let bytes = ts_packet_with_af(0b0000_0100, &[(-3i8) as u8]);
743        let pkt = TsPacket::parse(&bytes).unwrap();
744        let af = pkt.adaptation_field.expect("af");
745        assert!(af.splicing_point_flag);
746        assert_eq!(af.splice_countdown, Some(-3));
747    }
748
749    #[test]
750    fn af_transport_private_data() {
751        // transport_private_data_flag — 1-byte length + payload.
752        let payload: &[u8] = &[0xDE, 0xAD, 0xBE, 0xEF];
753        let mut tail = Vec::new();
754        tail.push(payload.len() as u8);
755        tail.extend_from_slice(payload);
756        let bytes = ts_packet_with_af(0b0000_0010, &tail);
757        let pkt = TsPacket::parse(&bytes).unwrap();
758        let af = pkt.adaptation_field.expect("af");
759        assert!(af.transport_private_data_flag);
760        assert_eq!(af.transport_private_data, Some(payload));
761    }
762
763    #[test]
764    fn af_extension_with_ltw_and_piecewise_rate() {
765        // Extension: ltw_flag + piecewise_rate_flag, no seamless_splice.
766        // ltw_valid_flag=1, ltw_offset=0x1234. piecewise_rate=0x2A_3B4C.
767        // ext_len = flags(1) + ltw(2) + piecewise(3) = 6.
768        let ltw_offset: u16 = 0x1234;
769        let b0 = 0b1000_0000 | ((ltw_offset >> 8) & 0x7F) as u8;
770        let b1 = (ltw_offset & 0xFF) as u8;
771        let pwr: u32 = 0x2A_3B4C;
772        let p0 = ((pwr >> 16) & 0x3F) as u8 | 0b1100_0000; // reserved=11
773        let p1 = ((pwr >> 8) & 0xFF) as u8;
774        let p2 = (pwr & 0xFF) as u8;
775        let tail = vec![
776            6,           // adaptation_field_extension_length
777            0b1100_0000, // ltw + piecewise, no seamless
778            b0,
779            b1,
780            p0,
781            p1,
782            p2,
783        ];
784        // flag = adaptation_field_extension_flag only.
785        let bytes = ts_packet_with_af(0b0000_0001, &tail);
786        let pkt = TsPacket::parse(&bytes).unwrap();
787        let af = pkt.adaptation_field.expect("af");
788        let ext = af.adaptation_field_extension.expect("ext");
789        assert_eq!(ext.length, 6);
790        assert!(ext.ltw_flag);
791        assert!(ext.piecewise_rate_flag);
792        assert!(!ext.seamless_splice_flag);
793        assert_eq!(ext.ltw_valid_flag, Some(true));
794        assert_eq!(ext.ltw_offset, Some(0x1234));
795        assert_eq!(ext.piecewise_rate, Some(0x2A_3B4C));
796        assert_eq!(ext.splice_type, None);
797        assert_eq!(ext.dts_next_au, None);
798    }
799
800    #[test]
801    fn af_extension_seamless_splice_dts() {
802        // seamless_splice only. splice_type = 5, DTS_next_AU = some
803        // 33-bit value. ext_len = flags(1) + seamless(5) = 6.
804        let dts: u64 = 0x1_2345_6789;
805        let splice_type: u8 = 5;
806        let b0 = (splice_type << 4) | (((dts >> 30) & 0b0111) as u8) << 1 | 0b1;
807        let b1 = ((dts >> 22) & 0xFF) as u8;
808        let b2 = (((dts >> 15) & 0b0111_1111) as u8) << 1 | 0b1;
809        let b3 = ((dts >> 7) & 0xFF) as u8;
810        let b4 = (((dts & 0b0111_1111) as u8) << 1) | 0b1;
811        let mut tail = Vec::new();
812        tail.push(6); // ext_len
813        tail.push(0b0010_0000); // seamless_splice only
814        tail.extend_from_slice(&[b0, b1, b2, b3, b4]);
815        let bytes = ts_packet_with_af(0b0000_0001, &tail);
816        let pkt = TsPacket::parse(&bytes).unwrap();
817        let ext = pkt
818            .adaptation_field
819            .unwrap()
820            .adaptation_field_extension
821            .expect("ext");
822        assert!(ext.seamless_splice_flag);
823        assert_eq!(ext.splice_type, Some(splice_type));
824        assert_eq!(ext.dts_next_au, Some(dts));
825    }
826
827    #[test]
828    fn af_extension_with_reserved_padding_is_accepted() {
829        // ext_len = 4: flags(1) + ltw(2) + 1 byte of reserved padding.
830        // Parser must not error and must skip the padding.
831        let ltw_offset: u16 = 0x0ABC;
832        let b0 = ((ltw_offset >> 8) & 0x7F) as u8; // ltw_valid=0
833        let b1 = (ltw_offset & 0xFF) as u8;
834        let tail = vec![
835            4,           // ext_len includes flags + ltw + padding
836            0b1000_0000, // ltw only
837            b0,
838            b1,
839            0xFF, // reserved padding
840        ];
841        let bytes = ts_packet_with_af(0b0000_0001, &tail);
842        let pkt = TsPacket::parse(&bytes).unwrap();
843        let ext = pkt
844            .adaptation_field
845            .unwrap()
846            .adaptation_field_extension
847            .expect("ext");
848        assert_eq!(ext.length, 4);
849        assert!(ext.ltw_flag);
850        assert_eq!(ext.ltw_valid_flag, Some(false));
851        assert_eq!(ext.ltw_offset, Some(0x0ABC));
852    }
853
854    #[test]
855    fn af_all_optional_subfields_together() {
856        // Every flag set; the AF carries PCR + OPCR + splice_countdown +
857        // 2-byte transport_private_data + a minimal extension with
858        // ltw_flag only. Verifies cursor ordering through the §2.4.3.4
859        // production.
860        let pcr_base: u64 = 100;
861        let pcr_ext: u16 = 50;
862        let opcr_base: u64 = 200;
863        let opcr_ext: u16 = 60;
864        let priv_data: &[u8] = &[0xAB, 0xCD];
865        let mut tail = Vec::new();
866        tail.extend_from_slice(&encode_clock_reference(pcr_base, pcr_ext));
867        tail.extend_from_slice(&encode_clock_reference(opcr_base, opcr_ext));
868        tail.push(7i8 as u8); // splice_countdown
869        tail.push(priv_data.len() as u8); // transport_private_data_length
870        tail.extend_from_slice(priv_data);
871        // Extension: ext_len = flags(1) + ltw(2) = 3, ltw_valid=0,
872        // ltw_offset = 0.
873        tail.push(3);
874        tail.push(0b1000_0000);
875        tail.push(0x00);
876        tail.push(0x00);
877        let bytes = ts_packet_with_af(0b0001_1111, &tail);
878        let pkt = TsPacket::parse(&bytes).unwrap();
879        let af = pkt.adaptation_field.expect("af");
880        assert_eq!(af.pcr_base, Some(pcr_base));
881        assert_eq!(af.opcr_base, Some(opcr_base));
882        assert_eq!(af.opcr_extension, Some(opcr_ext));
883        assert_eq!(af.splice_countdown, Some(7));
884        assert_eq!(af.transport_private_data, Some(priv_data));
885        let ext = af.adaptation_field_extension.expect("ext");
886        assert_eq!(ext.length, 3);
887        assert_eq!(ext.ltw_valid_flag, Some(false));
888        assert_eq!(ext.ltw_offset, Some(0));
889    }
890
891    #[test]
892    fn af_extension_truncated_body_errors() {
893        // ext_len claims 5 but only flags + 1 byte present in AF.
894        let tail = vec![
895            5,           // ext_len
896            0b1000_0000, // ltw_flag set
897            0x00,        // only one byte where ltw needs two
898        ];
899        let af_len = (1 + tail.len()) as u8;
900        let header = [0x47, 0x01, 0x00, 0b0011_0000];
901        let mut packet_tail = Vec::new();
902        packet_tail.push(af_len);
903        packet_tail.push(0b0000_0001);
904        packet_tail.extend_from_slice(&tail);
905        let bytes = make_packet(header, &packet_tail);
906        let err = TsPacket::parse(&bytes).unwrap_err();
907        match err {
908            TsError::Truncated { .. } => {}
909            other => panic!("expected Truncated, got {other:?}"),
910        }
911    }
912}