use std::collections::VecDeque;
use std::time::Duration;
use muldiv::MulDiv;
use log::trace;
use crate::{Cea608, DTVCCPacket, Framerate};
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum WriterError {
#[error("Writing the data would overflow by {0} bytes")]
WouldOverflow(usize),
#[error("The resource is not writable")]
ReadOnly,
#[error("Empty service was attempted to be written")]
EmptyService,
}
#[derive(Debug, Default)]
pub struct CCDataWriter {
output_cea608_padding: bool,
output_padding: bool,
packets: VecDeque<DTVCCPacket>,
pending_packet_data: Vec<u8>,
cea608_1: VecDeque<(u8, u8)>,
cea608_2: VecDeque<(u8, u8)>,
last_cea608_was_field1: bool,
}
impl CCDataWriter {
pub fn set_output_cea608_padding(&mut self, output_cea608_padding: bool) {
self.output_cea608_padding = output_cea608_padding;
}
pub fn output_cea608_padding(&self) -> bool {
self.output_cea608_padding
}
pub fn set_output_padding(&mut self, output_padding: bool) {
self.output_padding = output_padding;
}
pub fn output_padding(&self) -> bool {
self.output_padding
}
pub fn push_packet(&mut self, packet: DTVCCPacket) {
self.packets.push_front(packet)
}
pub fn push_cea608(&mut self, cea608: Cea608) {
match cea608 {
Cea608::Field1(byte0, byte1) => {
if byte0 != 0x80 || byte1 != 0x80 {
self.cea608_1.push_front((byte0, byte1))
}
}
Cea608::Field2(byte0, byte1) => {
if byte0 != 0x80 || byte1 != 0x80 {
self.cea608_2.push_front((byte0, byte1))
}
}
}
}
pub fn flush(&mut self) {
self.packets.clear();
self.pending_packet_data.clear();
self.cea608_1.clear();
self.cea608_2.clear();
}
pub fn buffered_cea608_field1_duration(&self) -> Duration {
Duration::from_micros(
(self.cea608_1.len() as u64)
.mul_div_ceil(1001 * 1_000_000, 60000)
.unwrap(),
)
}
pub fn buffered_cea608_field2_duration(&self) -> Duration {
Duration::from_micros(
(self.cea608_2.len() as u64)
.mul_div_ceil(1001 * 1_000_000, 60000)
.unwrap(),
)
}
fn buffered_packet_bytes(&self) -> usize {
self.pending_packet_data.len()
+ self
.packets
.iter()
.map(|packet| packet.len())
.sum::<usize>()
}
pub fn buffered_packet_duration(&self) -> Duration {
Duration::from_micros(
((self.buffered_packet_bytes() + 1) as u64 / 2)
.mul_div_ceil(2 * 1001 * 1_000_000, 9_600_000 / 8)
.unwrap(),
)
}
pub fn write<W: std::io::Write>(
&mut self,
framerate: Framerate,
w: &mut W,
) -> Result<(), std::io::Error> {
let mut cea608_pair_rem = if self.output_cea608_padding {
framerate.cea608_pairs_per_frame()
} else {
framerate
.cea608_pairs_per_frame()
.min(self.cea608_1.len().max(self.cea608_2.len() * 2))
};
let mut cc_count_rem = if self.output_padding {
framerate.max_cc_count()
} else {
framerate.max_cc_count().min(
cea608_pair_rem
+ self.pending_packet_data.len() / 3
+ self.packets.iter().map(|p| p.cc_count()).sum::<usize>(),
)
};
trace!("writing with cc_count: {cc_count_rem} and {cea608_pair_rem} cea608 pairs");
let reserved = 0x80;
let process_cc_flag = 0x40;
w.write_all(&[
reserved | process_cc_flag | (cc_count_rem & 0x1f) as u8,
0xFF,
])?;
while cc_count_rem > 0 {
if cea608_pair_rem > 0 {
if !self.last_cea608_was_field1 {
trace!("attempting to write a cea608 byte pair from field 1");
if let Some((byte0, byte1)) = self.cea608_1.pop_back() {
w.write_all(&[0xFC, byte0, byte1])?;
cc_count_rem -= 1;
} else if !self.cea608_2.is_empty() {
w.write_all(&[0xFC, 0x80, 0x80])?;
cc_count_rem -= 1;
} else if self.output_cea608_padding {
w.write_all(&[0xF8, 0x80, 0x80])?;
cc_count_rem -= 1;
}
self.last_cea608_was_field1 = true;
} else {
trace!("attempting to write a cea608 byte pair from field 2");
if let Some((byte0, byte1)) = self.cea608_2.pop_back() {
w.write_all(&[0xFD, byte0, byte1])?;
cc_count_rem -= 1;
} else if self.output_cea608_padding {
w.write_all(&[0xF9, 0x80, 0x80])?;
cc_count_rem -= 1;
}
self.last_cea608_was_field1 = false;
}
cea608_pair_rem -= 1;
} else {
let mut current_packet_data = &mut self.pending_packet_data;
let mut packet_offset = 0;
while packet_offset >= current_packet_data.len() {
if let Some(packet) = self.packets.pop_back() {
trace!("starting packet {packet:?}");
packet.write_as_cc_data(&mut current_packet_data)?;
} else {
trace!("no packet to write");
break;
}
}
trace!("cea708 pending data length {}", current_packet_data.len(),);
while packet_offset < current_packet_data.len() && cc_count_rem > 0 {
assert!(current_packet_data.len() >= packet_offset + 3);
w.write_all(¤t_packet_data[packet_offset..packet_offset + 3])?;
packet_offset += 3;
cc_count_rem -= 1;
}
self.pending_packet_data = current_packet_data[packet_offset..].to_vec();
if self.packets.is_empty() && self.pending_packet_data.is_empty() {
if self.output_padding {
trace!("writing {cc_count_rem} padding bytes");
while cc_count_rem > 0 {
w.write_all(&[0xFA, 0x00, 0x00])?;
cc_count_rem -= 1;
}
}
break;
}
}
}
Ok(())
}
}