Advertisement
Guest User

ccx.rs

a guest
Nov 4th, 2021
145
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 18.68 KB | None | 0 0
  1. use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
  2. use std::collections::HashMap;
  3. use std::fmt::Pointer;
  4. use std::fs::File;
  5. use std::io::Read;
  6. use std::io::{BufRead, BufReader, Seek};
  7. use std::ops::Add;
  8. use std::process::Command;
  9. use std::process::Stdio;
  10.  
  11.  
  12. #[derive(Debug, Clone, Copy)]
  13. struct CCSym {
  14.     sym: char,
  15.     style: SymStyle
  16. }
  17.  
  18. #[derive(Debug, Clone, Copy)]
  19. struct SymStyle {
  20.     color: u8,
  21.     underlined: bool,
  22.     transparent_bg: bool,
  23. }
  24.  
  25. #[derive(Debug)]
  26. struct CCState {
  27.     row: u8,
  28.     col: u8,
  29.     line_buffer: Vec<Vec<Option<CCSym>>>,
  30.     screen_buffer: Vec<Vec<Option<CCSym>>>,
  31.     style: SymStyle,
  32.     last_command: Option<(u8,u8,CC_Command)>
  33. }
  34.  
  35. fn convert_char_special(c: &u8) -> char {
  36.     match c {
  37.         0x0 => '®',
  38.         0x1 => '°',
  39.         0x2 => '½',
  40.         0x3 => '¿',
  41.         0x4 => '™',
  42.         0x5 => '¢',
  43.         0x6 => '£',
  44.         0x7 => '♪',
  45.         0x8 => 'à',
  46.         0x9 => ' ',
  47.         0xA => 'è',
  48.         0xB => 'â',
  49.         0xC => 'ê',
  50.         0xD => 'î',
  51.         0xE => 'ô',
  52.         0xF => 'û',
  53.         other => panic!("Invalid spcial char {}", other)
  54.     }
  55. }
  56.  
  57. fn convert_char(c: &u8) -> char {
  58.     match c {
  59.         0x00 => panic!("convert_char does not handle zero chars"),
  60.         0x2a => 'á',
  61.         0x5c => 'é',
  62.         0x5e => 'í',
  63.         0x5f => 'ó',
  64.         0x60 => 'ú',
  65.         0x7b => 'ç',
  66.         0x7c => '÷',
  67.         0x7d => 'Ñ',
  68.         0x7e => 'ñ',
  69.         0x7f => '█',
  70.         c => char::from(*c)
  71.     }
  72. }
  73.  
  74.  
  75. impl CCState {
  76.     fn new() -> Self {
  77.         Self {
  78.             row:0,
  79.             col:0,
  80.             line_buffer: vec![vec![None;0xff];0xff],
  81.             screen_buffer: vec![vec![None;0xff];0xff],
  82.             style: SymStyle {
  83.                 color:0,
  84.                 underlined: false,
  85.                 transparent_bg: false
  86.             },
  87.             last_command: None,
  88.         }
  89.     }
  90.  
  91.     fn print(&mut self, c: char) {
  92.         let c= CCSym {
  93.             sym: c,
  94.             style: self.style
  95.         };
  96.         self.line_buffer[self.row as usize][self.col as usize]=Some(c);
  97.     }
  98.  
  99.     fn render_buffer(&self) -> Vec<String> {
  100.         let mut ret = vec![];
  101.         for row in &self.screen_buffer {
  102.             let line : String = row.iter().flatten().map(|v| v.sym).collect();
  103.             if !line.is_empty() {
  104.                 ret.push(line);
  105.             }
  106.         }
  107.         return ret;
  108.     }
  109.  
  110.     fn handle_control(&mut self, caption: u8, field: u8, cmd: &CC_Command) -> Option<Vec<String>> {
  111.         let current_command = Some((caption,field,*cmd));
  112.         if self.last_command==current_command {
  113.             // println!("Skipping redundant command: {:?}",cmd);
  114.             self.last_command=None;
  115.             return None;
  116.         }
  117.         // println!("CMD: {:?} | Prev: {:?}",(caption,field,cmd),current_command);
  118.         self.last_command = current_command;
  119.         match cmd {
  120.             CC_Command::Load => return None,
  121.             CC_Command::Backspace => self.col=self.col.saturating_sub(1),
  122.             CC_Command::ClearLine => todo!(),
  123.             CC_Command::Alarm(_) => todo!(),
  124.             CC_Command::ScrollUp(_) => todo!(),
  125.             CC_Command::Flash => todo!(),
  126.             CC_Command::Start(_) => todo!(),
  127.             CC_Command::Resume => todo!(),
  128.             CC_Command::ClearScreen => self.clear_screen_buffer(),
  129.             CC_Command::CarriageReturn => todo!(),
  130.             CC_Command::ClearBuffer => self.clear_line_buffer(),
  131.             CC_Command::DisplayBuffer => {
  132.                 std::mem::swap(&mut self.screen_buffer,&mut self.line_buffer);
  133.                 return Some(self.render_buffer());
  134.             },
  135.         }
  136.         None
  137.     }
  138.  
  139.     fn get_row(&mut self, kind: &PreambleType, row: u8, next_row: bool) -> u8 {
  140.         use PreambleType::*;
  141.         let row= match (kind,row) {
  142.             (Standard,0b00) => 11,
  143.             (Standard,0b01) => 1,
  144.             (Standard,0b10) => 3,
  145.             (Standard,0b11) => 12,
  146.             (Extended,0b00) => 14,
  147.             (Extended,0b01) => 5,
  148.             (Extended,0b10) => 7,
  149.             (Extended,0b11) => 9,
  150.             _ => unreachable!()
  151.         };
  152.         row + (next_row as u8)
  153.  
  154.     }
  155.  
  156.     fn handle_style(&mut self, kind: &PreambleType, style: &Style) {
  157.         self.row = self.get_row(kind,style.row,style.next_row);
  158.         self.style.color=style.style;
  159.         self.style.underlined=style.undeline;
  160.         self.col=0;
  161.     }
  162.  
  163.     fn handle_address(&mut self, kind: &PreambleType, addr: &Address) {
  164.         self.row = self.get_row(kind,addr.row,addr.next_row);
  165.         self.col = addr.cursor*4;
  166.         self.style.underlined=addr.undeline;
  167.     }
  168.  
  169.     fn clear_screen_buffer(&mut self) {
  170.         self.screen_buffer=vec![vec![None;0xff];0xff];
  171.     }
  172.     fn clear_line_buffer(&mut self) {
  173.         self.line_buffer=vec![vec![None;0xff];0xff];
  174.     }
  175.  
  176.     fn update(&mut self, event: &CaptionData) -> Option<Vec<String>> {
  177.         match &event {
  178.             CaptionData::Control{..} => (),
  179.             _ => self.last_command = None,
  180.         }
  181.         match event {
  182.             CaptionData::Padding => (),
  183.             CaptionData::XDS(_, _) => todo!(),
  184.             CaptionData::Character(c1, c2) => {
  185.                 if *c1!=0 {
  186.                     self.print(convert_char(c1));
  187.                     self.col+=1;
  188.                 };
  189.                 if *c2!=0 {
  190.                     self.print(convert_char(c2));
  191.                     self.col+=1;
  192.                 };
  193.             },
  194.             CaptionData::CharacterSpecial { channel, char } => {
  195.                 self.print(convert_char_special(char));
  196.                 self.col+=1;
  197.             },
  198.             CaptionData::CharacterWesternEurope { channel, charset, char } => todo!(),
  199.             CaptionData::CharacterNorpak { channel, charset } => todo!(),
  200.             CaptionData::Style(kind, style) => self.handle_style(kind, style),
  201.             CaptionData::Address(kind, addr) => self.handle_address(kind, addr),
  202.             CaptionData::BackgroundColor { channel, color, transparent } => todo!(),
  203.             CaptionData::MidrowStyle { channel, style, underlined } => {
  204.                 self.style.color= *style;
  205.                 self.style.underlined= *underlined;
  206.             },
  207.             CaptionData::NoBG { channel } => {
  208.                 self.style.transparent_bg=true;
  209.             },
  210.             CaptionData::BlackText { channel, undelined } => todo!(),
  211.             CaptionData::Control { caption, field, command } => {
  212.                 let line = self.handle_control(*caption, *field,command);
  213.                 if line.is_some() {
  214.                     return line;
  215.                 }
  216.             },
  217.             CaptionData::Tab(n) => {
  218.                 self.col+=n;
  219.             },
  220.         };
  221.         None
  222.     }
  223. }
  224.  
  225. impl Default for CCState {
  226.     fn default() -> Self {
  227.         Self::new()
  228.     }
  229. }
  230.  
  231. const MASKS: [((&str, &str), &str); 16] = [
  232.     (("?0000000", "?0000000"), "Padding"),
  233.     (("?CCCCCCC", "?CCCCCCC"), "Character"),
  234.     (("?000CCCC", "?000TTTT"), "XDS metadata"),
  235.     (
  236.         ("?001C001", "?011CCCC"),
  237.         "Special North American character set",
  238.     ),
  239.     (
  240.         ("?001C01S", "?01CCCCC"),
  241.         "Extended Western European character set",
  242.     ),
  243.     (
  244.         ("?001C111", "?010CCCC"),
  245.         "Non-Western Norpak Character Sets",
  246.     ),
  247.     (("?001C0RR", "?1N0SSSU"), "Standard Style"),
  248.     (("?001C0RR", "?1N1SSSU"), "Standard Address"),
  249.     (("?001C1RR", "?1N1SSSU"), "Extended Style"),
  250.     (("?001C1RR", "?1N0SSSU"), "Extended Address"),
  251.     (("?001C000", "?010CCCT"), "BG Color"),
  252.     (("?001C001", "?010SSSU"), "Midrow Style"),
  253.     (("?001C111", "?0101101"), "No BG"),
  254.     (("?001C111", "?010111U"), "Black Text"),
  255.     (("?001C10F", "?010CCCC"), "Control"),
  256.     (("?001C111", "?01000TT"), "Tab"),
  257. ];
  258.  
  259. #[derive(Debug)]
  260. struct Packet {
  261.     pos: u64,
  262.     time: f64,
  263. }
  264.  
  265. #[derive(Debug, PartialEq, Eq, Clone, Copy)]
  266. enum CC_Command {
  267.     Load,
  268.     Backspace,
  269.     ClearLine,
  270.     Alarm(bool),
  271.     ScrollUp(u8),
  272.     Flash,
  273.     Start(bool),
  274.     Resume,
  275.     ClearScreen,
  276.     CarriageReturn,
  277.     ClearBuffer,
  278.     DisplayBuffer,
  279. }
  280.  
  281. impl From<u8> for CC_Command {
  282.     fn from(cmd: u8) -> Self {
  283.         use CC_Command::*;
  284.         match cmd {
  285.             0b0000 => Load,
  286.             0b0001 => Backspace,
  287.             0b0010 => Alarm(false),
  288.             0b0011 => Alarm(true),
  289.             0b0100 => ClearLine,
  290.             0b0101 => ScrollUp(2),
  291.             0b0110 => ScrollUp(3),
  292.             0b0111 => ScrollUp(4),
  293.             0b1000 => Flash,
  294.             0b1001 => Start(true),
  295.             0b1010 => Start(false),
  296.             0b1011 => Resume,
  297.             0b1100 => ClearScreen,
  298.             0b1101 => CarriageReturn,
  299.             0b1110 => ClearBuffer,
  300.             0b1111 => DisplayBuffer,
  301.             other => panic!("Invalid command: {}", other),
  302.         }
  303.     }
  304. }
  305.  
  306. #[derive(Debug, PartialEq, Eq)]
  307. struct Style {
  308.     channel: bool,
  309.     row: u8,
  310.     next_row: bool,
  311.     style: u8,
  312.     undeline: bool,
  313. }
  314. #[derive(Debug, PartialEq, Eq)]
  315. struct Address {
  316.     channel: bool,
  317.     row: u8,
  318.     next_row: bool,
  319.     cursor: u8,
  320.     undeline: bool,
  321. }
  322.  
  323. #[derive(Debug, PartialEq, Eq)]
  324. enum PreambleType {
  325.     Standard,
  326.     Extended,
  327. }
  328.  
  329. #[derive(Debug, PartialEq, Eq)]
  330. enum CaptionData {
  331.     Padding,
  332.     XDS(u8, u8),
  333.     Character(u8, u8),
  334.     CharacterSpecial {
  335.         channel: bool,
  336.         char: u8,
  337.     },
  338.     CharacterWesternEurope {
  339.         channel: bool,
  340.         charset: bool,
  341.         char: u8,
  342.     },
  343.     CharacterNorpak {
  344.         channel: bool,
  345.         charset: u8,
  346.     },
  347.     Style(PreambleType, Style),
  348.     Address(PreambleType, Address),
  349.     BackgroundColor {
  350.         channel: bool,
  351.         color: u8,
  352.         transparent: bool,
  353.     },
  354.     MidrowStyle {
  355.         channel: bool,
  356.         style: u8,
  357.         underlined: bool,
  358.     },
  359.     NoBG {
  360.         channel: u8,
  361.     },
  362.     BlackText {
  363.         channel: u8,
  364.         undelined: bool,
  365.     },
  366.     Control {
  367.         caption: u8,
  368.         field: u8,
  369.         command: CC_Command,
  370.     },
  371.     Tab(u8),
  372. }
  373.  
  374. #[derive(Debug, PartialEq, Eq)]
  375. struct Caption {
  376.     odd_field: bool,
  377.     data: CaptionData,
  378. }
  379.  
  380. #[derive(Debug, PartialEq, Eq)]
  381. struct CC {
  382.     user_data_type: u8,
  383.     block_size: u8,
  384.     odd_field_first: bool,
  385.     filler: bool,
  386.     block: Vec<Caption>,
  387. }
  388.  
  389. fn parse_line(line: &str) -> Option<Packet> {
  390.     if line.is_empty() {
  391.         return None;
  392.     }
  393.     let line: Vec<&str> = line.split('|').collect();
  394.     let data_type = line[0];
  395.     let data: HashMap<&str, &str> = line
  396.         .into_iter()
  397.         .skip(1)
  398.         .map(|v| {
  399.             let v: Vec<&str> = v.split('=').collect();
  400.             (v[0], v[1])
  401.         })
  402.         .collect();
  403.     let codec_type = data["codec_type"];
  404.     match (data_type, codec_type) {
  405.         ("packet", "subtitle") => Some(Packet {
  406.             pos: data["pos"].parse().unwrap(),
  407.             time: data["pts_time"].parse().unwrap(),
  408.         }),
  409.         _ => None,
  410.     }
  411. }
  412.  
  413. fn to_bits(n: u8) -> [bool; 8] {
  414.     let mut ret = [false; 8];
  415.     for v in 0..8 {
  416.         ret[7 - v] = ((n >> v) & 1) == 1;
  417.     }
  418.     return ret;
  419. }
  420.  
  421. fn from_bits(n: &[bool]) -> u8 {
  422.     let mut ret = 0;
  423.     for &b in n.iter() {
  424.         ret <<= 1;
  425.         ret |= b as u8;
  426.     }
  427.     ret
  428. }
  429.  
  430. fn bitmatch(n: [bool; 8], mask: &str) -> Option<HashMap<char, u8>> {
  431.     if mask.len() != 8 {
  432.         panic!("bitmach mask should be 8 characters!");
  433.     }
  434.     let mut m: HashMap<char, Vec<bool>> = HashMap::new();
  435.     for (&b, c) in n.iter().zip(mask.chars()) {
  436.         if c == '1' && !b {
  437.             return None;
  438.         }
  439.         if c == '0' && b {
  440.             return None;
  441.         }
  442.         if c == '1' || c == '0' || c == '?' {
  443.             continue;
  444.         }
  445.         let v = m.entry(c).or_default();
  446.         v.push(b);
  447.     }
  448.     Some(
  449.         m.iter()
  450.             .map(|(&k, v)| {
  451.                 let v = from_bits(v);
  452.                 (k, v)
  453.             })
  454.             .collect(),
  455.     )
  456. }
  457.  
  458. fn valid(data: &CaptionData) -> bool {
  459.     use CaptionData::*;
  460.     match data {
  461.         Character(c1, c2) => {
  462.             let c1=*c1;
  463.             let c2=*c2;
  464.             let c1 = c1 == 0 || c1 >= 0x20;
  465.             let c2 = c2 == 0 || c2 >= 0x20;
  466.             c1 && c2
  467.         }
  468.         Padding | Control { .. } | Address { .. } | MidrowStyle { .. } | Style(..) => true,
  469.         CharacterSpecial { .. } => true,
  470.         other => todo!("{:?}", other),
  471.     }
  472. }
  473.  
  474. fn parse_caption_data(field: &[u8]) -> Result<CaptionData, Box<dyn std::error::Error>> {
  475.     let (c1, c2) = (field[0] & 0x7f, field[1] & 0x7f);
  476.     if c1 == 0 && c2 == 0 {
  477.         return Ok(CaptionData::Padding);
  478.     }
  479.     let b1 = to_bits(c1);
  480.     let b2 = to_bits(c2);
  481.     if c1 != 0 && c2 != 0 {
  482.         // println!("CC: {:08b} {:08b}", c1, c2);
  483.     }
  484.     for ((m1, m2), name) in MASKS {
  485.         if let (Some(m1), Some(m2)) = (bitmatch(b1, m1), bitmatch(b2, m2)) {
  486.             // println!("Try: {} ({:?},{:?})",name,m1,m2);
  487.             let data = match name {
  488.                 "Character" => CaptionData::Character(m1[&'C'], m2[&'C']),
  489.                 "Control" => CaptionData::Control {
  490.                     caption: 0,
  491.                     field: 0,
  492.                     command: CC_Command::from(m2[&'C']),
  493.                 },
  494.                 "Standard Address" => CaptionData::Address(
  495.                     PreambleType::Standard,
  496.                     Address {
  497.                         channel: m1[&'C'] == 1,
  498.                         row: m1[&'R'],
  499.                         next_row: m2[&'N'] == 1,
  500.                         cursor: m2[&'S'],
  501.                         undeline: m2[&'U'] == 1,
  502.                     },
  503.                 ),
  504.                 "Extended Address" => CaptionData::Address(
  505.                     PreambleType::Extended,
  506.                     Address {
  507.                         channel: m1[&'C'] == 1,
  508.                         row: m1[&'R'],
  509.                         next_row: m2[&'N'] == 1,
  510.                         cursor: m2[&'S'],
  511.                         undeline: m2[&'U'] == 1,
  512.                     },
  513.                 ),
  514.                 "Standard Style" => CaptionData::Style(
  515.                     PreambleType::Standard,
  516.                     Style {
  517.                         channel: m1[&'C'] == 1,
  518.                         row: m1[&'R'],
  519.                         next_row: m2[&'N'] == 1,
  520.                         style: m2[&'S'],
  521.                         undeline: m2[&'U'] == 1,
  522.                     },
  523.                 ),
  524.                 "Extended Style" => CaptionData::Style(
  525.                     PreambleType::Extended,
  526.                     Style {
  527.                         channel: m1[&'C'] == 1,
  528.                         row: m1[&'R'],
  529.                         next_row: m2[&'N'] == 1,
  530.                         style: m2[&'S'],
  531.                         undeline: m2[&'U'] == 1,
  532.                     },
  533.                 ),
  534.                 "Midrow Style" => CaptionData::MidrowStyle {
  535.                     channel: m1[&'C'] == 1,
  536.                     underlined: m2[&'U'] == 1,
  537.                     style: m2[&'S'],
  538.                 },
  539.                 "Special North American character set" => CaptionData::CharacterSpecial {
  540.                     channel: m1[&'C'] == 1,
  541.                     char: m2[&'C'],
  542.                 },
  543.                 other => todo!("{}: {:?}", other, (m1, m2))
  544.             };
  545.             if !valid(&data) {
  546.                 continue;
  547.             };
  548.             // println!("{:?}", data);
  549.             return Ok(data);
  550.         }
  551.     }
  552.     panic!("Invalid CC: {:?}", (c1, c2));
  553. }
  554.  
  555. fn read_block<R: Read>(rdr: &mut R) -> Result<Caption, Box<dyn std::error::Error>> {
  556.     let mut field: [u8; 3] = [0; 3];
  557.     if rdr.read(&mut field)? != 3 {
  558.         panic!("Read less than 3 bytes while decoding CC block");
  559.     };
  560.     let filler = field[0] >> 1;
  561.     if filler != 0x7f {
  562.         panic!("caption filler should be 0x7f, was 0x{:02x}", filler);
  563.     }
  564.     Ok(Caption {
  565.         odd_field: field[0] & 1 == 1,
  566.         data: parse_caption_data(&field[1..])?,
  567.     })
  568. }
  569.  
  570. fn read_cc<R: Read>(rdr: &mut R) -> Result<CC, Box<dyn std::error::Error>> {
  571.     let user_data_type = rdr.read_u8()?;
  572.     if user_data_type != 1 {
  573.         panic!("Invalid user_data_type: {}", user_data_type);
  574.     }
  575.     let block_size = rdr.read_u8()?;
  576.     let flags = rdr.read_u8()?;
  577.     let odd_field_first = (flags & 0b10000000) != 0;
  578.     let filler = (flags & 0b01000000) != 0;
  579.     let block_count = (flags & 0b00111110) >> 1;
  580.     let extra_field = flags & 1;
  581.     let num_blocks = (block_count * 2) + extra_field;
  582.     let mut block: Vec<Caption> = (0..num_blocks)
  583.         .map(|_| read_block(rdr))
  584.         .collect::<Result<_, _>>()?;
  585.     let block = block
  586.         .drain(..)
  587.         .filter(|v| v.data != CaptionData::Padding)
  588.         .collect();
  589.     Ok(CC {
  590.         user_data_type,
  591.         block_size,
  592.         odd_field_first,
  593.         filler,
  594.         block,
  595.     })
  596. }
  597.  
  598. fn main() -> Result<(), Box<dyn std::error::Error>> {
  599.     const CC_START: [u8; 6] = [0x00, 0x00, 0x01, 0xB2, 0x43, 0x43];
  600.     let mut search_buffer = [0u8; 6];
  601.     let path = r#"E:\batch_demux\chapter_grabber\out\Zim_Vol_01_Disc_01_d858015e8310c49ed580fc1d4fc362e9\t004_a001_0x1e0.m2v"#;
  602.     let mut reader = BufReader::new(File::open(path)?);
  603.     let mut proc = Command::new("ffprobe")
  604.         .args(&[
  605.             "-probesize",
  606.             &format!("{}", 0x7FFFFFFF),
  607.             "-analyzeduration",
  608.             &format!("{}", 0x7FFFFFFF),
  609.             "-v",
  610.             "fatal",
  611.             "-f",
  612.             "lavfi",
  613.             "-i",
  614.             &format!("movie=\\'{}\\'[out+subcc]", path.replace("\\", "/")),
  615.             "-show_packets",
  616.             "-print_format",
  617.             "compact",
  618.         ])
  619.         .stdout(Stdio::piped())
  620.         .spawn()?;
  621.     let ffmpeg = BufReader::new(proc.stdout.take().unwrap());
  622.     let mut state = CCState::new();
  623.     for line in ffmpeg.lines() {
  624.         if let Some(entry) = parse_line(&line?) {
  625.             reader.seek(std::io::SeekFrom::Start(entry.pos + 42))?;
  626.             let bytes_read = reader.read(&mut search_buffer)?;
  627.             let has_header = bytes_read == search_buffer.len() && search_buffer == CC_START;
  628.             if has_header {
  629.                 let cc = read_cc(&mut reader)?;
  630.                 for b in &cc.block {
  631.                     if let Some(line) = state.update(&b.data) {
  632.                         println!("{} {:?}",entry.time,line)
  633.                     };
  634.                 }
  635.             } else {
  636.                 panic!("No caption header found!");
  637.             };
  638.         }
  639.     }
  640.     proc.wait()?;
  641.     Ok(())
  642. }
  643.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement