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}