1use crate::error::{Error, Result};
4use alloc::vec::Vec;
5use dvb_common::{Parse, Serialize};
6
7const MAX_CC_COUNT: usize = 31;
9const FF: u8 = 0xFF;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize))]
16#[non_exhaustive]
17pub enum CcType {
18 Ntsc608Field1,
20 Ntsc608Field2,
22 Dtvcc708Data,
24 Dtvcc708Start,
26}
27
28impl CcType {
29 #[must_use]
31 pub fn from_bits(v: u8) -> Self {
32 match v & 0x03 {
33 0 => Self::Ntsc608Field1,
34 1 => Self::Ntsc608Field2,
35 2 => Self::Dtvcc708Data,
36 _ => Self::Dtvcc708Start,
37 }
38 }
39 #[must_use]
41 pub fn to_bits(self) -> u8 {
42 match self {
43 Self::Ntsc608Field1 => 0,
44 Self::Ntsc608Field2 => 1,
45 Self::Dtvcc708Data => 2,
46 Self::Dtvcc708Start => 3,
47 }
48 }
49 #[must_use]
51 pub fn is_cea608(self) -> bool {
52 matches!(self, Self::Ntsc608Field1 | Self::Ntsc608Field2)
53 }
54 #[must_use]
56 pub fn is_cea708(self) -> bool {
57 matches!(self, Self::Dtvcc708Data | Self::Dtvcc708Start)
58 }
59 #[must_use]
61 pub fn name(&self) -> &'static str {
62 match self {
63 Self::Ntsc608Field1 => "ntsc_608_field1",
64 Self::Ntsc608Field2 => "ntsc_608_field2",
65 Self::Dtvcc708Data => "dtvcc_708_data",
66 Self::Dtvcc708Start => "dtvcc_708_start",
67 }
68 }
69}
70
71dvb_common::impl_spec_display!(CcType);
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75#[cfg_attr(feature = "serde", derive(serde::Serialize))]
76pub struct CcTriplet {
77 pub cc_valid: bool,
79 pub cc_type: CcType,
81 pub cc_data_1: u8,
83 pub cc_data_2: u8,
85}
86
87#[derive(Debug, Clone, PartialEq, Eq)]
89#[cfg_attr(feature = "serde", derive(serde::Serialize))]
90pub struct CcData {
91 pub process_cc_data_flag: bool,
93 pub triplets: Vec<CcTriplet>,
95}
96
97impl CcData {
98 pub fn cea608(&self) -> impl Iterator<Item = &CcTriplet> {
100 self.triplets.iter().filter(|t| t.cc_type.is_cea608())
101 }
102 pub fn cea708(&self) -> impl Iterator<Item = &CcTriplet> {
104 self.triplets.iter().filter(|t| t.cc_type.is_cea708())
105 }
106}
107
108impl<'a> Parse<'a> for CcData {
109 type Error = Error;
110 fn parse(b: &'a [u8]) -> Result<Self> {
111 if b.len() < 2 {
114 return Err(Error::BufferTooShort {
115 need: 2,
116 have: b.len(),
117 what: "cc_data header",
118 });
119 }
120 let process_cc_data_flag = (b[0] >> 6) & 0x01 != 0;
121 let cc_count = usize::from(b[0] & 0x1F);
122 let total = 2 + cc_count * 3 + 1; if b.len() < total {
124 return Err(Error::BufferTooShort {
125 need: total,
126 have: b.len(),
127 what: "cc_data triplets",
128 });
129 }
130 let mut triplets = Vec::with_capacity(cc_count);
131 let mut pos = 2;
132 for _ in 0..cc_count {
133 let flags = b[pos];
134 triplets.push(CcTriplet {
136 cc_valid: (flags >> 2) & 0x01 != 0,
137 cc_type: CcType::from_bits(flags),
138 cc_data_1: b[pos + 1],
139 cc_data_2: b[pos + 2],
140 });
141 pos += 3;
142 }
143 Ok(CcData {
144 process_cc_data_flag,
145 triplets,
146 })
147 }
148}
149
150impl Serialize for CcData {
151 type Error = Error;
152 fn serialized_len(&self) -> usize {
153 2 + self.triplets.len() * 3 + 1
154 }
155 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
156 if self.triplets.len() > MAX_CC_COUNT {
157 return Err(Error::TooManyTriplets(self.triplets.len()));
158 }
159 let len = self.serialized_len();
160 if buf.len() < len {
161 return Err(Error::OutputBufferTooSmall {
162 need: len,
163 have: buf.len(),
164 });
165 }
166 let cc_count = self.triplets.len() as u8 & 0x1F;
168 buf[0] = 0x80 | (u8::from(self.process_cc_data_flag) << 6) | cc_count;
169 buf[1] = FF;
170 let mut pos = 2;
171 for t in &self.triplets {
172 buf[pos] = 0xF8 | (u8::from(t.cc_valid) << 2) | t.cc_type.to_bits();
174 buf[pos + 1] = t.cc_data_1;
175 buf[pos + 2] = t.cc_data_2;
176 pos += 3;
177 }
178 buf[pos] = FF; Ok(len)
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186
187 fn tr(cc_valid: bool, cc_type: CcType, d1: u8, d2: u8) -> CcTriplet {
188 CcTriplet {
189 cc_valid,
190 cc_type,
191 cc_data_1: d1,
192 cc_data_2: d2,
193 }
194 }
195
196 fn sample() -> CcData {
197 CcData {
198 process_cc_data_flag: true,
199 triplets: alloc::vec![
200 tr(true, CcType::Dtvcc708Start, 0xC1, 0x02),
201 tr(true, CcType::Ntsc608Field1, 0x94, 0x2C),
202 tr(false, CcType::Dtvcc708Data, 0x00, 0x00),
203 ],
204 }
205 }
206
207 #[test]
208 fn round_trip_constructed() {
209 let cc = sample();
210 let bytes = cc.to_bytes();
211 assert_eq!(CcData::parse(&bytes).unwrap(), cc);
213 assert_eq!(bytes[0], 0b1100_0011);
215 assert_eq!(bytes[1], 0xFF);
216 assert_eq!(*bytes.last().unwrap(), 0xFF);
217 }
218
219 #[test]
220 fn mutate_field_changes_output() {
221 let a = sample().to_bytes();
222 let mut cc = sample();
223 cc.triplets[0].cc_data_1 = 0x42;
224 let b = cc.to_bytes();
225 assert_ne!(a, b, "mutating a field must change serialized bytes");
226 }
227
228 #[test]
229 fn splits_608_708() {
230 let cc = sample();
231 assert_eq!(cc.cea608().count(), 1);
232 assert_eq!(cc.cea708().count(), 2);
233 }
234
235 #[test]
236 fn empty_round_trip() {
237 let cc = CcData {
238 process_cc_data_flag: false,
239 triplets: alloc::vec![],
240 };
241 let bytes = cc.to_bytes();
242 assert_eq!(bytes, [0x80, 0xFF, 0xFF]); assert_eq!(CcData::parse(&bytes).unwrap(), cc);
244 }
245
246 #[test]
247 fn serialize_rejects_over_31_triplets() {
248 let cc = CcData {
249 process_cc_data_flag: true,
250 triplets: alloc::vec![tr(true, CcType::Ntsc608Field1, 0, 0); 32],
251 };
252 let mut buf = [0u8; 200];
253 assert!(matches!(
254 cc.serialize_into(&mut buf),
255 Err(Error::TooManyTriplets(32))
256 ));
257 }
258}