va_ts/
packet.rs

1use crate::error::{Error, Kind as ErrorKind};
2use crate::header::{Adaptation, Header};
3use crate::pcr::PCR;
4use crate::pid::PID;
5use crate::result::Result;
6
7pub struct Packet<'buf> {
8    buf: &'buf [u8],
9}
10
11impl<'buf> Packet<'buf> {
12    pub const SZ: usize = 188;
13    const SYNC_BYTE: u8 = 0x47;
14
15    #[inline(always)]
16    pub fn new(buf: &'buf [u8]) -> Result<Packet<'buf>> {
17        let pkt = Packet { buf };
18
19        pkt.validate()?;
20
21        Ok(pkt)
22    }
23
24    #[inline(always)]
25    fn validate(&self) -> Result<()> {
26        if self.buf.len() != Self::SZ {
27            Err(Error::new(ErrorKind::Buf(self.buf.len(), Self::SZ)))
28        } else if self.buf[0] != Self::SYNC_BYTE {
29            Err(Error::new(ErrorKind::SyncByte(self.buf[0])))
30        } else {
31            Ok(())
32        }
33    }
34
35    /// adaptation start position
36    #[inline(always)]
37    fn buf_pos_adaptation() -> usize {
38        Header::SZ
39    }
40
41    // TODO: try_seek?
42    //       or pos_<name> + seek?
43    /// position payload start
44    #[inline(always)]
45    fn buf_pos_payload(&self, is_section: bool) -> usize {
46        let mut pos = Self::buf_pos_adaptation();
47        let header = self.header();
48
49        if header.got_adaptation() {
50            // TODO: Adaptation::sz(self.buf)
51            //       self.adaptation() + self.try_adaptation()
52            let adapt = Adaptation::new(self.buf_seek(pos));
53            pos += adapt.sz();
54        }
55
56        if header.pusi() && is_section {
57            // payload data start
58            //
59            // https://stackoverflow.com/a/27525217
60            // From the en300 468 spec:
61            //
62            // Sections may start at the beginning of the payload of a TS packet,
63            // but this is not a requirement, because the start of the first
64            // section in the payload of a TS packet is pointed to by the pointer_field.
65            //
66            // So the section start actually is an offset from the payload:
67            //
68            // uint8_t* section_start = payload + *payload + 1;
69            pos += (self.buf[pos] as usize) + 1;
70        }
71
72        pos
73    }
74
75    #[inline(always)]
76    fn buf_seek(&self, offset: usize) -> &'buf [u8] {
77        &self.buf[offset..]
78    }
79
80    #[inline(always)]
81    fn buf_try_seek(&self, offset: usize) -> Result<&'buf [u8]> {
82        if self.buf.len() <= offset {
83            Err(Error::new(ErrorKind::Buf(self.buf.len(), Self::SZ)))
84        } else {
85            Ok(self.buf_seek(offset))
86        }
87    }
88
89    #[inline(always)]
90    fn buf_adaptation(&self) -> Result<&'buf [u8]> {
91        self.buf_try_seek(Self::buf_pos_adaptation())
92    }
93
94    #[inline(always)]
95    fn buf_payload(&self, is_section: bool) -> Result<&'buf [u8]> {
96        self.buf_try_seek(self.buf_pos_payload(is_section))
97    }
98
99    #[inline(always)]
100    pub fn buf_payload_section(&self) -> Result<&'buf [u8]> {
101        self.buf_payload(true)
102    }
103
104    #[inline(always)]
105    pub fn buf_payload_pes(&self) -> Result<&'buf [u8]> {
106        self.buf_payload(false)
107    }
108
109    // TODO: merge Header and Packet?
110    #[inline(always)]
111    fn header(&self) -> Header<'buf> {
112        Header::new(self.buf)
113    }
114
115    #[inline(always)]
116    fn adaptation(&self) -> Option<Result<Adaptation<'buf>>> {
117        let header = self.header();
118
119        if header.got_adaptation() {
120            // TODO: move to macro? or optional-result crate
121            match self.buf_adaptation() {
122                Ok(buf) => Some(Adaptation::try_new(buf)),
123                Err(e) => Some(Err(e)),
124            }
125        } else {
126            None
127        }
128    }
129
130    #[inline(always)]
131    pub fn pid(&self) -> PID {
132        self.header().pid()
133    }
134
135    #[inline(always)]
136    pub fn cc(&self) -> u8 {
137        self.header().cc()
138    }
139
140    #[inline(always)]
141    pub fn pusi(&self) -> bool {
142        self.header().pusi()
143    }
144
145    #[inline(always)]
146    pub fn pcr(&self) -> Result<Option<PCR<'buf>>> {
147        self.adaptation()
148            .and_then(|res| match res {
149                Ok(adapt) => adapt.pcr().map(Ok),
150                Err(e) => Some(Err(e)),
151            })
152            .transpose()
153    }
154
155    // TODO: generic pmt, pat method
156    #[inline(always)]
157    pub fn pat(&self) -> Result<Option<&'buf [u8]>> {
158        let header = self.header();
159
160        if !header.got_payload() {
161            return Ok(None);
162        }
163
164        let res = if self.pid() == PID::PAT {
165            // TODO: move to macro? or optional-result crate
166            match self.buf_payload_section() {
167                Ok(buf) => Some(Ok(buf)),
168                Err(e) => Some(Err(e)),
169            }
170        } else {
171            None
172        };
173
174        res.transpose()
175    }
176
177    // TODO: refactoring
178    #[inline(always)]
179    pub fn pmt(&self, pid: u16) -> Result<Option<&'buf [u8]>> {
180        let header = self.header();
181
182        if !header.got_payload() {
183            return Ok(None);
184        }
185
186        let res = if u16::from(self.pid()) == pid {
187            // TODO: move to macro? or optional-result crate
188            match self.buf_payload_section() {
189                Ok(buf) => Some(Ok(buf)),
190                Err(e) => Some(Err(e)),
191            }
192        } else {
193            None
194        };
195
196        res.transpose()
197    }
198
199    // TODO: refactoring
200    #[inline(always)]
201    pub fn eit(&self) -> Result<Option<&'buf [u8]>> {
202        let header = self.header();
203
204        if !header.got_payload() {
205            return Ok(None);
206        }
207
208        let res = if self.pid() == PID::EIT {
209            // TODO: move to macro? or optional-result crate
210            match self.buf_payload_section() {
211                Ok(buf) => Some(Ok(buf)),
212                Err(e) => Some(Err(e)),
213            }
214        } else {
215            None
216        };
217
218        res.transpose()
219    }
220}