cea708_types/
lib.rs

1// Copyright (C) 2023 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
7//! # cea708-types
8//!
9//! Provides the necessary infrastructure to read and write [DTVCCPacket]'s containing [Service]s
10//! with various [tables::Code]s
11//!
12//! The reference for this implementation is the [ANSI/CTA-708-E R-2018](https://shop.cta.tech/products/digital-television-dtv-closed-captioning) specification.
13
14use muldiv::MulDiv;
15
16mod packet;
17mod parser;
18pub mod tables;
19mod writer;
20
21/// A CEA-608 compatibility byte pair
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum Cea608 {
24    Field1(u8, u8),
25    Field2(u8, u8),
26}
27
28/// A framerate.  Framerates larger than 60fps are not well supported.
29#[derive(Debug, Copy, Clone)]
30pub struct Framerate {
31    numer: u32,
32    denom: u32,
33}
34
35impl Framerate {
36    /// Create a new [`Framerate`]
37    pub const fn new(numer: u32, denom: u32) -> Self {
38        Self { numer, denom }
39    }
40
41    /// The numerator of this [`Framerate`] fraction
42    pub fn numer(&self) -> u32 {
43        self.numer
44    }
45
46    /// The denominator of this [`Framerate`] fraction
47    pub fn denom(&self) -> u32 {
48        self.denom
49    }
50
51    fn cea608_pairs_per_frame(&self) -> usize {
52        // CEA-608 has a max bitrate of 960 bits/s for a single field
53        // TODO: handle alternating counts for 24fps
54        60.mul_div_round(self.denom, self.numer).unwrap() as usize
55    }
56
57    fn max_cc_count(&self) -> usize {
58        // CEA-708 has a max bitrate of 9_600 bits/s
59        600.mul_div_round(self.denom, self.numer).unwrap() as usize
60    }
61}
62
63pub use packet::{DTVCCPacket, Service};
64pub use parser::{CCDataParser, ParserError};
65pub use writer::{CCDataWriter, WriterError};
66
67#[cfg(test)]
68mod test {
69    use super::*;
70    use crate::tests::*;
71
72    #[test]
73    fn framerate_cea608_pairs_per_frame() {
74        test_init_log();
75        assert_eq!(Framerate::new(60, 1).cea608_pairs_per_frame(), 1);
76        assert_eq!(Framerate::new(30, 1).cea608_pairs_per_frame(), 2);
77    }
78
79    #[test]
80    fn framerate_max_cc_count() {
81        test_init_log();
82        assert_eq!(Framerate::new(60, 1).max_cc_count(), 10);
83        assert_eq!(Framerate::new(30, 1).max_cc_count(), 20);
84    }
85
86    #[test]
87    fn framerate_new() {
88        test_init_log();
89        let fps = Framerate::new(30, 8);
90        assert_eq!(fps.numer(), 30);
91        assert_eq!(fps.denom(), 8);
92    }
93}
94
95#[cfg(test)]
96pub(crate) mod tests {
97    use std::sync::OnceLock;
98
99    static TRACING: OnceLock<()> = OnceLock::new();
100
101    pub fn test_init_log() {
102        TRACING.get_or_init(|| {
103            env_logger::init();
104        });
105    }
106}