Skip to main content

ts_analyzer/packet/
payload.rs

1//! TSPayload keeps track of the payload data.
2
3use crate::ErrorKind;
4
5pub type PayloadBytes = Vec<u8>;
6
7#[derive(Clone, Debug, PartialEq)]
8pub enum TsPayloadData {
9    StartData(PayloadBytes, PayloadBytes),
10    Data(PayloadBytes),
11}
12
13#[derive(Clone, Debug, PartialEq)]
14/// Payload of a transport stream object.
15pub struct TsPayload {
16    /// The raw bytes contained in the payload (excluding the Payload Pointer
17    /// if one exists)
18    data: TsPayloadData,
19    /// The continuity counter keeps track of the order in which packets get
20    /// created for a specific PID.
21    ///
22    /// This is a clone of the continuity counter found in the header. I have
23    /// chosen to duplicate it here due to the fact that it is useful to
24    /// have when attempting to stitch payloads together. By including it
25    /// the user does not have to store every `TSPacket` and can instead just
26    /// store `TSPayload` objects.
27    ///
28    /// This is stored as a u8 but should actually be a u4 as it is only made
29    /// up of 4 bits in the header.
30    continuity_counter: u8,
31}
32
33impl TsPayload {
34    /// Parse the payload data and `pusi` from the raw payload bytes.
35    pub fn from_bytes(
36        pusi: bool,
37        continuity_counter: u8,
38        payload_data: &[u8],
39    ) -> TsPayload {
40        assert!(!payload_data.is_empty(), "Payload data is empty");
41        let data = if pusi {
42            let (start_index, full_data) = payload_data.split_first().unwrap();
43            let (end_data, start_data) =
44                full_data.split_at_checked(*start_index as usize).unwrap();
45            assert!(!start_data.is_empty(), "Starting payload data is empty");
46            TsPayloadData::StartData(end_data.to_vec(), start_data.to_vec())
47        } else {
48            TsPayloadData::Data(payload_data.to_vec())
49        };
50
51        TsPayload { data, continuity_counter }
52    }
53
54    /// Return the continuity counter of this payload.
55    pub fn continuity_counter(&self) -> u8 {
56        self.continuity_counter
57    }
58
59    /// Returns if this payload contains the start of a new payload in it's
60    /// data.
61    pub fn is_start(&self) -> bool {
62        matches!(self.data, TsPayloadData::StartData(_, _))
63    }
64
65    /// Returns the current payload data. This is the data before the start
66    /// index, if one exists.
67    pub fn get_payload_data(self) -> TsPayloadData {
68        self.data
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use test_case::test_case;
75
76    use super::*;
77
78    // #[test_case(true, Some(2), 2; "Payload contains start")]
79    // #[test_case(false, None, 10; "Payload does not contain start")]
80    // fn from_bytes(pusi: bool, start_index: Option<u8>, continuity_counter:
81    // u8) {     let raw_data = [2, 1, 2, 3, 4];
82    //     let expected_data: Box<[u8]> = match pusi {
83    //         true => Box::from(raw_data[1..raw_data.len()].to_vec()),
84    //         false => Box::from(raw_data),
85    //     };
86    //
87    //     let payload =
88    //         TsPayload::from_bytes(pusi, continuity_counter, &raw_data);
89    //     assert_eq!(payload, expected_data, "Data is not the same");
90    //     assert_eq!(
91    //         payload.start_index(),
92    //         start_index,
93    //         "Start index is not the same"
94    //     );
95    //     assert_eq!(
96    //         payload.continuity_counter(),
97    //         continuity_counter,
98    //         "Continuity counter is not the same"
99    //     );
100    // }
101
102    #[test_case(true; "Payload contains start")]
103    #[test_case(false; "Payload does not contain start")]
104    fn is_start(is_start: bool) {
105        let payload = TsPayload::from_bytes(is_start, 0, &[2, 1, 2, 3, 4]);
106        assert_eq!(payload.is_start(), is_start, "Start payload is incorrect");
107    }
108
109    // #[test_case(true; "Payload contains start")]
110    // #[test_case(false; "Payload does not contain start")]
111    // fn get_current_data(pusi: bool) {
112    //     let raw_data = [2, 1, 2, 3, 4];
113    //     let expected_data: Box<[u8]> = match pusi {
114    //         true => {
115    //             // We add 1 because in the actual function we remove the
116    // first             // item when the PUSI is true
117    //             let idx = raw_data[0] + 1;
118    //             Box::from(raw_data[1..idx as usize].to_vec())
119    //         }
120    //         false => Box::from(raw_data),
121    //     };
122    //
123    //     let payload = TsPayload::from_bytes(pusi, 0, &raw_data);
124    //     assert_eq!(payload, expected_data, "Current data is not the same");
125    // }
126
127    // #[test_case(true; "Payload contains start")]
128    // #[test_case(false; "Payload does not contain start")]
129    // fn get_start_data(pusi: bool) {
130    //     let raw_data = [2, 1, 2, 3, 4];
131    //     let expected_data: Result<Box<[u8]>, ErrorKind> = match pusi {
132    //         true => {
133    //             // We add 1 because in the actual function we remove the
134    // first             // item when the PUSI is true
135    //             let idx = raw_data[0] + 1;
136    //             Ok(Box::from(raw_data[idx as
137    // usize..raw_data.len()].to_vec()))         }
138    //         false => Err(ErrorKind::PayloadIsNotStart),
139    //     };
140    //     let payload = TsPayload::from_bytes(pusi, 0, &raw_data);
141    //
142    //     match payload.get_start_data() {
143    //         Ok(data) => assert!(
144    //             data.iter().eq(expected_data.unwrap().iter()),
145    //             "Start data is incorrect"
146    //         ),
147    //         Err(data) => assert_eq!(
148    //             data.to_string(),
149    //             expected_data.unwrap_err().to_string(),
150    //             "Incorrect error type"
151    //         ),
152    //     };
153    // }
154}