cea708_types/
parser.rs

1// Copyright (C) 2025 Matthew Waters <matthew@centricular.com>
2//
3// Licensed under the MIT license <LICENSE-MIT> or
4// http://opensource.org/licenses/MIT>, at your option. This file may not be
5// copied, modified, or distributed except according to those terms.
6
7use std::collections::VecDeque;
8
9use log::{trace, warn};
10
11use crate::{tables, Cea608, DTVCCPacket};
12
13/// Various possible errors when parsing data
14#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
15pub enum ParserError {
16    /// Length of data does not match length advertised
17    #[error("The length of the data ({actual}) does not match the advertised expected ({expected}) length")]
18    LengthMismatch {
19        /// The expected size
20        expected: usize,
21        /// The actual size
22        actual: usize,
23    },
24    /// CEA-608 comaptibility bytes encountered after CEA-708
25    #[error("CEA-608 compatibility bytes were found after CEA-708 bytes at position {byte_pos}")]
26    Cea608AfterCea708 {
27        /// Position of the offending bytes
28        byte_pos: usize,
29    },
30}
31
32impl From<tables::CodeError> for ParserError {
33    fn from(err: tables::CodeError) -> Self {
34        match err {
35            tables::CodeError::LengthMismatch { expected, actual } => {
36                ParserError::LengthMismatch { expected, actual }
37            }
38        }
39    }
40}
41
42/// Parses a byte stream of `cc_data` bytes into indivdual [`DTVCCPacket`]s.
43#[derive(Debug, Default)]
44pub struct CCDataParser {
45    pending_data: Vec<u8>,
46    packets: VecDeque<DTVCCPacket>,
47    cea608: Option<Vec<Cea608>>,
48    have_initial_ccp_header: bool,
49    ccp_bytes_needed: usize,
50}
51
52impl CCDataParser {
53    /// Create a new [CCDataParser]
54    pub fn new() -> Self {
55        Self::default()
56    }
57
58    pub fn handle_cea608(&mut self) {
59        self.cea608 = Some(vec![]);
60    }
61
62    /// Push a complete `cc_data` packet into the parser for processing.
63    ///
64    /// Will fail with [ParserError::LengthMismatch] if the length of the data does not match the
65    /// number of cc triples specified in the `cc_data` header.
66    ///
67    /// Any CEA-608 data provided after valid CEA-708 data will return
68    /// [ParserError::Cea608AfterCea708].
69    pub fn push(&mut self, data: &[u8]) -> Result<(), ParserError> {
70        trace!("parsing {data:?}");
71        if let Some(ref mut cea608) = self.cea608 {
72            cea608.clear();
73        }
74
75        if data.len() < 5 {
76            // enough for 2 byte header plus 1 byte triple
77            return Ok(());
78        }
79        let process_cc_data_flag = data[0] & 0x40 > 0;
80        if !process_cc_data_flag {
81            return Ok(());
82        }
83
84        let cc_count = data[0] & 0x1F;
85        if cc_count == 0 {
86            return Ok(());
87        }
88        trace!("cc_count: {cc_count}, len = {}", data.len());
89        if (cc_count * 3 + 2) as usize != data.len() {
90            return Err(ParserError::LengthMismatch {
91                expected: (cc_count * 3 + 1) as usize,
92                actual: data.len(),
93            });
94        }
95
96        let mut ccp_data = vec![];
97        let mut in_dtvcc = false;
98
99        // re-add first byte to pending_data
100        let mut pending_data = vec![];
101        for (i, d) in self.pending_data.chunks(2).enumerate() {
102            if i == 0 {
103                pending_data.push(0xFF);
104            } else {
105                pending_data.push(0xFE);
106            }
107            pending_data.extend(d);
108            if d.len() == 1 {
109                pending_data.push(0x00);
110            }
111        }
112
113        // find the start of ccp in data
114        let ccp_offset;
115        {
116            let mut ret = None;
117            for (i, triple) in data[2..].chunks_exact(3).enumerate() {
118                let cc_valid = (triple[0] & 0x04) == 0x04;
119                let cc_type = triple[0] & 0x3;
120                trace!(
121                    "input byte:{} triple 0x{:02x} 0x{:02x} 0x{:02x}. valid: {cc_valid}, type: {cc_type}",
122                    i * 3,
123                    triple[0],
124                    triple[1],
125                    triple[2]
126                );
127                if (cc_type & 0b10) > 0 {
128                    in_dtvcc = true;
129                }
130                if !cc_valid {
131                    continue;
132                }
133                if !in_dtvcc && (cc_type == 0b00 || cc_type == 0b01) {
134                    trace!(
135                        "have cea608 bytes type {cc_type} 0x{:02x} 0x{:02x}",
136                        triple[1],
137                        triple[2]
138                    );
139                    if let Some(ref mut cea608) = self.cea608 {
140                        let pair = match cc_type {
141                            0b00 => Cea608::Field1(triple[1], triple[2]),
142                            0b01 => Cea608::Field2(triple[1], triple[2]),
143                            _ => unreachable!(),
144                        };
145                        cea608.push(pair);
146                    }
147                    continue;
148                }
149
150                if in_dtvcc && (cc_type == 0b00 || cc_type == 0b01) {
151                    // invalid packet construction;
152                    warn!("cea608 bytes after cea708 data at byte:{}", i * 3);
153                    return Err(ParserError::Cea608AfterCea708 { byte_pos: i * 3 });
154                }
155
156                if ret.is_none() {
157                    ret = Some(i * 3);
158                }
159            }
160
161            if let Some(ret) = ret {
162                ccp_offset = 2 + ret
163            } else {
164                // no data to process
165                return Ok(());
166            }
167        }
168        trace!("ccp offset in input data is at index {ccp_offset}");
169
170        let mut data_iter = pending_data.iter().chain(data[ccp_offset..].iter());
171        let mut i = 0;
172        in_dtvcc = false;
173        loop {
174            let byte0 = data_iter.next();
175            let byte1 = data_iter.next();
176            let byte2 = data_iter.next();
177            let (Some(byte0), Some(byte1), Some(byte2)) = (byte0, byte1, byte2) else {
178                break;
179            };
180            let cc_valid = (byte0 & 0x04) == 0x04;
181            let cc_type = byte0 & 0x3;
182            trace!(
183                "pending byte:{i} triple 0x{byte0:02x} 0x{byte1:02x} 0x{byte2:02x}. valid: {cc_valid}, type: {cc_type}",
184            );
185            i += 3;
186            if (cc_type & 0b10) > 0 {
187                in_dtvcc = true;
188            }
189            if !cc_valid {
190                continue;
191            }
192            if !in_dtvcc && (cc_type == 0b00 || cc_type == 0b01) {
193                // 608-in-708 data should not be hit as we skip over it
194                unreachable!();
195            }
196
197            if (cc_type & 0b11) == 0b11 {
198                trace!("found ccp header at index {}", i - 3);
199                self.have_initial_ccp_header = true;
200                // a header byte truncates the size of any previous packet
201                match DTVCCPacket::parse(&ccp_data) {
202                    Ok(packet) => self.packets.push_front(packet),
203                    Err(ParserError::LengthMismatch { .. }) => (),
204                    Err(e) => {
205                        eprintln!("{e:?}");
206                        unreachable!()
207                    }
208                }
209                in_dtvcc = false;
210                ccp_data = vec![];
211                let (_seq_no, packet_len) = DTVCCPacket::parse_hdr_byte(*byte1);
212                trace!("waiting for {} dtvcc bytes", packet_len + 1);
213                self.ccp_bytes_needed = packet_len + 1;
214            }
215
216            if self.have_initial_ccp_header {
217                trace!("pushing 0x{:02x?}{:02x?}", byte1, byte2);
218                if self.ccp_bytes_needed > 0 {
219                    ccp_data.push(*byte1);
220                    self.ccp_bytes_needed -= 1;
221                }
222                if self.ccp_bytes_needed > 0 {
223                    ccp_data.push(*byte2);
224                    self.ccp_bytes_needed -= 1;
225                }
226            }
227        }
228
229        if self.ccp_bytes_needed == 0 {
230            match DTVCCPacket::parse(&ccp_data) {
231                Ok(packet) => self.packets.push_front(packet),
232                Err(ParserError::LengthMismatch { .. }) => (),
233                _ => unreachable!(),
234            }
235            ccp_data = vec![];
236        }
237
238        self.pending_data = ccp_data;
239
240        Ok(())
241    }
242
243    /// Clear any internal buffers
244    pub fn flush(&mut self) {
245        *self = Self::default();
246    }
247
248    /// Pop a valid [DTVCCPacket] or None if no packet could be parsed
249    pub fn pop_packet(&mut self) -> Option<DTVCCPacket> {
250        let ret = self.packets.pop_back();
251        trace!("popped {ret:?}");
252        ret
253    }
254
255    /// Any [`Cea608`] bytes in the last parsed `cc_data`
256    pub fn cea608(&mut self) -> Option<&[Cea608]> {
257        self.cea608.as_deref()
258    }
259}