1use std::collections::VecDeque;
15use std::time::Duration;
16
17use muldiv::MulDiv;
18
19use log::{debug, trace, warn};
20
21pub mod tables;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
25pub enum ParserError {
26 #[error("The length of the data ({actual}) does not match the advertised expected ({expected}) length")]
28 LengthMismatch {
29 expected: usize,
31 actual: usize,
33 },
34 #[error("CEA-608 compatibility bytes were found after CEA-708 bytes at position {byte_pos}")]
36 Cea608AfterCea708 {
37 byte_pos: usize,
39 },
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
44pub enum WriterError {
45 #[error("Writing the data would overflow by {0} bytes")]
47 WouldOverflow(usize),
48 #[error("The resource is not writable")]
50 ReadOnly,
51}
52
53impl From<tables::CodeError> for ParserError {
54 fn from(err: tables::CodeError) -> Self {
55 match err {
56 tables::CodeError::LengthMismatch { expected, actual } => {
57 ParserError::LengthMismatch { expected, actual }
58 }
59 }
60 }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum Cea608 {
66 Field1(u8, u8),
67 Field2(u8, u8),
68}
69
70#[derive(Debug, Default)]
72pub struct CCDataParser {
73 pending_data: Vec<u8>,
74 packets: VecDeque<DTVCCPacket>,
75 cea608: Option<Vec<Cea608>>,
76 have_initial_ccp_header: bool,
77 ccp_bytes_needed: usize,
78}
79
80impl CCDataParser {
81 pub fn new() -> Self {
83 Self::default()
84 }
85
86 pub fn handle_cea608(&mut self) {
87 self.cea608 = Some(vec![]);
88 }
89
90 pub fn push(&mut self, data: &[u8]) -> Result<(), ParserError> {
98 trace!("parsing {data:?}");
99 if let Some(ref mut cea608) = self.cea608 {
100 cea608.clear();
101 }
102
103 if data.len() < 5 {
104 return Ok(());
106 }
107 let process_cc_data_flag = data[0] & 0x40 > 0;
108 if !process_cc_data_flag {
109 return Ok(());
110 }
111
112 let cc_count = data[0] & 0x1F;
113 if cc_count == 0 {
114 return Ok(());
115 }
116 trace!("cc_count: {cc_count}, len = {}", data.len());
117 if (cc_count * 3 + 2) as usize != data.len() {
118 return Err(ParserError::LengthMismatch {
119 expected: (cc_count * 3 + 1) as usize,
120 actual: data.len(),
121 });
122 }
123
124 let mut ccp_data = vec![];
125 let mut in_dtvcc = false;
126
127 let mut pending_data = vec![];
129 for (i, d) in self.pending_data.chunks(2).enumerate() {
130 if i == 0 {
131 pending_data.push(0xFF);
132 } else {
133 pending_data.push(0xFE);
134 }
135 pending_data.extend(d);
136 if d.len() == 1 {
137 pending_data.push(0x00);
138 }
139 }
140
141 let ccp_offset;
143 {
144 let mut ret = None;
145 for (i, triple) in data[2..].chunks_exact(3).enumerate() {
146 let cc_valid = (triple[0] & 0x04) == 0x04;
147 let cc_type = triple[0] & 0x3;
148 trace!(
149 "byte:{} triple 0x{:02x} 0x{:02x} 0x{:02x}. valid: {cc_valid}, type: {cc_type}",
150 i * 3,
151 triple[0],
152 triple[1],
153 triple[2]
154 );
155 if (cc_type & 0b10) > 0 {
156 in_dtvcc = true;
157 }
158 if !cc_valid {
159 continue;
160 }
161 if !in_dtvcc && (cc_type == 0b00 || cc_type == 0b01) {
162 trace!(
163 "have cea608 bytes type {cc_type} 0x{:02x} 0x{:02x}",
164 triple[1],
165 triple[2]
166 );
167 if let Some(ref mut cea608) = self.cea608 {
168 let pair = match cc_type {
169 0b00 => Cea608::Field1(triple[1], triple[2]),
170 0b01 => Cea608::Field2(triple[1], triple[2]),
171 _ => unreachable!(),
172 };
173 cea608.push(pair);
174 }
175 continue;
176 }
177
178 if in_dtvcc && (cc_type == 0b00 || cc_type == 0b01) {
179 warn!("cea608 bytes after cea708 data at byte:{}", i * 3);
181 return Err(ParserError::Cea608AfterCea708 { byte_pos: i * 3 });
182 }
183
184 if ret.is_none() {
185 ret = Some(i * 3);
186 }
187 }
188
189 if let Some(ret) = ret {
190 ccp_offset = 2 + ret
191 } else {
192 return Ok(());
194 }
195 }
196 trace!("ccp offset in input data is at index {ccp_offset}");
197
198 let mut data_iter = pending_data.iter().chain(data[ccp_offset..].iter());
199 let mut i = 0;
200 in_dtvcc = false;
201 loop {
202 let byte0 = data_iter.next();
203 let byte1 = data_iter.next();
204 let byte2 = data_iter.next();
205 i += 3;
206 let (Some(byte0), Some(byte1), Some(byte2)) = (byte0, byte1, byte2) else {
207 break;
208 };
209 let cc_valid = (byte0 & 0x04) == 0x04;
210 let cc_type = byte0 & 0x3;
211 if (cc_type & 0b10) > 0 {
212 in_dtvcc = true;
213 }
214 if !cc_valid {
215 continue;
216 }
217 if !in_dtvcc && (cc_type == 0b00 || cc_type == 0b01) {
218 unreachable!();
220 }
221
222 if (cc_type & 0b11) == 0b11 {
223 trace!("found ccp header at index {}", i - 3);
224 self.have_initial_ccp_header = true;
225 match DTVCCPacket::parse(&ccp_data) {
227 Ok(packet) => self.packets.push_front(packet),
228 Err(ParserError::LengthMismatch { .. }) => (),
229 Err(e) => {
230 eprintln!("{e:?}");
231 unreachable!()
232 }
233 }
234 in_dtvcc = false;
235 ccp_data = vec![];
236 let (_seq_no, packet_len) = DTVCCPacket::parse_hdr_byte(*byte1);
237 trace!("waiting for {} dtvcc bytes", packet_len + 1);
238 self.ccp_bytes_needed = packet_len + 1;
239 }
240
241 if self.have_initial_ccp_header {
242 trace!("pushing 0x{:02x?}{:02x?}", byte1, byte2);
243 if self.ccp_bytes_needed > 0 {
244 ccp_data.push(*byte1);
245 self.ccp_bytes_needed -= 1;
246 }
247 if self.ccp_bytes_needed > 0 {
248 ccp_data.push(*byte2);
249 self.ccp_bytes_needed -= 1;
250 }
251 }
252 }
253
254 if self.ccp_bytes_needed == 0 {
255 match DTVCCPacket::parse(&ccp_data) {
256 Ok(packet) => self.packets.push_front(packet),
257 Err(ParserError::LengthMismatch { .. }) => (),
258 _ => unreachable!(),
259 }
260 ccp_data = vec![];
261 }
262
263 self.pending_data = ccp_data;
264
265 Ok(())
266 }
267
268 pub fn flush(&mut self) {
270 *self = Self::default();
271 }
272
273 pub fn pop_packet(&mut self) -> Option<DTVCCPacket> {
275 let ret = self.packets.pop_back();
276 trace!("popped {ret:?}");
277 ret
278 }
279
280 pub fn cea608(&mut self) -> Option<&[Cea608]> {
282 self.cea608.as_deref()
283 }
284}
285
286#[derive(Debug, Copy, Clone)]
288pub struct Framerate {
289 numer: u32,
290 denom: u32,
291}
292
293impl Framerate {
294 pub const fn new(numer: u32, denom: u32) -> Self {
296 Self { numer, denom }
297 }
298
299 pub fn numer(&self) -> u32 {
301 self.numer
302 }
303
304 pub fn denom(&self) -> u32 {
306 self.denom
307 }
308
309 fn cea608_pairs_per_frame(&self) -> usize {
310 60.mul_div_round(self.denom, self.numer).unwrap() as usize
313 }
314
315 fn max_cc_count(&self) -> usize {
316 600.mul_div_round(self.denom, self.numer).unwrap() as usize
318 }
319}
320
321#[derive(Debug, Default)]
323pub struct CCDataWriter {
324 output_cea608_padding: bool,
326 output_padding: bool,
327 packets: VecDeque<DTVCCPacket>,
329 pending_packet_data: Vec<u8>,
331 cea608_1: VecDeque<(u8, u8)>,
332 cea608_2: VecDeque<(u8, u8)>,
333 last_cea608_was_field1: bool,
334}
335
336impl CCDataWriter {
337 pub fn set_output_cea608_padding(&mut self, output_cea608_padding: bool) {
339 self.output_cea608_padding = output_cea608_padding;
340 }
341
342 pub fn output_cea608_padding(&self) -> bool {
344 self.output_cea608_padding
345 }
346
347 pub fn set_output_padding(&mut self, output_padding: bool) {
349 self.output_padding = output_padding;
350 }
351
352 pub fn output_padding(&self) -> bool {
354 self.output_padding
355 }
356
357 pub fn push_packet(&mut self, packet: DTVCCPacket) {
359 self.packets.push_front(packet)
360 }
361
362 pub fn push_cea608(&mut self, cea608: Cea608) {
364 match cea608 {
365 Cea608::Field1(byte0, byte1) => {
366 if byte0 != 0x80 || byte1 != 0x80 {
367 self.cea608_1.push_front((byte0, byte1))
368 }
369 }
370 Cea608::Field2(byte0, byte1) => {
371 if byte0 != 0x80 || byte1 != 0x80 {
372 self.cea608_2.push_front((byte0, byte1))
373 }
374 }
375 }
376 }
377
378 pub fn flush(&mut self) {
380 self.packets.clear();
381 self.pending_packet_data.clear();
382 self.cea608_1.clear();
383 self.cea608_2.clear();
384 }
385
386 pub fn buffered_cea608_field1_duration(&self) -> Duration {
388 Duration::from_micros(
390 (self.cea608_1.len() as u64)
391 .mul_div_ceil(1001 * 1_000_000, 60000)
392 .unwrap(),
393 )
394 }
395
396 pub fn buffered_cea608_field2_duration(&self) -> Duration {
398 Duration::from_micros(
400 (self.cea608_2.len() as u64)
401 .mul_div_ceil(1001 * 1_000_000, 60000)
402 .unwrap(),
403 )
404 }
405
406 fn buffered_packet_bytes(&self) -> usize {
407 self.pending_packet_data.len()
408 + self
409 .packets
410 .iter()
411 .map(|packet| packet.len())
412 .sum::<usize>()
413 }
414
415 pub fn buffered_packet_duration(&self) -> Duration {
417 Duration::from_micros(
419 ((self.buffered_packet_bytes() + 1) as u64 / 2)
420 .mul_div_ceil(2 * 1001 * 1_000_000, 9_600_000 / 8)
421 .unwrap(),
422 )
423 }
424
425 pub fn write<W: std::io::Write>(
428 &mut self,
429 framerate: Framerate,
430 w: &mut W,
431 ) -> Result<(), std::io::Error> {
432 let mut cea608_pair_rem = if self.output_cea608_padding {
433 framerate.cea608_pairs_per_frame()
434 } else {
435 framerate
436 .cea608_pairs_per_frame()
437 .min(self.cea608_1.len().max(self.cea608_2.len() * 2))
438 };
439
440 let mut cc_count_rem = if self.output_padding {
441 framerate.max_cc_count()
442 } else {
443 framerate.max_cc_count().min(
444 cea608_pair_rem
445 + self.pending_packet_data.len() / 3
446 + self.packets.iter().map(|p| p.cc_count()).sum::<usize>(),
447 )
448 };
449 trace!("writing with cc_count: {cc_count_rem} and {cea608_pair_rem} cea608 pairs");
450
451 let reserved = 0x80;
452 let process_cc_flag = 0x40;
453 w.write_all(&[
454 reserved | process_cc_flag | (cc_count_rem & 0x1f) as u8,
455 0xFF,
456 ])?;
457 while cc_count_rem > 0 {
458 if cea608_pair_rem > 0 {
459 if !self.last_cea608_was_field1 {
460 trace!("attempting to write a cea608 byte pair from field 1");
461 if let Some((byte0, byte1)) = self.cea608_1.pop_back() {
462 w.write_all(&[0xFC, byte0, byte1])?;
463 cc_count_rem -= 1;
464 } else if !self.cea608_2.is_empty() {
465 w.write_all(&[0xFC, 0x80, 0x80])?;
467 cc_count_rem -= 1;
468 } else if self.output_cea608_padding {
469 w.write_all(&[0xF8, 0x80, 0x80])?;
470 cc_count_rem -= 1;
471 }
472 self.last_cea608_was_field1 = true;
473 } else {
474 trace!("attempting to write a cea608 byte pair from field 2");
475 if let Some((byte0, byte1)) = self.cea608_2.pop_back() {
476 w.write_all(&[0xFD, byte0, byte1])?;
477 cc_count_rem -= 1;
478 } else if self.output_cea608_padding {
479 w.write_all(&[0xF9, 0x80, 0x80])?;
480 cc_count_rem -= 1;
481 }
482 self.last_cea608_was_field1 = false;
483 }
484 cea608_pair_rem -= 1;
485 } else {
486 let mut current_packet_data = &mut self.pending_packet_data;
487 let mut packet_offset = 0;
488 while packet_offset >= current_packet_data.len() {
489 if let Some(packet) = self.packets.pop_back() {
490 trace!("starting packet {packet:?}");
491 packet.write_as_cc_data(&mut current_packet_data)?;
492 } else {
493 trace!("no packet to write");
494 break;
495 }
496 }
497
498 trace!("cea708 pending data length {}", current_packet_data.len(),);
499
500 while packet_offset < current_packet_data.len() && cc_count_rem > 0 {
501 assert!(current_packet_data.len() >= packet_offset + 3);
502 w.write_all(¤t_packet_data[packet_offset..packet_offset + 3])?;
503 packet_offset += 3;
504 cc_count_rem -= 1;
505 }
506
507 self.pending_packet_data = current_packet_data[packet_offset..].to_vec();
508
509 if self.packets.is_empty() && self.pending_packet_data.is_empty() {
510 if self.output_padding {
512 trace!("writing {cc_count_rem} padding bytes");
513 while cc_count_rem > 0 {
514 w.write_all(&[0xFA, 0x00, 0x00])?;
515 cc_count_rem -= 1;
516 }
517 }
518 break;
519 }
520 }
521 }
522 Ok(())
523 }
524}
525
526#[derive(Debug)]
528pub struct DTVCCPacket {
529 seq_no: u8,
530 services: Vec<Service>,
531}
532
533impl DTVCCPacket {
534 pub fn new(seq_no: u8) -> Self {
540 if seq_no > 3 {
541 panic!("DTVCCPacket sequence numbers must be between 0 and 3 inclusive, not {seq_no}");
542 }
543 Self {
544 seq_no,
545 services: vec![],
546 }
547 }
548
549 pub fn sequence_no(&self) -> u8 {
558 self.seq_no
559 }
560
561 pub fn free_space(&self) -> usize {
563 128 - self.len()
565 }
566
567 pub fn len(&self) -> usize {
580 let services_len = self.services.iter().map(|s| s.len()).sum::<usize>();
581 if services_len > 0 {
582 1 + services_len
583 } else {
584 0
585 }
586 }
587
588 pub fn push_service(&mut self, service: Service) -> Result<(), WriterError> {
601 if service.len() > self.free_space() {
603 return Err(WriterError::WouldOverflow(
604 service.len() - self.free_space(),
605 ));
606 }
607 self.services.push(service);
608 Ok(())
609 }
610
611 fn parse_hdr_byte(byte: u8) -> (u8, usize) {
612 let seq_no = (byte & 0xC0) >> 6;
613 let len = byte & 0x3F;
614 let len = if len == 0 {
615 127usize
616 } else {
617 ((len as usize) * 2) - 1
618 };
619 (seq_no, len)
620 }
621
622 pub fn parse(data: &[u8]) -> Result<Self, ParserError> {
638 if data.is_empty() {
639 return Err(ParserError::LengthMismatch {
640 expected: 1,
641 actual: 0,
642 });
643 }
644 let (seq_no, len) = Self::parse_hdr_byte(data[0]);
645 trace!(
646 "dtvcc seq:{seq_no} len {len} data {data_len}",
647 data_len = data.len()
648 );
649 if (len + 1) < data.len() {
650 return Err(ParserError::LengthMismatch {
651 expected: len + 1,
652 actual: data.len(),
653 });
654 }
655
656 let mut offset = 1;
657 let mut services = vec![];
658 while offset < data.len() {
659 let service = Service::parse(&data[offset..])?;
660 trace!("parsed service {service:?}, len:{}", service.len());
661 if service.len() == 0 {
662 offset += 1;
663 continue;
664 }
665 offset += service.len();
666 services.push(service);
667 }
668 Ok(Self { seq_no, services })
669 }
670
671 pub fn services(&self) -> &[Service] {
673 &self.services
674 }
675
676 fn cc_count(&self) -> usize {
677 (self.len() + 1) / 2
678 }
679
680 fn hdr_byte(&self) -> u8 {
681 let packet_size_code = if self.len() == 127 {
682 0
683 } else {
684 (self.len() + 1) / 2
685 };
686 (self.seq_no & 0x3) << 6 | packet_size_code as u8
687 }
688
689 pub fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
704 w.write_all(&[self.hdr_byte()])?;
706 for service in self.services.iter() {
707 service.write(w)?;
708 }
709 if self.len() % 2 == 1 {
710 w.write_all(&[0x00])?;
711 }
712 Ok(())
713 }
714
715 fn write_as_cc_data<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
716 if self.services.is_empty() {
719 return Ok(());
720 }
721 let mut written = vec![];
722 for service in self.services.iter() {
723 service.write(&mut written)?;
724 trace!("wrote service {service:?}");
725 }
726 w.write_all(&[0xFF, self.hdr_byte(), written[0]])?;
727 for pair in written[1..].chunks(2) {
728 let cc_valid = 0x04;
729 let cc_type = 0b10;
730 let reserved = 0xF8;
731 w.write_all(&[reserved | cc_valid | cc_type])?;
732 w.write_all(pair)?;
733 if pair.len() == 1 {
734 w.write_all(&[0x00])?;
735 }
736 }
737 Ok(())
738 }
739}
740
741#[derive(Debug, Clone)]
747pub struct Service {
748 number: u8,
749 codes: Vec<tables::Code>,
750}
751
752impl Service {
753 pub fn new(service_no: u8) -> Self {
759 if service_no >= 64 {
760 panic!("Service numbers must be between 0 and 63 inclusive, not {service_no}");
761 }
762 Self {
763 number: service_no,
764 codes: vec![],
765 }
766 }
767
768 pub fn number(&self) -> u8 {
777 self.number
778 }
779
780 fn codes_len(&self) -> usize {
781 self.codes.iter().map(|c| c.byte_len()).sum()
782 }
783
784 pub fn free_space(&self) -> usize {
793 31 - self.codes_len()
795 }
796
797 pub fn len(&self) -> usize {
810 if self.number == 0 {
811 return 0;
812 }
813 if self.codes.is_empty() {
814 return 0;
815 }
816 let hdr_size = if self.number >= 7 { 2 } else { 1 };
817 hdr_size + self.codes_len()
818 }
819
820 pub fn push_code(&mut self, code: &tables::Code) -> Result<(), WriterError> {
834 if self.number == 0 {
836 return Err(WriterError::ReadOnly);
837 }
838
839 if code.byte_len() > self.free_space() {
840 let overflow_bytes = code.byte_len() - self.free_space();
841 debug!("pushing would overflow by {overflow_bytes} bytes");
842 return Err(WriterError::WouldOverflow(overflow_bytes));
843 }
844 trace!("pushing {code:?}");
845 self.codes.push(code.clone());
846 Ok(())
847 }
848
849 pub fn parse(data: &[u8]) -> Result<Self, ParserError> {
865 if data.is_empty() {
866 return Err(ParserError::LengthMismatch {
867 expected: 1,
868 actual: 0,
869 });
870 }
871 let byte = data[0];
872 let mut service_no = (byte & 0xE0) >> 5;
873 let block_size = (byte & 0x1F) as usize;
874 let mut idx = 1;
875 trace!("block_size: {block_size}");
876 if service_no == 7 && block_size != 0 {
877 if data.len() == 1 {
878 return Err(ParserError::LengthMismatch {
879 expected: 2,
880 actual: data.len(),
881 });
882 }
883 let byte2 = data[1];
884 service_no = byte2 & 0x3F;
885 idx += 1;
886 }
887
888 if data.len() < idx + block_size {
889 return Err(ParserError::LengthMismatch {
890 expected: idx + block_size,
891 actual: data.len(),
892 });
893 }
894
895 if service_no != 0 {
896 Ok(Self {
897 number: service_no,
898 codes: tables::Code::from_data(&data[idx..idx + block_size])?,
899 })
900 } else {
901 Ok(Self {
902 number: 0,
903 codes: vec![],
904 })
905 }
906 }
907
908 pub fn codes(&self) -> &[tables::Code] {
919 &self.codes
920 }
921
922 pub fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
935 let len = (self.codes_len() & 0x3F) as u8;
937 if self.number > 7 {
938 let mut buf = [0; 2];
939 buf[0] = 0xC0 | len;
940 buf[1] = self.number;
941 w.write_all(&buf)?;
942 } else {
943 let byte = (self.number & 0x7) << 5 | len;
944 w.write_all(&[byte])?;
945 }
946 for code in self.codes.iter() {
947 code.write(w)?;
948 }
949 Ok(())
950 }
951}
952
953#[cfg(test)]
954mod test {
955 use super::*;
956 use crate::tests::*;
957
958 #[test]
959 fn simple_parse_dtvcc() {
960 test_init_log();
961 let data = [0x02, 0x01 << 5 | 0x01, 0x2A];
962 let dtvcc = DTVCCPacket::parse(&data).unwrap();
963 let services = dtvcc.services();
964 assert_eq!(services.len(), 1);
965 for service in services.iter() {
966 assert_eq!(service.number, 1);
967 let codes = service.codes();
968 for code in codes.iter() {
969 trace!("parsed {code:?}");
970 }
971 }
972 }
973
974 #[test]
975 fn simple_write_dtvcc() {
976 test_init_log();
977 let mut service = Service::new(1);
978 let code = tables::Code::Asterisk;
979 service.push_code(&code).unwrap();
980 let mut dtvcc = DTVCCPacket::new(0);
981 dtvcc.push_service(service).unwrap();
982 let mut written = vec![];
983 dtvcc.write(&mut written).unwrap();
984 let data = [0x02, 0x01 << 5 | 0x01, 0x2A, 0x00];
985 assert_eq!(written, data);
986 }
987
988 #[derive(Debug)]
989 struct ServiceData<'a> {
990 service_no: u8,
991 codes: &'a [tables::Code],
992 }
993
994 #[derive(Debug)]
995 struct PacketData<'a> {
996 sequence_no: u8,
997 services: &'a [ServiceData<'a>],
998 }
999
1000 #[derive(Debug)]
1001 struct TestCCData<'a> {
1002 framerate: Framerate,
1003 cc_data: &'a [&'a [u8]],
1004 packets: &'a [PacketData<'a>],
1005 cea608: &'a [&'a [Cea608]],
1006 }
1007
1008 static TEST_CC_DATA: [TestCCData; 8] = [
1009 TestCCData {
1011 framerate: Framerate::new(25, 1),
1012 cc_data: &[&[0x80 | 0x40 | 0x02, 0xFF, 0xFF, 0x02, 0x21, 0xFE, 0x41, 0x00]],
1013 packets: &[PacketData {
1014 sequence_no: 0,
1015 services: &[ServiceData {
1016 service_no: 1,
1017 codes: &[tables::Code::LatinCapitalA],
1018 }],
1019 }],
1020 cea608: &[],
1021 },
1022 TestCCData {
1024 framerate: Framerate::new(25, 1),
1025 cc_data: &[&[0x80 | 0x40 | 0x02, 0xFF, 0xFF, 0x02, 0x22, 0xFE, 0x41, 0x42]],
1026 packets: &[PacketData {
1027 sequence_no: 0,
1028 services: &[ServiceData {
1029 service_no: 1,
1030 codes: &[tables::Code::LatinCapitalA, tables::Code::LatinCapitalB],
1031 }],
1032 }],
1033 cea608: &[],
1034 },
1035 TestCCData {
1037 framerate: Framerate::new(25, 1),
1038 cc_data: &[
1039 &[0x80 | 0x40 | 0x02, 0xFF, 0xFF, 0x02, 0x21, 0xFE, 0x41, 0x00],
1040 &[0x80 | 0x40 | 0x02, 0xFF, 0xFF, 0x42, 0x21, 0xFE, 0x42, 0x00],
1041 ],
1042 packets: &[
1043 PacketData {
1044 sequence_no: 0,
1045 services: &[ServiceData {
1046 service_no: 1,
1047 codes: &[tables::Code::LatinCapitalA],
1048 }],
1049 },
1050 PacketData {
1051 sequence_no: 1,
1052 services: &[ServiceData {
1053 service_no: 1,
1054 codes: &[tables::Code::LatinCapitalB],
1055 }],
1056 },
1057 ],
1058 cea608: &[],
1059 },
1060 TestCCData {
1062 framerate: Framerate::new(25, 1),
1063 cc_data: &[
1064 &[0x80 | 0x40 | 0x01, 0xFF, 0xFF, 0x02, 0x21],
1065 &[0x80 | 0x40 | 0x01, 0xFF, 0xFE, 0x41, 0x00],
1066 ],
1067 packets: &[PacketData {
1068 sequence_no: 0,
1069 services: &[ServiceData {
1070 service_no: 1,
1071 codes: &[tables::Code::LatinCapitalA],
1072 }],
1073 }],
1074 cea608: &[],
1075 },
1076 TestCCData {
1078 framerate: Framerate::new(25, 1),
1079 cc_data: &[&[0x80 | 0x40 | 0x01, 0xFF, 0xFF, 0x01, 0x00]],
1080 packets: &[PacketData {
1081 sequence_no: 0,
1082 services: &[],
1083 }],
1084 cea608: &[],
1085 },
1086 TestCCData {
1088 framerate: Framerate::new(25, 1),
1089 cc_data: &[&[
1090 0x80 | 0x40 | 0x03,
1091 0xFF,
1092 0xFF,
1093 0x03,
1094 0x21,
1095 0xFE,
1096 0x41,
1097 0x41,
1098 0xFE,
1099 0x42,
1100 0x00,
1101 ]],
1102 packets: &[PacketData {
1103 sequence_no: 0,
1104 services: &[
1105 ServiceData {
1106 service_no: 1,
1107 codes: &[tables::Code::LatinCapitalA],
1108 },
1109 ServiceData {
1110 service_no: 2,
1111 codes: &[tables::Code::LatinCapitalB],
1112 },
1113 ],
1114 }],
1115 cea608: &[],
1116 },
1117 TestCCData {
1119 framerate: Framerate::new(25, 1),
1120 cc_data: &[&[
1121 0x80 | 0x40 | 0x04,
1122 0xFF,
1123 0xFF,
1124 0x02,
1125 0x21,
1126 0xFE,
1127 0x41,
1128 0x00,
1129 0xFF,
1130 0x42,
1131 0x41,
1132 0xFE,
1133 0x42,
1134 0x00,
1135 ]],
1136 packets: &[
1137 PacketData {
1138 sequence_no: 0,
1139 services: &[ServiceData {
1140 service_no: 1,
1141 codes: &[tables::Code::LatinCapitalA],
1142 }],
1143 },
1144 PacketData {
1145 sequence_no: 1,
1146 services: &[ServiceData {
1147 service_no: 2,
1148 codes: &[tables::Code::LatinCapitalB],
1149 }],
1150 },
1151 ],
1152 cea608: &[],
1153 },
1154 TestCCData {
1157 framerate: Framerate::new(25, 1),
1158 cc_data: &[
1159 &[
1160 0x80 | 0x40 | 0x03,
1161 0xFF,
1162 0xFC,
1163 0x61,
1164 0x62,
1165 0xFD,
1166 0x63,
1167 0x64,
1168 0xFF,
1169 0x02,
1170 0x21,
1171 ],
1172 &[
1173 0x80 | 0x40 | 0x03,
1174 0xFF,
1175 0xFC,
1176 0x41,
1177 0x42,
1178 0xFD,
1179 0x43,
1180 0x44,
1181 0xFE,
1182 0x41,
1183 0x00,
1184 ],
1185 ],
1186 packets: &[PacketData {
1187 sequence_no: 0,
1188 services: &[ServiceData {
1189 service_no: 1,
1190 codes: &[tables::Code::LatinCapitalA],
1191 }],
1192 }],
1193 cea608: &[
1194 &[Cea608::Field1(0x61, 0x62), Cea608::Field2(0x63, 0x64)],
1195 &[Cea608::Field1(0x41, 0x42), Cea608::Field2(0x43, 0x44)],
1196 ],
1197 },
1198 ];
1199
1200 #[test]
1201 fn cc_data_parse() {
1202 test_init_log();
1203 for (i, test_data) in TEST_CC_DATA.iter().enumerate() {
1204 log::info!("parsing {i}: {test_data:?}");
1205 let mut parser = CCDataParser::new();
1206 if !test_data.cea608.is_empty() {
1207 parser.handle_cea608();
1208 }
1209 let mut expected_iter = test_data.packets.iter();
1210 let mut cea608_iter = test_data.cea608.iter();
1211 for data in test_data.cc_data.iter() {
1212 debug!("pushing {data:?}");
1213 parser.push(data).unwrap();
1214 while let Some(packet) = parser.pop_packet() {
1215 let expected = expected_iter.next().unwrap();
1216 assert_eq!(expected.sequence_no, packet.sequence_no());
1217 let services = packet.services();
1218 let mut expected_service_iter = expected.services.iter();
1219 for parsed_service in services.iter() {
1220 let expected_service = expected_service_iter.next().unwrap();
1221 assert_eq!(parsed_service.number(), expected_service.service_no);
1222 assert_eq!(expected_service.codes, parsed_service.codes());
1223 }
1224 assert!(expected_service_iter.next().is_none());
1225 }
1226 assert_eq!(parser.cea608().as_ref(), cea608_iter.next());
1227 }
1228 assert!(parser.pop_packet().is_none());
1229 assert!(expected_iter.next().is_none());
1230 assert!(cea608_iter.next().is_none());
1231 }
1232 }
1233
1234 static WRITE_CC_DATA: [TestCCData; 7] = [
1235 TestCCData {
1237 framerate: Framerate::new(25, 1),
1238 cc_data: &[&[0x80 | 0x40 | 0x02, 0xFF, 0xFF, 0x02, 0x21, 0xFE, 0x41, 0x00]],
1239 packets: &[PacketData {
1240 sequence_no: 0,
1241 services: &[ServiceData {
1242 service_no: 1,
1243 codes: &[tables::Code::LatinCapitalA],
1244 }],
1245 }],
1246 cea608: &[],
1247 },
1248 TestCCData {
1250 framerate: Framerate::new(25, 1),
1251 cc_data: &[&[0x80 | 0x40 | 0x02, 0xFF, 0xFF, 0x02, 0x22, 0xFE, 0x41, 0x42]],
1252 packets: &[PacketData {
1253 sequence_no: 0,
1254 services: &[ServiceData {
1255 service_no: 1,
1256 codes: &[tables::Code::LatinCapitalA, tables::Code::LatinCapitalB],
1257 }],
1258 }],
1259 cea608: &[],
1260 },
1261 TestCCData {
1263 framerate: Framerate::new(25, 1),
1264 cc_data: &[&[
1265 0x80 | 0x40 | 0x11,
1266 0xFF,
1267 0xFF,
1268 0xC0 | 0x11,
1269 0x20 | 0x1F,
1270 0xFE,
1271 0x41,
1272 0x42,
1273 0xFE,
1274 0x43,
1275 0x44,
1276 0xFE,
1277 0x45,
1278 0x46,
1279 0xFE,
1280 0x47,
1281 0x48,
1282 0xFE,
1283 0x49,
1284 0x4A,
1285 0xFE,
1286 0x4B,
1287 0x4C,
1288 0xFE,
1289 0x4D,
1290 0x4E,
1291 0xFE,
1292 0x4F,
1293 0x50,
1294 0xFE,
1295 0x51,
1296 0x52,
1297 0xFE,
1298 0x53,
1299 0x54,
1300 0xFE,
1301 0x55,
1302 0x56,
1303 0xFE,
1304 0x57,
1305 0x58,
1306 0xFE,
1307 0x59,
1308 0x5A,
1309 0xFE,
1310 0x61,
1311 0x62,
1312 0xFE,
1313 0x63,
1314 0x64,
1315 0xFE,
1316 0x65,
1317 0x0,
1318 ]],
1319 packets: &[PacketData {
1320 sequence_no: 3,
1321 services: &[ServiceData {
1322 service_no: 1,
1323 codes: &[
1324 tables::Code::LatinCapitalA,
1325 tables::Code::LatinCapitalB,
1326 tables::Code::LatinCapitalC,
1327 tables::Code::LatinCapitalD,
1328 tables::Code::LatinCapitalE,
1329 tables::Code::LatinCapitalF,
1330 tables::Code::LatinCapitalG,
1331 tables::Code::LatinCapitalH,
1332 tables::Code::LatinCapitalI,
1333 tables::Code::LatinCapitalJ,
1334 tables::Code::LatinCapitalK,
1335 tables::Code::LatinCapitalL,
1336 tables::Code::LatinCapitalM,
1337 tables::Code::LatinCapitalN,
1338 tables::Code::LatinCapitalO,
1339 tables::Code::LatinCapitalP,
1340 tables::Code::LatinCapitalQ,
1341 tables::Code::LatinCapitalR,
1342 tables::Code::LatinCapitalS,
1343 tables::Code::LatinCapitalT,
1344 tables::Code::LatinCapitalU,
1345 tables::Code::LatinCapitalV,
1346 tables::Code::LatinCapitalW,
1347 tables::Code::LatinCapitalX,
1348 tables::Code::LatinCapitalY,
1349 tables::Code::LatinCapitalZ,
1350 tables::Code::LatinLowerA,
1351 tables::Code::LatinLowerB,
1352 tables::Code::LatinLowerC,
1353 tables::Code::LatinLowerD,
1354 tables::Code::LatinLowerE,
1355 ],
1356 }],
1357 }],
1358 cea608: &[],
1359 },
1360 TestCCData {
1362 framerate: Framerate::new(25, 1),
1363 cc_data: &[&[0x80 | 0x40 | 0x01, 0xFF, 0xFC, 0x41, 0x42]],
1364 packets: &[],
1365 cea608: &[&[Cea608::Field1(0x41, 0x42)]],
1366 },
1367 TestCCData {
1369 framerate: Framerate::new(25, 1),
1370 cc_data: &[&[0x80 | 0x40 | 0x02, 0xFF, 0xFC, 0x80, 0x80, 0xFD, 0x41, 0x42]],
1371 packets: &[],
1372 cea608: &[&[Cea608::Field2(0x41, 0x42)]],
1373 },
1374 TestCCData {
1376 framerate: Framerate::new(60, 1),
1377 cc_data: &[
1378 &[
1379 0x80 | 0x40 | 0x0A,
1380 0xFF,
1381 0xFC,
1382 0x20,
1383 0x42,
1384 0xFF,
1385 0xC0 | 0x11,
1386 0x20 | 0x1F,
1387 0xFE,
1388 0x41,
1389 0x42,
1390 0xFE,
1391 0x43,
1392 0x44,
1393 0xFE,
1394 0x45,
1395 0x46,
1396 0xFE,
1397 0x47,
1398 0x48,
1399 0xFE,
1400 0x49,
1401 0x4A,
1402 0xFE,
1403 0x4B,
1404 0x4C,
1405 0xFE,
1406 0x4D,
1407 0x4E,
1408 0xFE,
1409 0x4F,
1410 0x50,
1411 ],
1412 &[
1413 0x80 | 0x40 | 0x09,
1414 0xFF,
1415 0xFD,
1416 0x21,
1417 0x43,
1418 0xFE,
1419 0x51,
1420 0x52,
1421 0xFE,
1422 0x53,
1423 0x54,
1424 0xFE,
1425 0x55,
1426 0x56,
1427 0xFE,
1428 0x57,
1429 0x58,
1430 0xFE,
1431 0x59,
1432 0x5A,
1433 0xFE,
1434 0x61,
1435 0x62,
1436 0xFE,
1437 0x63,
1438 0x64,
1439 0xFE,
1440 0x65,
1441 0x0,
1442 ],
1443 ],
1444 packets: &[PacketData {
1445 sequence_no: 3,
1446 services: &[ServiceData {
1447 service_no: 1,
1448 codes: &[
1449 tables::Code::LatinCapitalA,
1450 tables::Code::LatinCapitalB,
1451 tables::Code::LatinCapitalC,
1452 tables::Code::LatinCapitalD,
1453 tables::Code::LatinCapitalE,
1454 tables::Code::LatinCapitalF,
1455 tables::Code::LatinCapitalG,
1456 tables::Code::LatinCapitalH,
1457 tables::Code::LatinCapitalI,
1458 tables::Code::LatinCapitalJ,
1459 tables::Code::LatinCapitalK,
1460 tables::Code::LatinCapitalL,
1461 tables::Code::LatinCapitalM,
1462 tables::Code::LatinCapitalN,
1463 tables::Code::LatinCapitalO,
1464 tables::Code::LatinCapitalP,
1465 tables::Code::LatinCapitalQ,
1466 tables::Code::LatinCapitalR,
1467 tables::Code::LatinCapitalS,
1468 tables::Code::LatinCapitalT,
1469 tables::Code::LatinCapitalU,
1470 tables::Code::LatinCapitalV,
1471 tables::Code::LatinCapitalW,
1472 tables::Code::LatinCapitalX,
1473 tables::Code::LatinCapitalY,
1474 tables::Code::LatinCapitalZ,
1475 tables::Code::LatinLowerA,
1476 tables::Code::LatinLowerB,
1477 tables::Code::LatinLowerC,
1478 tables::Code::LatinLowerD,
1479 tables::Code::LatinLowerE,
1480 ],
1481 }],
1482 }],
1483 cea608: &[&[Cea608::Field1(0x20, 0x42), Cea608::Field2(0x21, 0x43)]],
1484 },
1485 TestCCData {
1487 framerate: Framerate::new(24, 1),
1488 cc_data: &[
1489 &[0x80 | 0x40 | 0x02, 0xFF, 0xFC, 0x20, 0x42, 0xFD, 0x21, 0x43],
1490 &[0x80 | 0x40 | 0x02, 0xFF, 0xFC, 0x22, 0x44, 0xFD, 0x23, 0x45],
1491 ],
1492 packets: &[PacketData {
1493 sequence_no: 3,
1494 services: &[],
1495 }],
1496 cea608: &[
1497 &[Cea608::Field1(0x20, 0x42), Cea608::Field2(0x21, 0x43)],
1498 &[Cea608::Field1(0x22, 0x44), Cea608::Field2(0x23, 0x45)],
1499 ],
1500 },
1501 ];
1502
1503 #[test]
1504 fn packet_write_cc_data() {
1505 test_init_log();
1506 for test_data in WRITE_CC_DATA.iter() {
1507 log::info!("writing {test_data:?}");
1508 let mut packet_iter = test_data.packets.iter();
1509 let mut cea608_iter = test_data.cea608.iter();
1510 let mut writer = CCDataWriter::default();
1511 for cc_data in test_data.cc_data.iter() {
1512 if let Some(packet_data) = packet_iter.next() {
1513 let mut pack = DTVCCPacket::new(packet_data.sequence_no);
1514 for service_data in packet_data.services.iter() {
1515 let mut service = Service::new(service_data.service_no);
1516 for code in service_data.codes.iter() {
1517 service.push_code(code).unwrap();
1518 }
1519 pack.push_service(service).unwrap();
1520 }
1521 writer.push_packet(pack);
1522 }
1523 if let Some(&cea608) = cea608_iter.next() {
1524 for pair in cea608 {
1525 writer.push_cea608(*pair);
1526 }
1527 }
1528 let mut written = vec![];
1529 writer.write(test_data.framerate, &mut written).unwrap();
1530 assert_eq!(cc_data, &written);
1531 }
1532 }
1533 }
1534
1535 #[test]
1536 fn framerate_cea608_pairs_per_frame() {
1537 assert_eq!(Framerate::new(60, 1).cea608_pairs_per_frame(), 1);
1538 assert_eq!(Framerate::new(30, 1).cea608_pairs_per_frame(), 2);
1539 }
1540
1541 #[test]
1542 fn framerate_max_cc_count() {
1543 assert_eq!(Framerate::new(60, 1).max_cc_count(), 10);
1544 assert_eq!(Framerate::new(30, 1).max_cc_count(), 20);
1545 }
1546
1547 #[test]
1548 fn framerate_new() {
1549 let fps = Framerate::new(30, 8);
1550 assert_eq!(fps.numer(), 30);
1551 assert_eq!(fps.denom(), 8);
1552 }
1553}
1554
1555#[cfg(test)]
1556pub(crate) mod tests {
1557 use std::sync::OnceLock;
1558
1559 static TRACING: OnceLock<()> = OnceLock::new();
1560
1561 pub fn test_init_log() {
1562 TRACING.get_or_init(|| {
1563 env_logger::init();
1564 });
1565 }
1566}