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}