Skip to main content

ts_analyzer/packet/
header.rs

1//! This module keeps track of all the information stored in the header of a
2//! transport stream packet.
3
4use std::fmt::Display;
5use std::fmt::Formatter;
6
7use bitvec::field::BitField;
8use bitvec::order::Msb0;
9use bitvec::slice::BitSlice;
10use bitvec::view::BitView;
11#[cfg(feature = "tracing")]
12use tracing::trace;
13
14use crate::AdaptationFieldControl;
15use crate::AdaptationFieldControl::AdaptationAndPayload;
16use crate::AdaptationFieldControl::AdaptationField;
17use crate::AdaptationFieldControl::Payload;
18use crate::ErrorKind;
19use crate::TransportScramblingControl;
20use crate::TransportScramblingControl::EvenKey;
21use crate::TransportScramblingControl::NoScrambling;
22use crate::TransportScramblingControl::OddKey;
23
24/// All transport stream packets start with a SYNC byte.
25pub const SYNC_BYTE: u8 = 0x47;
26
27/// All of this information is shamelessly stolen from wikipedia, my lord and
28/// savior. This [article](https://en.wikipedia.org/wiki/MPEG_transport_stream) in particular. Please donate
29/// to wikipedia if you have the means.
30#[derive(Clone, Copy, Debug)]
31pub struct TsHeader {
32    /// TEI: Transport error indicator is true when a packet is set when a
33    /// demodulator cannot correct invalid_first_byte and indicates that
34    /// the packet is corrupt.
35    tei: bool,
36    /// PUSI: Payload unit start indicator indicates if this packet contains
37    /// the first byte of a payload since they can be spread across
38    /// multiple packets.
39    pusi: bool,
40    /// Transport priority, set when the current packet is higher priority than
41    /// other packets of the same PID
42    transport_priority: bool,
43    /// PID: Packet identifier of the transport stream packet. Describes what
44    /// the payload data is.
45    pid: u16,
46    /// TSC: Transport scrambling control indicates whether the payload is
47    /// encrypted and with what key. Valid values are:
48    /// - `0` for no scrambling.
49    /// - `1` is reserved.
50    /// - `2` Scrambled with even key.
51    /// - `3` Scrambled with odd key.
52    tsc: TransportScramblingControl,
53    /// Adaptation field control describes if this packet contains adaptation
54    /// field data, payload data, or both. Valid values are:
55    /// - `0` is reserved.
56    /// - `1` for payload only.
57    /// - `2` for adaptation field only.
58    /// - `3` for adaptation field followed by payload.
59    adaptation_field_control: AdaptationFieldControl,
60    /// Continuity counter is used for determining the sequence of data in each
61    /// PID.
62    continuity_counter: u8,
63}
64
65impl TsHeader {
66    /// Create a new header
67    pub fn new(
68        tei: bool,
69        pusi: bool,
70        transport_priority: bool,
71        pid: u16,
72        tsc: u8,
73        adaptation_field_control: u8,
74        continuity_counter: u8,
75    ) -> Self {
76        #[cfg(feature = "tracing")]
77        {
78            trace!("pid: [{}]", pid);
79            trace!("adaptation_field_control: [{}]", adaptation_field_control);
80            trace!("continuity_counter: [{}]", continuity_counter);
81        }
82
83        TsHeader {
84            tei,
85            pusi,
86            transport_priority,
87            pid,
88            tsc: match tsc {
89                0 => NoScrambling,
90                1 => TransportScramblingControl::Reserved,
91                2 => EvenKey,
92                3 => OddKey,
93                _ => panic!("Invalid TSC value [{}]", tsc),
94            },
95            adaptation_field_control: match adaptation_field_control {
96                0 => AdaptationFieldControl::Reserved,
97                1 => Payload,
98                2 => AdaptationField,
99                3 => AdaptationAndPayload,
100                _ => panic!(
101                    "Invalid adaptation field control value [{}]",
102                    adaptation_field_control
103                ),
104            },
105            continuity_counter,
106        }
107    }
108
109    /// Get the packet header from raw bytes.
110    pub fn from_bytes(buf: &[u8]) -> Result<TsHeader, ErrorKind> {
111        let bytes: &BitSlice<u8, Msb0> = buf.view_bits();
112
113        // Check if the first byte is SYNC byte.
114        if bytes[0..8].load::<u8>() != SYNC_BYTE {
115            return Err(ErrorKind::InvalidFirstByte { byte: buf[0] });
116        }
117
118        #[cfg(feature = "tracing")]
119        trace!("header bytes: {:b}", bytes);
120
121        // Get the header information from the header bytes
122        let header = TsHeader {
123            tei: bytes[8],
124            pusi: bytes[9],
125            transport_priority: bytes[10],
126            pid: bytes[11..24].to_bitvec().load_be(),
127            tsc: match bytes[24..26].to_bitvec().load_be() {
128                0 => NoScrambling,
129                1 => TransportScramblingControl::Reserved,
130                2 => EvenKey,
131                3 => OddKey,
132                default => panic!("Invalid TSC value [{}]", default),
133            },
134            adaptation_field_control: match bytes[26..28]
135                .to_bitvec()
136                .load_be::<u8>()
137            {
138                0 => AdaptationFieldControl::Reserved,
139                1 => Payload,
140                2 => AdaptationField,
141                3 => AdaptationAndPayload,
142                default => panic!(
143                    "Invalid adaptation field control value [{}]",
144                    default
145                ),
146            },
147            continuity_counter: bytes[28..32].load_be(),
148        };
149
150        #[cfg(feature = "tracing")]
151        trace!("Header for TSPacket: {}", header);
152
153        Ok(header)
154    }
155
156    /// Return if the transport error indicator is set.
157    pub fn tei(&self) -> bool {
158        self.tei
159    }
160
161    /// Return if the payload unit start indicator is set.
162    pub fn pusi(&self) -> bool {
163        self.pusi
164    }
165
166    /// Return if the transport priority is set.
167    pub fn transport_priority(&self) -> bool {
168        self.transport_priority
169    }
170
171    /// Returns the packet identifier.
172    pub fn pid(&self) -> u16 {
173        self.pid
174    }
175
176    /// Return's the transport scrambling control of this packet.
177    pub fn tsc(&self) -> TransportScramblingControl {
178        self.tsc
179    }
180
181    /// Adaptation field control value.
182    pub fn adaptation_field_control(&self) -> AdaptationFieldControl {
183        self.adaptation_field_control
184    }
185
186    /// Return whether this packet has an adaptation field or not
187    pub fn has_adaptation_field(&self) -> bool {
188        matches!(
189            self.adaptation_field_control,
190            AdaptationField | AdaptationAndPayload
191        )
192    }
193
194    /// Return whether this packet has a payload or not
195    pub fn has_payload(&self) -> bool {
196        matches!(self.adaptation_field_control, Payload | AdaptationAndPayload)
197    }
198
199    /// Returns the continuity counter.
200    pub fn continuity_counter(&self) -> u8 {
201        self.continuity_counter
202    }
203}
204
205impl Display for TsHeader {
206    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
207        let msg = format!(
208            "\n\
209            TEI: {}\n\
210            PUSI: {}\n\
211            Transport Priority: {}\n\
212            PID: {}\n\
213            Transport Scrambling Control: {:?}\n\
214            Adaptation Field Control: {:?}\n\
215            Continuity Counter: {}",
216            self.tei,
217            self.pusi,
218            self.transport_priority,
219            self.pid,
220            self.tsc,
221            self.adaptation_field_control,
222            self.continuity_counter,
223        );
224        write!(f, "{}", msg)
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use super::*;
231
232    #[test]
233    fn from_bytes() {
234        let buf: Box<[u8]> = Box::new([0x47, 0x01, 0x00, 0x1A]);
235        let header = TsHeader::from_bytes(&buf).unwrap();
236        assert!(!header.tei(), "Transport Error Indicator is incorrect");
237        assert!(!header.pusi(), "Payload Unit Start Indicator is incorrect");
238        assert!(
239            !header.transport_priority(),
240            "Transport Priority is incorrect"
241        );
242        assert_eq!(header.pid(), 256, "Transport Priority is incorrect");
243        assert_eq!(
244            header.adaptation_field_control(),
245            Payload,
246            "Transport Priority is incorrect"
247        );
248        assert_eq!(
249            header.continuity_counter(),
250            10,
251            "Transport Priority is incorrect"
252        );
253    }
254
255    #[test]
256    fn from_bytes2() {
257        let buf: Box<[u8]> = Box::new([0x47, 0xE1, 0x00, 0x3B]);
258        let header = TsHeader::from_bytes(&buf).unwrap();
259        assert!(header.tei(), "Transport Error Indicator is incorrect");
260        assert!(header.pusi(), "Payload Unit Start Indicator is incorrect");
261        assert!(header.transport_priority(), "Transport Priority is incorrect");
262        assert_eq!(header.pid(), 256, "Transport Priority is incorrect");
263        assert_eq!(
264            header.adaptation_field_control(),
265            AdaptationAndPayload,
266            "Transport Priority is incorrect"
267        );
268        assert_eq!(
269            header.continuity_counter(),
270            11,
271            "Transport Priority is incorrect"
272        );
273    }
274}