cea708_types/
packet.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 log::{debug, trace};
8
9use crate::{tables, ParserError, WriterError};
10
11/// A packet in the `cc_data` bitstream
12#[derive(Debug)]
13pub struct DTVCCPacket {
14    seq_no: u8,
15    services: Vec<Service>,
16}
17
18impl DTVCCPacket {
19    /// Create a new [DTVCCPacket] with the specified sequence number.
20    ///
21    /// # Panics
22    ///
23    /// * If seq_no >= 4
24    pub fn new(seq_no: u8) -> Self {
25        if seq_no > 3 {
26            panic!("DTVCCPacket sequence numbers must be between 0 and 3 inclusive, not {seq_no}");
27        }
28        Self {
29            seq_no,
30            services: vec![],
31        }
32    }
33
34    /// The sequence number of the DTVCCPacket
35    ///
36    /// # Examples
37    /// ```
38    /// # use cea708_types::*;
39    /// let packet = DTVCCPacket::new(2);
40    /// assert_eq!(2, packet.sequence_no());
41    /// ```
42    pub fn sequence_no(&self) -> u8 {
43        self.seq_no
44    }
45
46    /// The amount of free space (in bytes) that can by placed inside this [DTVCCPacket]
47    pub fn free_space(&self) -> usize {
48        // 128 is the max size of a DTVCCPacket, minus 1 for the header
49        128 - self.len()
50    }
51
52    /// The number of bytes this [DTVCCPacket] will use when written to a byte stream.
53    ///
54    /// # Examples
55    /// ```
56    /// # use cea708_types::{*, tables::*};
57    /// let mut packet = DTVCCPacket::new(2);
58    /// assert_eq!(0, packet.len());
59    /// let mut service = Service::new(1);
60    /// service.push_code(&Code::LatinCapitalA).unwrap();
61    /// packet.push_service(service);
62    /// assert_eq!(3, packet.len());
63    /// ```
64    pub fn len(&self) -> usize {
65        let services_len = self.services.iter().map(|s| s.len()).sum::<usize>();
66        if services_len > 0 {
67            1 + services_len
68        } else {
69            0
70        }
71    }
72
73    /// Whether this packet is currently empty and contains no [`Service`]s.
74    ///
75    /// # Examples
76    /// ```
77    /// # use cea708_types::{*, tables::*};
78    /// let mut packet = DTVCCPacket::new(2);
79    /// assert!(packet.is_empty());
80    /// let mut service = Service::new(1);
81    /// service.push_code(&Code::LatinCapitalA).unwrap();
82    /// packet.push_service(service);
83    /// assert!(!packet.is_empty());
84    /// ```
85    pub fn is_empty(&self) -> bool {
86        self.services.is_empty()
87    }
88
89    /// Push a completed service block into this [DTVCCPacket]
90    ///
91    /// # Examples
92    /// ```
93    /// # use cea708_types::{*, tables::*};
94    /// let mut packet = DTVCCPacket::new(2);
95    /// assert_eq!(0, packet.len());
96    /// let mut service = Service::new(1);
97    /// service.push_code(&Code::LatinCapitalA).unwrap();
98    /// packet.push_service(service);
99    /// assert_eq!(3, packet.len());
100    /// ```
101    pub fn push_service(&mut self, service: Service) -> Result<(), WriterError> {
102        if service.len() > self.free_space() {
103            return Err(WriterError::WouldOverflow(
104                service.len() - self.free_space(),
105            ));
106        }
107        if service.is_empty() {
108            return Err(WriterError::EmptyService);
109        }
110        self.services.push(service);
111        Ok(())
112    }
113
114    fn last_service_that_fits_code(
115        &mut self,
116        service_no: u8,
117        code: &tables::Code,
118    ) -> Option<&mut Service> {
119        self.services
120            .iter_mut()
121            .rev()
122            .find(|service| service.number() == service_no)
123            .filter(|service| service.free_space() >= code.byte_len())
124    }
125
126    fn mut_service_by_number(&mut self, service_no: u8) -> Option<&mut Service> {
127        self.services
128            .iter_mut()
129            .rev()
130            .find(|service| service.number() == service_no)
131    }
132
133    fn push_new_service_with_code(
134        &mut self,
135        service_no: u8,
136        code: tables::Code,
137    ) -> Result<(), WriterError> {
138        let free_space = self.free_space();
139        let mut service = Service::new(service_no);
140        service.push_code(&code)?;
141        trace!(
142            "pusing code into new service {}, free space {free_space}",
143            service.len()
144        );
145        if service.len() > free_space {
146            return Err(WriterError::WouldOverflow(
147                service.len() - self.free_space(),
148            ));
149        }
150        self.services.push(service);
151        Ok(())
152    }
153
154    fn push_code_existing_service(
155        existing: &mut Service,
156        code: tables::Code,
157        free_space: usize,
158    ) -> Result<(), WriterError> {
159        trace!(
160            "push code into existing service {}, free space {free_space}",
161            existing.len()
162        );
163        if code.byte_len() > free_space {
164            return Err(WriterError::WouldOverflow(code.byte_len() - free_space));
165        }
166        existing.push_code(&code)
167    }
168
169    /// Push a [`Code`](tables::Code) into this [`DTVCCPacket`]
170    ///
171    /// Will try to push the [`Code`](tables::Code) into an already existing [`Service`] within the
172    /// packet.  If that fails, then will create and push a new [`Service`] with the
173    /// [`Code`](tables::Code).
174    ///
175    /// # Panics
176    ///
177    /// * if service_no >= 64
178    pub fn push_code(&mut self, service_no: u8, code: tables::Code) -> Result<(), WriterError> {
179        let free_space = self.free_space();
180
181        // find the latest service with this number that can fit this code
182        let Some(existing) = self.last_service_that_fits_code(service_no, &code) else {
183            return self.push_new_service_with_code(service_no, code);
184        };
185        Self::push_code_existing_service(existing, code, free_space)
186    }
187
188    /// Push a [`Code`](tables::Code) into this [`DTVCCPacket`] without creating a new [`Service`]
189    /// on overflow.
190    ///
191    /// Will try to push the [`Code`](tables::Code) into the last already existing [`Service`] with
192    /// `service_no` within the packet.
193    ///
194    /// Returns an error if the [`Service`] will not accomodate the [`Code`](tables::Code).
195    ///
196    /// # Panics
197    ///
198    /// * if service_no >= 64
199    pub fn push_code_into_single_service(
200        &mut self,
201        service_no: u8,
202        code: tables::Code,
203    ) -> Result<(), WriterError> {
204        let free_space = self.free_space();
205
206        // find the latest service with this number that can fit this code
207        let Some(existing) = self.mut_service_by_number(service_no) else {
208            return self.push_new_service_with_code(service_no, code);
209        };
210        Self::push_code_existing_service(existing, code, free_space)
211    }
212
213    pub(crate) fn parse_hdr_byte(byte: u8) -> (u8, usize) {
214        let seq_no = (byte & 0xC0) >> 6;
215        let len = byte & 0x3F;
216        let len = if len == 0 {
217            127usize
218        } else {
219            ((len as usize) * 2) - 1
220        };
221        (seq_no, len)
222    }
223
224    /// Parse bytes into a [DTVCCPacket]
225    ///
226    /// Will return [ParserError::LengthMismatch] if the data is shorter than the length advertised in
227    /// the [DTVCCPacket] header.
228    ///
229    /// Will return errors from [Service::parse] if parsing the contained [Service]s fails.
230    ///
231    /// # Examples
232    /// ```
233    /// # use cea708_types::{*, tables::*};
234    /// let data = [0x02, 0x21, 0x41, 0x00];
235    /// let packet = DTVCCPacket::parse(&data).unwrap();
236    /// assert_eq!(3, packet.len());
237    /// assert_eq!(0, packet.sequence_no());
238    /// ```
239    pub fn parse(data: &[u8]) -> Result<Self, ParserError> {
240        if data.is_empty() {
241            return Err(ParserError::LengthMismatch {
242                expected: 1,
243                actual: 0,
244            });
245        }
246        let (seq_no, len) = Self::parse_hdr_byte(data[0]);
247        trace!(
248            "dtvcc seq:{seq_no} len {len} data {data_len}",
249            data_len = data.len()
250        );
251        if (len + 1) < data.len() {
252            return Err(ParserError::LengthMismatch {
253                expected: len + 1,
254                actual: data.len(),
255            });
256        }
257
258        let mut offset = 1;
259        let mut services = vec![];
260        while offset < data.len() {
261            let service = Service::parse(&data[offset..])?;
262            trace!("parsed service {service:?}, len:{}", service.len());
263            if service.is_empty() {
264                offset += 1;
265                continue;
266            }
267            offset += service.len();
268            services.push(service);
269        }
270        Ok(Self { seq_no, services })
271    }
272
273    /// The [Service]s for this [DTVCCPacket]
274    pub fn services(&self) -> &[Service] {
275        &self.services
276    }
277
278    pub(crate) fn cc_count(&self) -> usize {
279        (self.len() + 1) / 2
280    }
281
282    fn hdr_byte(&self) -> u8 {
283        debug_assert!(self.len() <= 128);
284        (self.seq_no & 0x3) << 6 | (self.cc_count() & 0x3F) as u8
285    }
286
287    /// Write the [DTVCCPacket] to a byte stream
288    ///
289    /// # Examples
290    /// ```
291    /// # use cea708_types::{*, tables::*};
292    /// let mut packet = DTVCCPacket::new(2);
293    /// let mut service = Service::new(1);
294    /// service.push_code(&Code::LatinCapitalA).unwrap();
295    /// packet.push_service(service);
296    /// let mut written = vec![];
297    /// packet.write(&mut written);
298    /// let expected = [0x82, 0x21, 0x41, 0x00];
299    /// assert_eq!(written, expected);
300    /// ```
301    pub fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
302        // TODO: fail if we would overrun max size
303        w.write_all(&[self.hdr_byte()])?;
304        for service in self.services.iter() {
305            service.write(w)?;
306        }
307        if self.len() % 2 == 1 {
308            w.write_all(&[0x00])?;
309        }
310        Ok(())
311    }
312
313    pub(crate) fn write_as_cc_data<W: std::io::Write>(
314        &self,
315        w: &mut W,
316    ) -> Result<(), std::io::Error> {
317        // TODO: fail if we would overrun max size
318        // TODO: handle framerate?
319        if self.services.is_empty() {
320            return Ok(());
321        }
322        let mut written = vec![];
323        for service in self.services.iter() {
324            service.write(&mut written)?;
325            trace!("wrote service {service:?}");
326        }
327        w.write_all(&[0xFF, self.hdr_byte(), written[0]])?;
328        for pair in written[1..].chunks(2) {
329            let cc_valid = 0x04;
330            let cc_type = 0b10;
331            let reserved = 0xF8;
332            w.write_all(&[reserved | cc_valid | cc_type])?;
333            w.write_all(pair)?;
334            if pair.len() == 1 {
335                w.write_all(&[0x00])?;
336            }
337        }
338        Ok(())
339    }
340}
341
342/// A [Service] in a [DTVCCPacket]
343///
344/// As specified in CEA-708, there can be a maximum of 63 services.  Service 1 is the primary
345/// caption service and Service 2 is the secondary caption service.  All other services are
346/// undefined.
347#[derive(Debug, Clone)]
348pub struct Service {
349    number: u8,
350    codes: Vec<tables::Code>,
351}
352
353impl Service {
354    /// Create a new [Service]
355    ///
356    /// # Panics
357    ///
358    /// * if number >= 64
359    pub fn new(service_no: u8) -> Self {
360        if service_no >= 64 {
361            panic!("Service numbers must be between 0 and 63 inclusive, not {service_no}");
362        }
363        Self {
364            number: service_no,
365            codes: vec![],
366        }
367    }
368
369    /// Returns the number of this [Service]
370    ///
371    /// # Examples
372    /// ```
373    /// # use cea708_types::{*, tables::*};
374    /// let mut service = Service::new(1);
375    /// assert_eq!(service.number(), 1);
376    /// ```
377    pub fn number(&self) -> u8 {
378        self.number
379    }
380
381    fn codes_len(&self) -> usize {
382        self.codes.iter().map(|c| c.byte_len()).sum()
383    }
384
385    /// The amount of free space (in bytes) that can by placed inside this [Service] block
386    ///
387    /// # Examples
388    /// ```
389    /// # use cea708_types::{*, tables::*};
390    /// let service = Service::new(1);
391    /// assert_eq!(service.free_space(), 31);
392    /// ```
393    pub fn free_space(&self) -> usize {
394        // 31 is the maximum size of a service block
395        31 - self.codes_len()
396    }
397
398    /// The length in bytes of this [Service] block
399    ///
400    /// # Examples
401    /// ```
402    /// # use cea708_types::{*, tables::*};
403    /// let mut service = Service::new(1);
404    /// assert_eq!(service.len(), 0);
405    /// service.push_code(&Code::LatinCapitalA).unwrap();
406    /// assert_eq!(service.len(), 2);
407    /// service.push_code(&Code::LatinCapitalB).unwrap();
408    /// assert_eq!(service.len(), 3);
409    /// ```
410    pub fn len(&self) -> usize {
411        if self.number == 0 {
412            return 0;
413        }
414        if self.codes.is_empty() {
415            return 0;
416        }
417        let hdr_size = if self.number >= 7 { 2 } else { 1 };
418        hdr_size + self.codes_len()
419    }
420
421    /// Whether this [Service] block contains no [Code](tables::Code)s.
422    ///
423    /// # Examples
424    /// ```
425    /// # use cea708_types::Service;
426    /// let service = Service::new(1);
427    /// assert_eq!(service.len(), 0);
428    /// assert!(service.is_empty());
429    /// ```
430    pub fn is_empty(&self) -> bool {
431        self.codes.is_empty()
432    }
433
434    /// Push a [tables::Code] to the end of this [Service]
435    ///
436    /// # Errors
437    ///
438    /// * [WriterError::ReadOnly] if [Service] is number 0 (called the NULL Service)
439    /// * [WriterError::WouldOverflow] if adding the [tables::Code] would cause to [Service] to overflow
440    ///
441    /// # Examples
442    /// ```
443    /// # use cea708_types::{*, tables::*};
444    /// let mut service = Service::new(1);
445    /// service.push_code(&Code::LatinCapitalA).unwrap();
446    /// ```
447    pub fn push_code(&mut self, code: &tables::Code) -> Result<(), WriterError> {
448        // TODO: errors?
449        if self.number == 0 {
450            return Err(WriterError::ReadOnly);
451        }
452
453        if code.byte_len() > self.free_space() {
454            let overflow_bytes = code.byte_len() - self.free_space();
455            debug!("pushing would overflow by {overflow_bytes} bytes");
456            return Err(WriterError::WouldOverflow(overflow_bytes));
457        }
458        trace!("pushing {code:?}");
459        self.codes.push(code.clone());
460        Ok(())
461    }
462
463    /// Parse a [Service] from a set of bytes
464    ///
465    /// # Errors
466    ///
467    /// * [ParserError::LengthMismatch] if the length of the data is less than the size advertised in the
468    ///   header
469    ///
470    /// # Examples
471    /// ```
472    /// # use cea708_types::{*, tables::*};
473    /// let bytes = [0x21, 0x41];
474    /// let service = Service::parse(&bytes).unwrap();
475    /// assert_eq!(service.number(), 1);
476    /// assert_eq!(service.codes()[0], Code::LatinCapitalA);
477    /// ```
478    pub fn parse(data: &[u8]) -> Result<Self, ParserError> {
479        if data.is_empty() {
480            return Err(ParserError::LengthMismatch {
481                expected: 1,
482                actual: 0,
483            });
484        }
485        let byte = data[0];
486        let mut service_no = (byte & 0xE0) >> 5;
487        let block_size = (byte & 0x1F) as usize;
488        let mut idx = 1;
489        if service_no == 7 && block_size != 0 {
490            if data.len() == 1 {
491                return Err(ParserError::LengthMismatch {
492                    expected: 2,
493                    actual: data.len(),
494                });
495            }
496            let byte2 = data[1];
497            service_no = byte2 & 0x3F;
498            idx += 1;
499        }
500        trace!("service no: {service_no}, block_size: {block_size}");
501
502        if data.len() < idx + block_size {
503            return Err(ParserError::LengthMismatch {
504                expected: idx + block_size,
505                actual: data.len(),
506            });
507        }
508
509        if service_no != 0 {
510            Ok(Self {
511                number: service_no,
512                codes: tables::Code::from_data(&data[idx..idx + block_size])?,
513            })
514        } else {
515            Ok(Self {
516                number: 0,
517                codes: vec![],
518            })
519        }
520    }
521
522    /// The ordered list of [tables::Code]s present in this [Service] block
523    ///
524    /// # Examples
525    /// ```
526    /// # use cea708_types::{*, tables::*};
527    /// let mut service = Service::new(1);
528    /// service.push_code(&Code::LatinCapitalA).unwrap();
529    /// let codes = service.codes();
530    /// assert_eq!(codes, [Code::LatinCapitalA]);
531    /// ```
532    pub fn codes(&self) -> &[tables::Code] {
533        &self.codes
534    }
535
536    /// Write the [Service] block to a byte stream
537    ///
538    /// # Examples
539    /// ```
540    /// # use cea708_types::{*, tables::*};
541    /// let mut service = Service::new(1);
542    /// service.push_code(&Code::LatinCapitalA).unwrap();
543    /// let mut written = vec![];
544    /// service.write(&mut written);
545    /// let expected = [0x21, 0x41];
546    /// assert_eq!(written, expected);
547    /// ```
548    pub fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
549        // TODO: fail if we would overrun max size
550        let len = (self.codes_len() & 0x3F) as u8;
551        if self.number >= 7 {
552            let mut buf = [0; 2];
553            buf[0] = 0xE0 | len;
554            buf[1] = self.number;
555            w.write_all(&buf)?;
556        } else {
557            let byte = (self.number & 0x7) << 5 | len;
558            w.write_all(&[byte])?;
559        }
560        for code in self.codes.iter() {
561            code.write(w)?;
562        }
563        Ok(())
564    }
565}
566
567#[cfg(test)]
568mod test {
569    use super::*;
570    use crate::tests::*;
571
572    #[test]
573    fn simple_parse_dtvcc() {
574        test_init_log();
575        let data = [0x02, 0x01 << 5 | 0x01, 0x2A];
576        let dtvcc = DTVCCPacket::parse(&data).unwrap();
577        let services = dtvcc.services();
578        assert_eq!(services.len(), 1);
579        for service in services.iter() {
580            assert_eq!(service.number, 1);
581            let codes = service.codes();
582            for code in codes.iter() {
583                trace!("parsed {code:?}");
584            }
585        }
586    }
587
588    #[test]
589    fn simple_write_dtvcc() {
590        test_init_log();
591        let mut service = Service::new(1);
592        let code = tables::Code::Asterisk;
593        service.push_code(&code).unwrap();
594        let mut dtvcc = DTVCCPacket::new(0);
595        dtvcc.push_service(service).unwrap();
596        let mut written = vec![];
597        dtvcc.write(&mut written).unwrap();
598        let data = [0x02, 0x01 << 5 | 0x01, 0x2A, 0x00];
599        assert_eq!(written, data);
600    }
601
602    #[test]
603    fn service_numbers() {
604        test_init_log();
605        for i in 1..64 {
606            let mut service = Service::new(i);
607            let code = tables::Code::Asterisk;
608            service.push_code(&code).unwrap();
609            let mut output = vec![];
610            service.write(&mut output).unwrap();
611            log::info!("created service {i} with data {output:x?}");
612            let parsed = Service::parse(&output).unwrap();
613            assert_eq!(service.number(), parsed.number());
614            assert_eq!(service.codes(), &[code]);
615        }
616    }
617
618    #[test]
619    fn write_full_packet_same_service_no() {
620        test_init_log();
621        let mut packet = DTVCCPacket::new(0);
622        while packet.free_space() > 0 {
623            packet.push_code(1, tables::Code::LatinLowerA).unwrap();
624        }
625        let codes = packet
626            .services()
627            .iter()
628            .flat_map(|service| service.codes())
629            .collect::<Vec<_>>();
630        assert_eq!(codes.len(), 123);
631    }
632
633    #[test]
634    fn write_packet_single_service() {
635        test_init_log();
636        let mut packet = DTVCCPacket::new(0);
637        let mut service = Service::new(2);
638        while service.free_space() > 0 {
639            packet
640                .push_code_into_single_service(1, tables::Code::LatinLowerA)
641                .unwrap();
642            service.push_code(&tables::Code::LatinLowerA).unwrap();
643        }
644        let packet_codes = packet
645            .services()
646            .iter()
647            .flat_map(|service| service.codes().iter().cloned())
648            .collect::<Vec<_>>();
649        let service_codes = service.codes().to_vec();
650        assert_eq!(packet_codes.len(), 31);
651        assert_eq!(packet_codes, service_codes);
652    }
653}