mirror of
https://github.com/CCExtractor/ccextractor.git
synced 2026-02-12 13:35:15 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5127da50d1 | ||
|
|
352f035214 | ||
|
|
f04ba8d0c4 |
@@ -4,7 +4,7 @@ MAINTAINER = Marc Espie <espie@openbsd.org>
|
||||
CATEGORIES = multimedia
|
||||
COMMENT = closed caption subtitles extractor
|
||||
HOMEPAGE = https://ccextractor.org
|
||||
V = 0.90
|
||||
V = 0.91
|
||||
DISTFILES = ccextractor.${V:S/.//}-src.zip
|
||||
MASTER_SITES = ${MASTER_SITE_SOURCEFORGE:=ccextractor/}
|
||||
DISTNAME = ccextractor-$V
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
0.91 (2021-07-26)
|
||||
-----------------
|
||||
- More Rust in the 708 decoder (Add Pen Presets and timing functions)
|
||||
- Updated GUI
|
||||
|
||||
0.90 (2021-07-14)
|
||||
-----------------
|
||||
- New installer (WiX based)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.69])
|
||||
AC_INIT([CCExtractor], [0.90], [carlos@ccextractor.org])
|
||||
AC_INIT([CCExtractor], [0.91], [carlos@ccextractor.org])
|
||||
AC_CONFIG_AUX_DIR([build-conf])
|
||||
AC_CONFIG_SRCDIR([../src/ccextractor.c])
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.69])
|
||||
AC_INIT([CCExtractor], [0.90], [carlos@ccextractor.org])
|
||||
AC_INIT([CCExtractor], [0.91], [carlos@ccextractor.org])
|
||||
AC_CONFIG_AUX_DIR([build-conf])
|
||||
AC_CONFIG_SRCDIR([../src/ccextractor.c])
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pkgname=ccextractor
|
||||
pkgver=0.90
|
||||
pkgver=0.91
|
||||
pkgrel=1
|
||||
pkgdesc="A closed captions and teletext subtitles extractor for video streams."
|
||||
arch=('i686' 'x86_64')
|
||||
|
||||
@@ -683,7 +683,7 @@ void dtvcc_process_bs(dtvcc_service_decoder *decoder)
|
||||
window->pen_column++;
|
||||
break;
|
||||
case DTVCC_WINDOW_PD_LEFT_RIGHT:
|
||||
if (decoder->windows->pen_column > 0)
|
||||
if (window->pen_column > 0)
|
||||
window->pen_column--;
|
||||
break;
|
||||
case DTVCC_WINDOW_PD_BOTTOM_TOP:
|
||||
@@ -808,7 +808,7 @@ void dtvcc_process_character(dtvcc_service_decoder *decoder, dtvcc_symbol symbol
|
||||
window->pen_column++;
|
||||
break;
|
||||
case DTVCC_WINDOW_PD_RIGHT_LEFT:
|
||||
if (decoder->windows->pen_column > 0)
|
||||
if (window->pen_column > 0)
|
||||
window->pen_column--;
|
||||
break;
|
||||
case DTVCC_WINDOW_PD_TOP_BOTTOM:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef CCX_CCEXTRACTOR_H
|
||||
#define CCX_CCEXTRACTOR_H
|
||||
|
||||
#define VERSION "0.90"
|
||||
#define VERSION "0.91"
|
||||
|
||||
// Load common includes and constants for library usage
|
||||
#include "ccx_common_platform.h"
|
||||
|
||||
@@ -152,3 +152,35 @@ impl C1Command {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle C2 - Code Set - Extended Control Code Set 1
|
||||
pub fn handle_C2(code: u8) -> u8 {
|
||||
match code {
|
||||
// ... Single-byte control bytes (0 additional bytes)
|
||||
0..=0x07 => 1,
|
||||
// ..two-byte control codes (1 additional byte)
|
||||
0x08..=0x0F => 2,
|
||||
// ..three-byte control codes (2 additional bytes)
|
||||
0x10..=0x17 => 3,
|
||||
// 18-1F => four-byte control codes (3 additional bytes)
|
||||
_ => 4,
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle C3 - Code Set - Extended Control Code Set 2
|
||||
pub fn handle_C3(code: u8, next_code: u8) -> u8 {
|
||||
match code {
|
||||
// Five-byte control bytes (4 additional bytes)
|
||||
0x80..=0x87 => 5,
|
||||
// Six-byte control codes (5 additional byte)
|
||||
0x88..=0x8F => 6,
|
||||
// 90-9F variable length commands
|
||||
// Refer Section 7.1.11.2
|
||||
_ => {
|
||||
// next code is the header which specifies additional bytes
|
||||
let length = (next_code & 0x3F) + 1;
|
||||
// + 1 for current code
|
||||
length + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
|
||||
mod commands;
|
||||
mod service_decoder;
|
||||
mod timing;
|
||||
mod window;
|
||||
|
||||
use log::{debug, warn};
|
||||
|
||||
use crate::bindings::*;
|
||||
use crate::{bindings::*, utils::is_true};
|
||||
|
||||
const CCX_DTVCC_MAX_PACKET_LENGTH: u8 = 128;
|
||||
const CCX_DTVCC_NO_LAST_SEQUENCE: i32 = -1;
|
||||
@@ -33,41 +34,23 @@ pub struct Dtvcc<'a> {
|
||||
impl<'a> Dtvcc<'a> {
|
||||
/// Create a new dtvcc context
|
||||
pub fn new(ctx: &'a mut dtvcc_ctx) -> Self {
|
||||
let mut is_active = false;
|
||||
let mut report_enabled = false;
|
||||
let mut is_header_parsed = false;
|
||||
let mut no_rollup = false;
|
||||
|
||||
if ctx.is_active == 1 {
|
||||
is_active = true;
|
||||
}
|
||||
if ctx.report_enabled == 1 {
|
||||
report_enabled = true;
|
||||
}
|
||||
if ctx.is_current_packet_header_parsed == 1 {
|
||||
is_header_parsed = true;
|
||||
}
|
||||
if ctx.no_rollup == 1 {
|
||||
no_rollup = true;
|
||||
}
|
||||
|
||||
let report = unsafe { &mut *ctx.report };
|
||||
let encoder = unsafe { &mut *(ctx.encoder as *mut encoder_ctx) };
|
||||
let timing = unsafe { &mut *ctx.timing };
|
||||
|
||||
Self {
|
||||
is_active,
|
||||
is_active: is_true(ctx.is_active),
|
||||
active_services_count: ctx.active_services_count as u8,
|
||||
services_active: ctx.services_active.iter().copied().collect(),
|
||||
report_enabled,
|
||||
report_enabled: is_true(ctx.report_enabled),
|
||||
report,
|
||||
decoders: ctx.decoders.iter_mut().collect(),
|
||||
packet: ctx.current_packet.to_vec(),
|
||||
packet_length: ctx.current_packet_length as u8,
|
||||
is_header_parsed,
|
||||
is_header_parsed: is_true(ctx.is_current_packet_header_parsed),
|
||||
last_sequence: ctx.last_sequence,
|
||||
encoder,
|
||||
no_rollup,
|
||||
no_rollup: is_true(ctx.no_rollup),
|
||||
timing,
|
||||
}
|
||||
}
|
||||
@@ -172,12 +155,10 @@ impl<'a> Dtvcc<'a> {
|
||||
self.report.services[service_number as usize] = 1;
|
||||
}
|
||||
|
||||
if service_number > 0 && self.services_active[(service_number - 1) as usize] == 1 {
|
||||
if service_number > 0 && is_true(self.services_active[(service_number - 1) as usize]) {
|
||||
let decoder = &mut self.decoders[(service_number - 1) as usize];
|
||||
decoder.process_service_block(
|
||||
&mut self.packet,
|
||||
pos,
|
||||
block_length,
|
||||
&self.packet[pos as usize..(pos + block_length) as usize],
|
||||
self.encoder,
|
||||
self.timing,
|
||||
self.no_rollup,
|
||||
@@ -207,4 +188,9 @@ impl dtvcc_symbol {
|
||||
pub fn new(sym: u16) -> dtvcc_symbol {
|
||||
dtvcc_symbol { init: 1, sym }
|
||||
}
|
||||
/// Create a new 16 bit symbol
|
||||
pub fn new_16(data1: u8, data2: u8) -> dtvcc_symbol {
|
||||
let sym = (data1 as u16) << 8 | data2 as u16;
|
||||
dtvcc_symbol { init: 1, sym }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,93 +1,87 @@
|
||||
use std::os::raw::c_uchar;
|
||||
use std::{
|
||||
alloc::{alloc, dealloc, Layout},
|
||||
os::raw::c_uchar,
|
||||
};
|
||||
|
||||
use log::{debug, warn};
|
||||
use log::{debug, error, warn};
|
||||
|
||||
use super::commands::{C0CodeSet, C0Command, C1CodeSet, C1Command};
|
||||
use crate::bindings::*;
|
||||
use super::{
|
||||
commands::{self, C0CodeSet, C0Command, C1CodeSet, C1Command},
|
||||
window::{PenPreset, WindowPreset},
|
||||
};
|
||||
use crate::{
|
||||
bindings::*,
|
||||
utils::{is_false, is_true},
|
||||
};
|
||||
|
||||
const CCX_DTVCC_MUSICAL_NOTE_CHAR: u16 = 9836;
|
||||
const CCX_DTVCC_MAX_WINDOWS: u8 = 8;
|
||||
const DTVCC_COMMANDS_C0_CODES_DTVCC_C0_EXT1: u8 = 16;
|
||||
const CCX_DTVCC_SCREENGRID_ROWS: u8 = 75;
|
||||
const CCX_DTVCC_SCREENGRID_COLUMNS: u8 = 210;
|
||||
const CCX_DTVCC_MAX_ROWS: u8 = 15;
|
||||
const CCX_DTVCC_MAX_COLUMNS: u8 = 32 * 2;
|
||||
|
||||
impl dtvcc_service_decoder {
|
||||
/// Process service block and call handlers for the respective codesets
|
||||
pub fn process_service_block(
|
||||
&mut self,
|
||||
current_packet: &mut [c_uchar],
|
||||
pos: u8,
|
||||
block_length: u8,
|
||||
block: &[u8],
|
||||
encoder: &mut encoder_ctx,
|
||||
timing: &mut ccx_common_timing_ctx,
|
||||
no_rollup: bool,
|
||||
) {
|
||||
let mut i = 0;
|
||||
while i < block_length {
|
||||
let curr: usize = (pos + i) as usize;
|
||||
|
||||
let consumed = if current_packet[curr] != DTVCC_COMMANDS_C0_CODES_DTVCC_C0_EXT1 as u8 {
|
||||
let used = match current_packet[curr] {
|
||||
0..=0x1F => self.handle_C0(
|
||||
current_packet,
|
||||
pos + i,
|
||||
block_length - i,
|
||||
encoder,
|
||||
timing,
|
||||
no_rollup,
|
||||
),
|
||||
0x20..=0x7F => self.handle_G0(current_packet, pos + i, block_length - i),
|
||||
0x80..=0x9F => {
|
||||
self.handle_C1(current_packet, pos + i, block_length - i, encoder, timing)
|
||||
}
|
||||
_ => self.handle_G1(current_packet, pos + i, block_length - i),
|
||||
while i < block.len() {
|
||||
let consumed = if block[i] != DTVCC_COMMANDS_C0_CODES_DTVCC_C0_EXT1 as u8 {
|
||||
let used = match block[i] {
|
||||
0..=0x1F => self.handle_C0(&block[i..], encoder, timing, no_rollup),
|
||||
0x20..=0x7F => self.handle_G0(&block[i..]),
|
||||
0x80..=0x9F => self.handle_C1(&block[i..], encoder, timing),
|
||||
_ => self.handle_G1(&block[i..]),
|
||||
};
|
||||
if used == -1 {
|
||||
warn!("dtvcc_process_service_block: There was a problem handling the data.");
|
||||
return;
|
||||
}
|
||||
used
|
||||
used as u8
|
||||
} else {
|
||||
let mut used = self.handle_extended_char(current_packet, pos + i, block_length - i);
|
||||
let mut used = self.handle_extended_char(&block[i..]);
|
||||
used += 1; // Since we had CCX_DTVCC_C0_EXT1
|
||||
used
|
||||
};
|
||||
i += consumed as u8;
|
||||
i += consumed as usize;
|
||||
}
|
||||
}
|
||||
/// Handle C0 - Code Set - Miscellaneous Control Codes
|
||||
pub fn handle_C0(
|
||||
&mut self,
|
||||
current_packet: &mut [c_uchar],
|
||||
pos: u8,
|
||||
block_length: u8,
|
||||
block: &[u8],
|
||||
encoder: &mut encoder_ctx,
|
||||
timing: &mut ccx_common_timing_ctx,
|
||||
no_rollup: bool,
|
||||
) -> i32 {
|
||||
let service_decoder = self as *mut dtvcc_service_decoder;
|
||||
let data = (&mut current_packet[pos as usize]) as *mut std::os::raw::c_uchar;
|
||||
|
||||
let code = current_packet[pos as usize];
|
||||
let code = block[0];
|
||||
let C0Command { command, length } = C0Command::new(code);
|
||||
debug!("C0: [{:?}] ({})", command, block_length);
|
||||
unsafe {
|
||||
match command {
|
||||
// NUL command does nothing
|
||||
C0CodeSet::NUL => {}
|
||||
C0CodeSet::ETX => dtvcc_process_etx(service_decoder),
|
||||
C0CodeSet::BS => dtvcc_process_bs(service_decoder),
|
||||
C0CodeSet::FF => dtvcc_process_ff(service_decoder),
|
||||
C0CodeSet::CR => self.process_cr(encoder, timing, no_rollup),
|
||||
C0CodeSet::HCR => dtvcc_process_hcr(service_decoder),
|
||||
// EXT1 is handled elsewhere as an extended command
|
||||
C0CodeSet::EXT1 => {}
|
||||
C0CodeSet::P16 => dtvcc_handle_C0_P16(service_decoder, data.add(1)),
|
||||
C0CodeSet::RESERVED => {}
|
||||
}
|
||||
debug!("C0: [{:?}] ({})", command, block.len());
|
||||
match command {
|
||||
// NUL command does nothing
|
||||
C0CodeSet::NUL => {}
|
||||
C0CodeSet::ETX => self.process_etx(),
|
||||
C0CodeSet::BS => self.process_bs(),
|
||||
C0CodeSet::FF => self.process_ff(),
|
||||
C0CodeSet::CR => self.process_cr(encoder, timing, no_rollup),
|
||||
C0CodeSet::HCR => self.process_hcr(),
|
||||
// EXT1 is handled elsewhere as an extended command
|
||||
C0CodeSet::EXT1 => {}
|
||||
C0CodeSet::P16 => self.process_p16(&block[1..]),
|
||||
C0CodeSet::RESERVED => {}
|
||||
}
|
||||
if length > block_length {
|
||||
if length as usize > block.len() {
|
||||
warn!(
|
||||
"dtvcc_handle_C0: command is {} bytes long but we only have {}",
|
||||
length, block_length
|
||||
length,
|
||||
block.len()
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
@@ -96,79 +90,73 @@ impl dtvcc_service_decoder {
|
||||
/// Handle C1 - Code Set - Captioning Command Control Codes
|
||||
pub fn handle_C1(
|
||||
&mut self,
|
||||
current_packet: &mut [c_uchar],
|
||||
pos: u8,
|
||||
block_length: u8,
|
||||
block: &[c_uchar],
|
||||
encoder: &mut encoder_ctx,
|
||||
timing: &mut ccx_common_timing_ctx,
|
||||
) -> i32 {
|
||||
let data = (&mut current_packet[pos as usize]) as *mut std::os::raw::c_uchar;
|
||||
|
||||
let code = current_packet[pos as usize];
|
||||
let next_code = current_packet[(pos + 1) as usize] as i32;
|
||||
let code = block[0];
|
||||
let next_code = block[1];
|
||||
let C1Command {
|
||||
command,
|
||||
length,
|
||||
name,
|
||||
} = C1Command::new(code);
|
||||
|
||||
if length > block_length {
|
||||
if length as usize > block.len() {
|
||||
warn!("Warning: Not enough bytes for command.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug!("C1: [{:?}] [{}] ({})", command, name, length);
|
||||
unsafe {
|
||||
match command {
|
||||
C1CodeSet::CW0
|
||||
| C1CodeSet::CW1
|
||||
| C1CodeSet::CW2
|
||||
| C1CodeSet::CW3
|
||||
| C1CodeSet::CW4
|
||||
| C1CodeSet::CW5
|
||||
| C1CodeSet::CW6
|
||||
| C1CodeSet::CW7 => self
|
||||
.handle_set_current_window(code - DTVCC_COMMANDS_C1_CODES_DTVCC_C1_CW0 as u8),
|
||||
C1CodeSet::CLW => self.handle_clear_windows(next_code, encoder, timing),
|
||||
C1CodeSet::HDW => self.handle_hide_windows(next_code, encoder, timing),
|
||||
C1CodeSet::TGW => self.handle_toggle_windows(next_code, encoder, timing),
|
||||
C1CodeSet::DLW => self.handle_delete_windows(next_code, encoder, timing),
|
||||
C1CodeSet::DSW => dtvcc_handle_DSW_DisplayWindows(self, next_code, timing),
|
||||
C1CodeSet::DLY => dtvcc_handle_DLY_Delay(self, next_code),
|
||||
C1CodeSet::DLC => dtvcc_handle_DLC_DelayCancel(self),
|
||||
C1CodeSet::RST => dtvcc_handle_RST_Reset(self),
|
||||
C1CodeSet::SPA => self.handle_set_pen_attributes(current_packet, pos),
|
||||
C1CodeSet::SPC => self.handle_set_pen_color(current_packet, pos),
|
||||
C1CodeSet::SPL => self.handle_set_pen_location(current_packet, pos),
|
||||
C1CodeSet::SWA => self.handle_set_window_attributes(current_packet, pos),
|
||||
C1CodeSet::DF0
|
||||
| C1CodeSet::DF1
|
||||
| C1CodeSet::DF2
|
||||
| C1CodeSet::DF3
|
||||
| C1CodeSet::DF4
|
||||
| C1CodeSet::DF5
|
||||
| C1CodeSet::DF6
|
||||
| C1CodeSet::DF7 => dtvcc_handle_DFx_DefineWindow(
|
||||
self,
|
||||
(code - DTVCC_COMMANDS_C1_CODES_DTVCC_C1_DF0 as u8) as i32,
|
||||
data,
|
||||
timing,
|
||||
),
|
||||
C1CodeSet::RESERVED => {
|
||||
warn!("Warning, Found Reserved codes, ignored");
|
||||
}
|
||||
};
|
||||
}
|
||||
match command {
|
||||
C1CodeSet::CW0
|
||||
| C1CodeSet::CW1
|
||||
| C1CodeSet::CW2
|
||||
| C1CodeSet::CW3
|
||||
| C1CodeSet::CW4
|
||||
| C1CodeSet::CW5
|
||||
| C1CodeSet::CW6
|
||||
| C1CodeSet::CW7 => {
|
||||
self.handle_set_current_window(code - DTVCC_COMMANDS_C1_CODES_DTVCC_C1_CW0 as u8)
|
||||
}
|
||||
C1CodeSet::CLW => self.handle_clear_windows(next_code, encoder, timing),
|
||||
C1CodeSet::HDW => self.handle_hide_windows(next_code, encoder, timing),
|
||||
C1CodeSet::TGW => self.handle_toggle_windows(next_code, encoder, timing),
|
||||
C1CodeSet::DLW => self.handle_delete_windows(next_code, encoder, timing),
|
||||
C1CodeSet::DSW => self.handle_display_windows(next_code, timing),
|
||||
C1CodeSet::DLY => self.handle_delay(next_code),
|
||||
C1CodeSet::DLC => self.handle_delay_cancel(),
|
||||
C1CodeSet::RST => self.handle_reset(),
|
||||
C1CodeSet::SPA => self.handle_set_pen_attributes(&block[1..]),
|
||||
C1CodeSet::SPC => self.handle_set_pen_color(&block[1..]),
|
||||
C1CodeSet::SPL => self.handle_set_pen_location(&block[1..]),
|
||||
C1CodeSet::SWA => self.handle_set_window_attributes(&block[1..]),
|
||||
C1CodeSet::DF0
|
||||
| C1CodeSet::DF1
|
||||
| C1CodeSet::DF2
|
||||
| C1CodeSet::DF3
|
||||
| C1CodeSet::DF4
|
||||
| C1CodeSet::DF5
|
||||
| C1CodeSet::DF6
|
||||
| C1CodeSet::DF7 => self.handle_define_windows(
|
||||
code - DTVCC_COMMANDS_C1_CODES_DTVCC_C1_DF0 as u8,
|
||||
&block[1..],
|
||||
timing,
|
||||
),
|
||||
C1CodeSet::RESERVED => {
|
||||
warn!("Warning, Found Reserved codes, ignored");
|
||||
}
|
||||
};
|
||||
length as i32
|
||||
}
|
||||
/// Handle G0 - Code Set - ASCII printable characters
|
||||
pub fn handle_G0(&mut self, current_packet: &mut [c_uchar], pos: u8, block_length: u8) -> i32 {
|
||||
pub fn handle_G0(&mut self, block: &[c_uchar]) -> i32 {
|
||||
if self.current_window == -1 {
|
||||
warn!("dtvcc_handle_G0: Window has to be defined first");
|
||||
return block_length as i32;
|
||||
return block.len() as i32;
|
||||
}
|
||||
|
||||
let character = current_packet[pos as usize];
|
||||
let character = block[0];
|
||||
debug!("G0: [{:2X}] ({})", character, character as char);
|
||||
let sym = if character == 0x7F {
|
||||
dtvcc_symbol::new(CCX_DTVCC_MUSICAL_NOTE_CHAR)
|
||||
@@ -179,34 +167,54 @@ impl dtvcc_service_decoder {
|
||||
1
|
||||
}
|
||||
/// Handle G1 - Code Set - ISO 8859-1 LATIN-1 Character Set
|
||||
pub fn handle_G1(&mut self, current_packet: &mut [c_uchar], pos: u8, block_length: u8) -> i32 {
|
||||
pub fn handle_G1(&mut self, block: &[c_uchar]) -> i32 {
|
||||
if self.current_window == -1 {
|
||||
warn!("dtvcc_handle_G1: Window has to be defined first");
|
||||
return block_length as i32;
|
||||
return block.len() as i32;
|
||||
}
|
||||
|
||||
let character = current_packet[pos as usize];
|
||||
let character = block[0];
|
||||
debug!("G1: [{:2X}] ({})", character, character as char);
|
||||
let sym = dtvcc_symbol::new(character as u16);
|
||||
self.process_character(sym);
|
||||
1
|
||||
}
|
||||
/// Handle extended codes (EXT1 + code), from the extended sets
|
||||
pub fn handle_extended_char(
|
||||
&mut self,
|
||||
current_packet: &mut [c_uchar],
|
||||
pos: u8,
|
||||
block_length: u8,
|
||||
) -> i32 {
|
||||
unsafe {
|
||||
let data = (&mut current_packet[pos as usize]) as *mut std::os::raw::c_uchar;
|
||||
dtvcc_handle_extended_char(self, data, block_length as i32)
|
||||
/// G2 (20-7F) => Mostly unmapped, except for a few characters.
|
||||
/// G3 (A0-FF) => A0 is the CC symbol, everything else reserved for future expansion in EIA708
|
||||
/// C2 (00-1F) => Reserved for future extended misc. control and captions command codes
|
||||
/// C3 (80-9F) => Reserved for future extended misc. control and captions command codes
|
||||
/// WARN: This code is completely untested due to lack of samples. Just following specs!
|
||||
/// Returns number of used bytes, usually 1 (since EXT1 is not counted).
|
||||
pub fn handle_extended_char(&mut self, block: &[c_uchar]) -> u8 {
|
||||
let code = block[0];
|
||||
debug!(
|
||||
"dtvcc_handle_extended_char, first data code: [{}], length: [{}]",
|
||||
code as char,
|
||||
block.len()
|
||||
);
|
||||
|
||||
match code {
|
||||
0..=0x1F => commands::handle_C2(code),
|
||||
0x20..=0x7F => {
|
||||
let val = unsafe { dtvcc_get_internal_from_G2(code) };
|
||||
let sym = dtvcc_symbol::new(val as u16);
|
||||
self.process_character(sym);
|
||||
1
|
||||
}
|
||||
0x80..=0x9F => commands::handle_C3(code, block[1]),
|
||||
_ => {
|
||||
let val = unsafe { dtvcc_get_internal_from_G3(code) };
|
||||
let sym = dtvcc_symbol::new(val as u16);
|
||||
self.process_character(sym);
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Handle CLW Clear Windows
|
||||
pub fn handle_clear_windows(
|
||||
&mut self,
|
||||
mut windows_bitmap: i32,
|
||||
mut windows_bitmap: u8,
|
||||
encoder: &mut encoder_ctx,
|
||||
timing: &mut ccx_common_timing_ctx,
|
||||
) {
|
||||
@@ -220,12 +228,13 @@ impl dtvcc_service_decoder {
|
||||
if windows_bitmap & 1 == 1 {
|
||||
let window = &mut self.windows[i as usize];
|
||||
debug!("[W{}]", i);
|
||||
let window_had_content =
|
||||
window.is_defined == 1 && window.visible == 1 && window.is_empty == 0;
|
||||
let window_had_content = is_true(window.is_defined)
|
||||
&& is_true(window.visible)
|
||||
&& is_false(window.is_empty);
|
||||
if window_had_content {
|
||||
screen_content_changed = true;
|
||||
window.update_time_hide(timing);
|
||||
let window = window as *mut dtvcc_window;
|
||||
dtvcc_window_update_time_hide(window, timing);
|
||||
dtvcc_window_copy_to_screen(self, window)
|
||||
}
|
||||
dtvcc_window_clear(self, i as i32);
|
||||
@@ -241,7 +250,7 @@ impl dtvcc_service_decoder {
|
||||
/// Handle HDW Hide Windows
|
||||
pub fn handle_hide_windows(
|
||||
&mut self,
|
||||
mut windows_bitmap: i32,
|
||||
mut windows_bitmap: u8,
|
||||
encoder: &mut encoder_ctx,
|
||||
timing: &mut ccx_common_timing_ctx,
|
||||
) {
|
||||
@@ -255,12 +264,12 @@ impl dtvcc_service_decoder {
|
||||
if windows_bitmap & 1 == 1 {
|
||||
let window = &mut self.windows[i as usize];
|
||||
debug!("[W{}]", i);
|
||||
if window.visible == 1 {
|
||||
if is_true(window.visible) {
|
||||
screen_content_changed = true;
|
||||
window.visible = 0;
|
||||
window.update_time_hide(timing);
|
||||
let window_ctx = window as *mut dtvcc_window;
|
||||
dtvcc_window_update_time_hide(window_ctx, timing);
|
||||
if window.is_empty == 0 {
|
||||
if is_false(window.is_empty) {
|
||||
dtvcc_window_copy_to_screen(self, window_ctx);
|
||||
}
|
||||
}
|
||||
@@ -276,7 +285,7 @@ impl dtvcc_service_decoder {
|
||||
/// Handle TGW Toggle Windows
|
||||
pub fn handle_toggle_windows(
|
||||
&mut self,
|
||||
mut windows_bitmap: i32,
|
||||
mut windows_bitmap: u8,
|
||||
encoder: &mut encoder_ctx,
|
||||
timing: &mut ccx_common_timing_ctx,
|
||||
) {
|
||||
@@ -289,16 +298,16 @@ impl dtvcc_service_decoder {
|
||||
for i in 0..CCX_DTVCC_MAX_WINDOWS {
|
||||
let window = &mut self.windows[i as usize];
|
||||
let window_ctx = window as *mut dtvcc_window;
|
||||
if windows_bitmap & 1 == 1 && window.is_defined == 1 {
|
||||
if window.visible == 0 {
|
||||
if windows_bitmap & 1 == 1 && is_true(window.is_defined) {
|
||||
if is_false(window.visible) {
|
||||
debug!("[W-{}: 0->1]", i);
|
||||
window.visible = 1;
|
||||
dtvcc_window_update_time_show(window_ctx, timing);
|
||||
window.update_time_show(timing);
|
||||
} else {
|
||||
debug!("[W-{}: 1->0]", i);
|
||||
window.visible = 0;
|
||||
dtvcc_window_update_time_hide(window_ctx, timing);
|
||||
if window.is_empty == 0 {
|
||||
window.update_time_hide(timing);
|
||||
if is_false(window.is_empty) {
|
||||
screen_content_changed = true;
|
||||
dtvcc_window_copy_to_screen(self, window_ctx);
|
||||
}
|
||||
@@ -315,7 +324,7 @@ impl dtvcc_service_decoder {
|
||||
/// Handle DLW Delete Windows
|
||||
pub fn handle_delete_windows(
|
||||
&mut self,
|
||||
mut windows_bitmap: i32,
|
||||
mut windows_bitmap: u8,
|
||||
encoder: &mut encoder_ctx,
|
||||
timing: &mut ccx_common_timing_ctx,
|
||||
) {
|
||||
@@ -329,12 +338,13 @@ impl dtvcc_service_decoder {
|
||||
if windows_bitmap & 1 == 1 {
|
||||
debug!("Deleting [W{}]", i);
|
||||
let window = &mut self.windows[i as usize];
|
||||
let window_had_content =
|
||||
window.is_defined == 1 && window.visible == 1 && window.is_empty == 0;
|
||||
let window_had_content = is_true(window.is_defined)
|
||||
&& is_true(window.visible)
|
||||
&& is_false(window.is_empty);
|
||||
if window_had_content {
|
||||
screen_content_changed = true;
|
||||
window.update_time_hide(timing);
|
||||
let window = window as *mut dtvcc_window;
|
||||
dtvcc_window_update_time_hide(window, timing);
|
||||
dtvcc_window_copy_to_screen(self, window);
|
||||
if self.current_window == i as i32 {
|
||||
self.screen_print(encoder, timing);
|
||||
@@ -357,21 +367,211 @@ impl dtvcc_service_decoder {
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Handle DSW Display Windows
|
||||
pub fn handle_display_windows(
|
||||
&mut self,
|
||||
mut windows_bitmap: u8,
|
||||
timing: &mut ccx_common_timing_ctx,
|
||||
) {
|
||||
debug!("dtvcc_handle_DSW_DisplayWindows: windows:");
|
||||
if windows_bitmap == 0 {
|
||||
debug!("none");
|
||||
} else {
|
||||
for i in 0..CCX_DTVCC_MAX_WINDOWS {
|
||||
if windows_bitmap & 1 == 1 {
|
||||
let window = &mut self.windows[i as usize];
|
||||
debug!("[W{}]", i);
|
||||
if is_false(window.is_defined) {
|
||||
error!("Window {} was not defined", i);
|
||||
continue;
|
||||
}
|
||||
if is_false(window.visible) {
|
||||
window.visible = 1;
|
||||
window.update_time_show(timing);
|
||||
}
|
||||
}
|
||||
windows_bitmap >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Handle DFx Define Windows
|
||||
pub fn handle_define_windows(
|
||||
&mut self,
|
||||
window_id: u8,
|
||||
block: &[c_uchar],
|
||||
timing: &mut ccx_common_timing_ctx,
|
||||
) {
|
||||
debug!(
|
||||
"dtvcc_handle_DFx_DefineWindow: W[{}], attributes:",
|
||||
window_id
|
||||
);
|
||||
let window = &mut self.windows[window_id as usize];
|
||||
let block = &block[..=5];
|
||||
let is_command_repeated = window
|
||||
.commands
|
||||
.iter()
|
||||
.zip(block.iter())
|
||||
.all(|(x, y)| x == y);
|
||||
|
||||
if is_true(window.is_defined) && is_command_repeated {
|
||||
// When a decoder receives a DefineWindow command for an existing window, the
|
||||
// command is to be ignored if the command parameters are unchanged from the
|
||||
// previous window definition.
|
||||
debug!("dtvcc_handle_DFx_DefineWindow: Repeated window definition, ignored\n");
|
||||
return;
|
||||
}
|
||||
|
||||
window.number = window_id as i32;
|
||||
let priority = (block[0]) & 0x7;
|
||||
let col_lock = (block[0] >> 3) & 0x1;
|
||||
let row_lock = (block[0] >> 4) & 0x1;
|
||||
let visible = (block[0] >> 5) & 0x1;
|
||||
let mut anchor_vertical = block[1] & 0x7f;
|
||||
let relative_pos = block[1] >> 7;
|
||||
let mut anchor_horizontal = block[2];
|
||||
let row_count = (block[3] & 0xf) + 1;
|
||||
let anchor_point = block[3] >> 4;
|
||||
let col_count = (block[4] & 0x3f) + 1;
|
||||
let mut pen_style = block[5] & 0x7;
|
||||
let mut win_style = (block[5] >> 3) & 0x7;
|
||||
|
||||
let mut do_clear_window = false;
|
||||
|
||||
debug!("Visible: [{}]", if is_true(visible) { "Yes" } else { "No" });
|
||||
debug!("Priority: [{}]", priority);
|
||||
debug!("Row count: [{}]", row_count);
|
||||
debug!("Column count: [{}]", col_count);
|
||||
debug!("Anchor point: [{}]", anchor_point);
|
||||
debug!("Anchor vertical: [{}]", anchor_vertical);
|
||||
debug!("Anchor horizontal: [{}]", anchor_horizontal);
|
||||
debug!(
|
||||
"Relative pos: [{}]",
|
||||
if is_true(relative_pos) { "Yes" } else { "No" }
|
||||
);
|
||||
debug!(
|
||||
"Row lock: [{}]",
|
||||
if is_true(row_lock) { "Yes" } else { "No" }
|
||||
);
|
||||
debug!(
|
||||
"Column lock: [{}]",
|
||||
if is_true(col_lock) { "Yes" } else { "No" }
|
||||
);
|
||||
debug!("Pen style: [{}]", pen_style);
|
||||
debug!("Win style: [{}]", win_style);
|
||||
|
||||
// Korean samples have "anchor_vertical" and "anchor_horizontal" mixed up,
|
||||
// this seems to be an encoder issue, but we can workaround it
|
||||
if anchor_vertical > CCX_DTVCC_SCREENGRID_ROWS - row_count {
|
||||
anchor_vertical = CCX_DTVCC_SCREENGRID_ROWS - row_count;
|
||||
}
|
||||
if anchor_horizontal > CCX_DTVCC_SCREENGRID_COLUMNS - col_count {
|
||||
anchor_horizontal = CCX_DTVCC_SCREENGRID_COLUMNS - col_count;
|
||||
}
|
||||
|
||||
window.priority = priority as i32;
|
||||
window.col_lock = col_lock as i32;
|
||||
window.row_lock = row_lock as i32;
|
||||
window.visible = visible as i32;
|
||||
window.anchor_vertical = anchor_vertical as i32;
|
||||
window.relative_pos = relative_pos as i32;
|
||||
window.anchor_horizontal = anchor_horizontal as i32;
|
||||
window.row_count = row_count as i32;
|
||||
window.anchor_point = anchor_point as i32;
|
||||
window.col_count = col_count as i32;
|
||||
|
||||
// If changing the style of an existing window delete contents
|
||||
if win_style > 0 && is_false(window.is_defined) && window.win_style != win_style as i32 {
|
||||
do_clear_window = true;
|
||||
}
|
||||
|
||||
/* If the window doesn't exist and win style==0 then default to win_style=1 */
|
||||
if win_style == 0 && is_false(window.is_defined) {
|
||||
win_style = 1;
|
||||
}
|
||||
/* If the window doesn't exist and pen style==0 then default to pen_style=1 */
|
||||
if pen_style == 0 && is_false(window.is_defined) {
|
||||
pen_style = 1;
|
||||
}
|
||||
|
||||
if win_style > 0 && win_style < 8 {
|
||||
let preset = WindowPreset::get_style(win_style);
|
||||
if let Ok(preset) = preset {
|
||||
window.set_style(preset);
|
||||
}
|
||||
}
|
||||
|
||||
if pen_style > 0 {
|
||||
let preset = PenPreset::get_style(pen_style);
|
||||
if let Ok(preset) = preset {
|
||||
window.set_pen_style(preset);
|
||||
}
|
||||
window.pen_style = pen_style as i32;
|
||||
}
|
||||
unsafe {
|
||||
if is_false(window.is_defined) {
|
||||
// If the window is being created, all character positions in the window
|
||||
// are set to the fill color and the pen location is set to (0,0)
|
||||
window.pen_column = 0;
|
||||
window.pen_row = 0;
|
||||
if is_false(window.memory_reserved) {
|
||||
for i in 0..CCX_DTVCC_MAX_ROWS as usize {
|
||||
let layout = Layout::array::<dtvcc_symbol>(CCX_DTVCC_MAX_COLUMNS as usize);
|
||||
if let Err(e) = layout {
|
||||
error!("dtvcc_handle_DFx_DefineWindow: Incorrect Layout, {}", e);
|
||||
} else {
|
||||
let ptr = alloc(layout.unwrap());
|
||||
if ptr.is_null() {
|
||||
error!("dtvcc_handle_DFx_DefineWindow: Not enough memory",);
|
||||
}
|
||||
// Exit here?
|
||||
window.rows[i] = ptr as *mut dtvcc_symbol;
|
||||
}
|
||||
}
|
||||
window.memory_reserved = 1;
|
||||
}
|
||||
window.is_defined = 1;
|
||||
dtvcc_window_clear_text(window);
|
||||
} else if do_clear_window {
|
||||
dtvcc_window_clear_text(window);
|
||||
}
|
||||
|
||||
window
|
||||
.commands
|
||||
.iter_mut()
|
||||
.zip(block.iter())
|
||||
.for_each(|(command, val)| *command = *val);
|
||||
|
||||
if is_true(window.visible) {
|
||||
window.update_time_show(timing);
|
||||
}
|
||||
if is_false(window.memory_reserved) {
|
||||
for i in 0..CCX_DTVCC_MAX_ROWS as usize {
|
||||
let layout = Layout::array::<dtvcc_symbol>(CCX_DTVCC_MAX_COLUMNS as usize);
|
||||
if let Err(e) = layout {
|
||||
error!("dtvcc_handle_DFx_DefineWindow: Incorrect Layout, {}", e);
|
||||
} else {
|
||||
dealloc(window.rows[i] as *mut u8, layout.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
// ...also makes the defined windows the current window
|
||||
self.handle_set_current_window(window_id);
|
||||
}
|
||||
}
|
||||
/// Handle SPA Set Pen Attributes
|
||||
pub fn handle_set_pen_attributes(&mut self, current_packet: &[c_uchar], pos: u8) {
|
||||
pub fn handle_set_pen_attributes(&mut self, block: &[c_uchar]) {
|
||||
if self.current_window == -1 {
|
||||
warn!("dtvcc_handle_SPA_SetPenAttributes: Window has to be defined first");
|
||||
return;
|
||||
}
|
||||
|
||||
let pos = pos as usize;
|
||||
let pen_size = (current_packet[pos + 1]) & 0x3;
|
||||
let offset = (current_packet[pos + 1] >> 2) & 0x3;
|
||||
let text_tag = (current_packet[pos + 1] >> 4) & 0xf;
|
||||
let font_tag = (current_packet[pos + 2]) & 0x7;
|
||||
let edge_type = (current_packet[pos + 2] >> 3) & 0x7;
|
||||
let underline = (current_packet[pos + 2] >> 6) & 0x1;
|
||||
let italic = (current_packet[pos + 2] >> 7) & 0x1;
|
||||
let pen_size = (block[0]) & 0x3;
|
||||
let offset = (block[0] >> 2) & 0x3;
|
||||
let text_tag = (block[0] >> 4) & 0xf;
|
||||
let font_tag = (block[1]) & 0x7;
|
||||
let edge_type = (block[1] >> 3) & 0x7;
|
||||
let underline = (block[1] >> 6) & 0x1;
|
||||
let italic = (block[1] >> 7) & 0x1;
|
||||
debug!("dtvcc_handle_SPA_SetPenAttributes: attributes: ");
|
||||
debug!(
|
||||
"Pen size: [{}] Offset: [{}] Text tag: [{}] Font tag: [{}]",
|
||||
@@ -398,18 +598,17 @@ impl dtvcc_service_decoder {
|
||||
pen.italic = italic as i32;
|
||||
}
|
||||
/// Handle SPC Set Pen Color
|
||||
pub fn handle_set_pen_color(&mut self, current_packet: &[c_uchar], pos: u8) {
|
||||
pub fn handle_set_pen_color(&mut self, block: &[c_uchar]) {
|
||||
if self.current_window == -1 {
|
||||
warn!("dtvcc_handle_SPC_SetPenColor: Window has to be defined first");
|
||||
return;
|
||||
}
|
||||
|
||||
let pos = pos as usize;
|
||||
let fg_color = (current_packet[pos + 1]) & 0x3f;
|
||||
let fg_opacity = (current_packet[pos + 1] >> 6) & 0x03;
|
||||
let bg_color = (current_packet[pos + 2]) & 0x3f;
|
||||
let bg_opacity = (current_packet[pos + 2] >> 6) & 0x03;
|
||||
let edge_color = (current_packet[pos + 3]) & 0x3f;
|
||||
let fg_color = (block[0]) & 0x3f;
|
||||
let fg_opacity = (block[0] >> 6) & 0x03;
|
||||
let bg_color = (block[1]) & 0x3f;
|
||||
let bg_opacity = (block[1] >> 6) & 0x03;
|
||||
let edge_color = (block[2]) & 0x3f;
|
||||
debug!("dtvcc_handle_SPC_SetPenColor: attributes: ");
|
||||
debug!(
|
||||
"Foreground color: [{}] Foreground opacity: [{}]",
|
||||
@@ -435,16 +634,15 @@ impl dtvcc_service_decoder {
|
||||
color.edge_color = edge_color as i32;
|
||||
}
|
||||
/// Handle SPL Set Pen Location
|
||||
pub fn handle_set_pen_location(&mut self, current_packet: &[c_uchar], pos: u8) {
|
||||
pub fn handle_set_pen_location(&mut self, block: &[c_uchar]) {
|
||||
if self.current_window == -1 {
|
||||
warn!("dtvcc_handle_SPL_SetPenLocation: Window has to be defined first");
|
||||
return;
|
||||
}
|
||||
|
||||
debug!("dtvcc_handle_SPL_SetPenLocation: attributes: ");
|
||||
let pos = pos as usize;
|
||||
let row = current_packet[pos + 1] & 0x0f;
|
||||
let col = current_packet[pos + 2] & 0x3f;
|
||||
let row = block[0] & 0x0f;
|
||||
let col = block[1] & 0x3f;
|
||||
debug!("Row: [{}] Column: [{}]", row, col);
|
||||
|
||||
let window = &mut self.windows[self.current_window as usize];
|
||||
@@ -452,25 +650,24 @@ impl dtvcc_service_decoder {
|
||||
window.pen_column = col as i32;
|
||||
}
|
||||
/// Handle SWA Set Window Attributes
|
||||
pub fn handle_set_window_attributes(&mut self, current_packet: &[c_uchar], pos: u8) {
|
||||
pub fn handle_set_window_attributes(&mut self, block: &[c_uchar]) {
|
||||
if self.current_window == -1 {
|
||||
warn!("dtvcc_handle_SWA_SetWindowAttributes: Window has to be defined first");
|
||||
return;
|
||||
}
|
||||
|
||||
let pos = pos as usize;
|
||||
let fill_color = (current_packet[pos + 1]) & 0x3f;
|
||||
let fill_opacity = (current_packet[pos + 1] >> 6) & 0x03;
|
||||
let border_color = (current_packet[pos + 2]) & 0x3f;
|
||||
let border_type01 = (current_packet[pos + 2] >> 6) & 0x03;
|
||||
let justify = (current_packet[pos + 3]) & 0x03;
|
||||
let scroll_dir = (current_packet[pos + 3] >> 2) & 0x03;
|
||||
let print_dir = (current_packet[pos + 3] >> 4) & 0x03;
|
||||
let word_wrap = (current_packet[pos + 3] >> 6) & 0x01;
|
||||
let border_type = ((current_packet[pos + 3] >> 5) & 0x04) | border_type01;
|
||||
let display_eff = (current_packet[pos + 4]) & 0x03;
|
||||
let effect_dir = (current_packet[pos + 4] >> 2) & 0x03;
|
||||
let effect_speed = (current_packet[pos + 4] >> 4) & 0x0f;
|
||||
let fill_color = (block[0]) & 0x3f;
|
||||
let fill_opacity = (block[0] >> 6) & 0x03;
|
||||
let border_color = (block[1]) & 0x3f;
|
||||
let border_type01 = (block[1] >> 6) & 0x03;
|
||||
let justify = (block[2]) & 0x03;
|
||||
let scroll_dir = (block[2] >> 2) & 0x03;
|
||||
let print_dir = (block[2] >> 4) & 0x03;
|
||||
let word_wrap = (block[2] >> 6) & 0x01;
|
||||
let border_type = ((block[2] >> 5) & 0x04) | border_type01;
|
||||
let display_eff = (block[3]) & 0x03;
|
||||
let effect_dir = (block[3] >> 2) & 0x03;
|
||||
let effect_speed = (block[3] >> 4) & 0x0f;
|
||||
debug!("dtvcc_handle_SWA_SetWindowAttributes: attributes: ");
|
||||
debug!(
|
||||
"Fill color: [{}] Fill opacity: [{}] Border color: [{}] Border type: [{}]",
|
||||
@@ -500,8 +697,8 @@ impl dtvcc_service_decoder {
|
||||
}
|
||||
/// handle CWx Set Current Window
|
||||
pub fn handle_set_current_window(&mut self, window_id: u8) {
|
||||
debug!("dtvcc_handle_CWx_SetCurrentWindow: ha[{}]\n", window_id);
|
||||
if self.windows[window_id as usize].is_defined == 1 {
|
||||
debug!("dtvcc_handle_CWx_SetCurrentWindow: [{}]", window_id);
|
||||
if is_true(self.windows[window_id as usize].is_defined) {
|
||||
self.current_window = window_id as i32;
|
||||
} else {
|
||||
debug!(
|
||||
@@ -567,13 +764,13 @@ impl dtvcc_service_decoder {
|
||||
}
|
||||
};
|
||||
|
||||
if window.is_defined == 1 {
|
||||
if is_true(window.is_defined) {
|
||||
debug!("dtvcc_process_cr: rolling up");
|
||||
|
||||
let pen_row = window.pen_row;
|
||||
unsafe {
|
||||
window.update_time_hide(timing);
|
||||
let window = window as *mut dtvcc_window;
|
||||
dtvcc_window_update_time_hide(window, timing);
|
||||
dtvcc_window_copy_to_screen(self, window);
|
||||
self.screen_print(encoder, timing);
|
||||
|
||||
@@ -588,11 +785,169 @@ impl dtvcc_service_decoder {
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Process Horizontal Carriage Return (HCR)
|
||||
pub fn process_hcr(&mut self) {
|
||||
if self.current_window == -1 {
|
||||
warn!("dtvcc_process_hcr: Window has to be defined first");
|
||||
return;
|
||||
}
|
||||
let window = &mut self.windows[self.current_window as usize];
|
||||
window.pen_column = 0;
|
||||
unsafe {
|
||||
dtvcc_window_clear_row(window, window.pen_row);
|
||||
}
|
||||
}
|
||||
/// Process Form Feed (FF)
|
||||
pub fn process_ff(&mut self) {
|
||||
if self.current_window == -1 {
|
||||
warn!("dtvcc_process_ff: Window has to be defined first");
|
||||
return;
|
||||
}
|
||||
let window = &mut self.windows[self.current_window as usize];
|
||||
window.pen_column = 0;
|
||||
window.pen_row = 0;
|
||||
}
|
||||
/// Process Backspace (BS)
|
||||
pub fn process_bs(&mut self) {
|
||||
if self.current_window == -1 {
|
||||
warn!("dtvcc_process_bs: Window has to be defined first");
|
||||
return;
|
||||
}
|
||||
//it looks strange, but in some videos (rarely) we have a backspace command
|
||||
//we just print one character over another
|
||||
let window = &mut self.windows[self.current_window as usize];
|
||||
let pd = match dtvcc_window_pd::new(window.attribs.print_direction) {
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
warn!("{}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
match pd {
|
||||
dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT => {
|
||||
if window.pen_column > 0 {
|
||||
window.pen_column -= 1;
|
||||
}
|
||||
}
|
||||
dtvcc_window_pd::DTVCC_WINDOW_PD_RIGHT_LEFT => {
|
||||
if window.pen_column + 1 < window.col_count {
|
||||
window.pen_column += 1;
|
||||
}
|
||||
}
|
||||
dtvcc_window_pd::DTVCC_WINDOW_PD_TOP_BOTTOM => {
|
||||
if window.pen_row > 0 {
|
||||
window.pen_row -= 1;
|
||||
}
|
||||
}
|
||||
dtvcc_window_pd::DTVCC_WINDOW_PD_BOTTOM_TOP => {
|
||||
if window.pen_row + 1 < window.row_count {
|
||||
window.pen_row += 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
/// Process P16
|
||||
/// Used for Code space extension for 16 bit charsets
|
||||
pub fn process_p16(&mut self, block: &[c_uchar]) {
|
||||
if self.current_window == -1 {
|
||||
warn!("dtvcc_process_p16: Window has to be defined first");
|
||||
return;
|
||||
}
|
||||
let sym = dtvcc_symbol::new_16(block[0], block[1]);
|
||||
debug!("dtvcc_process_p16: [{:4X}]", sym.sym);
|
||||
self.process_character(sym);
|
||||
}
|
||||
/// Process End of Text (ETX)
|
||||
pub fn process_etx(&mut self) {}
|
||||
/// Handle DLY Delay
|
||||
pub fn handle_delay(&mut self, tenths_of_sec: u8) {
|
||||
debug!(
|
||||
"dtvcc_handle_DLY_Delay: dely for {} tenths of second",
|
||||
tenths_of_sec
|
||||
);
|
||||
todo!()
|
||||
}
|
||||
/// Handle DLC Delay Cancel
|
||||
pub fn handle_delay_cancel(&mut self) {
|
||||
debug!("dtvcc_handle_DLC_DelayCancel");
|
||||
todo!();
|
||||
}
|
||||
/// Handle RST Reset
|
||||
pub fn handle_reset(&mut self) {
|
||||
unsafe {
|
||||
dtvcc_windows_reset(self);
|
||||
}
|
||||
}
|
||||
|
||||
/// Process the character and add it to the current window
|
||||
pub fn process_character(&mut self, sym: dtvcc_symbol) {
|
||||
unsafe {
|
||||
/* unsafe {
|
||||
dtvcc_process_character(self, sym);
|
||||
} */
|
||||
debug!("{}", self.current_window);
|
||||
let window = &mut self.windows[self.current_window as usize];
|
||||
let window_state = if is_true(window.is_defined) {
|
||||
"OK"
|
||||
} else {
|
||||
"undefined"
|
||||
};
|
||||
let (mut row, mut column) = (-1, -1);
|
||||
if self.current_window != -1 {
|
||||
row = window.pen_row;
|
||||
column = window.pen_column
|
||||
}
|
||||
debug!(
|
||||
"dtvcc_process_character: [{:04X}] - Window {} [{}], Pen: {}:{}",
|
||||
sym.sym, self.current_window, window_state, row, column
|
||||
);
|
||||
|
||||
if self.current_window == -1 || is_false(window.is_defined) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.is_empty = 0;
|
||||
// Add symbol to window
|
||||
unsafe {
|
||||
window.rows[window.pen_row as usize]
|
||||
.add(window.pen_column as usize)
|
||||
.write(sym);
|
||||
}
|
||||
// "Painting" char by pen - attribs
|
||||
window.pen_attribs[window.pen_row as usize][window.pen_column as usize] =
|
||||
window.pen_attribs_pattern;
|
||||
// "Painting" char by pen - colors
|
||||
window.pen_colors[window.pen_row as usize][window.pen_column as usize] =
|
||||
window.pen_color_pattern;
|
||||
|
||||
let pd = match dtvcc_window_pd::new(window.attribs.print_direction) {
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
warn!("{}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
match pd {
|
||||
dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT => {
|
||||
if window.pen_column + 1 < window.col_count {
|
||||
window.pen_column += 1;
|
||||
}
|
||||
}
|
||||
dtvcc_window_pd::DTVCC_WINDOW_PD_RIGHT_LEFT => {
|
||||
if window.pen_column > 0 {
|
||||
window.pen_column -= 1;
|
||||
}
|
||||
}
|
||||
dtvcc_window_pd::DTVCC_WINDOW_PD_TOP_BOTTOM => {
|
||||
if window.pen_row + 1 < window.row_count {
|
||||
window.pen_row += 1;
|
||||
}
|
||||
}
|
||||
dtvcc_window_pd::DTVCC_WINDOW_PD_BOTTOM_TOP => {
|
||||
if window.pen_row > 0 {
|
||||
window.pen_row -= 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
pub fn screen_print(&mut self, encoder: &mut encoder_ctx, timing: &mut ccx_common_timing_ctx) {
|
||||
debug!("dtvcc_screen_print rust");
|
||||
|
||||
43
src/rust/src/decoder/timing.rs
Normal file
43
src/rust/src/decoder/timing.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use crate::{bindings::*, cb_708, cb_field1, cb_field2};
|
||||
|
||||
use log::{debug, error};
|
||||
|
||||
impl ccx_common_timing_ctx {
|
||||
pub fn get_fts(&self, current_field: u8) -> LLONG {
|
||||
unsafe {
|
||||
match current_field {
|
||||
1 => self.fts_now + self.fts_global + cb_field1 as i64 * 1001 / 30,
|
||||
2 => self.fts_now + self.fts_global + cb_field2 as i64 * 1001 / 30,
|
||||
3 => self.fts_now + self.fts_global + cb_708 as i64 * 1001 / 30,
|
||||
_ => {
|
||||
error!("get_fts: Unknown field");
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn get_visible_end(&mut self, current_field: u8) -> LLONG {
|
||||
let fts = self.get_fts(current_field);
|
||||
if fts > self.minimum_fts {
|
||||
self.minimum_fts = fts;
|
||||
}
|
||||
debug!("Visible End time={}", get_time_str(fts));
|
||||
fts
|
||||
}
|
||||
pub fn get_visible_start(self, current_field: u8) -> LLONG {
|
||||
let mut fts = self.get_fts(current_field);
|
||||
if fts <= self.minimum_fts {
|
||||
fts = self.minimum_fts + 1;
|
||||
}
|
||||
debug!("Visible Start time={}", get_time_str(fts));
|
||||
fts
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_time_str(time: LLONG) -> String {
|
||||
let hh = time / 1000 / 60 / 60;
|
||||
let mm = time / 1000 / 60 - 60 * hh;
|
||||
let ss = time / 1000 - 60 * (mm + 60 * hh);
|
||||
let ms = time - 1000 * (ss + 60 * (mm + 60 * hh));
|
||||
format!("{:02}:{:02}:{:02}:{:03}", hh, mm, ss, ms)
|
||||
}
|
||||
@@ -1,5 +1,53 @@
|
||||
use log::debug;
|
||||
|
||||
use super::timing::get_time_str;
|
||||
use crate::bindings::*;
|
||||
|
||||
impl dtvcc_window {
|
||||
pub fn set_style(&mut self, preset: WindowPreset) {
|
||||
let style_id = preset as i32;
|
||||
let window_style = WindowStyle::new(preset);
|
||||
self.win_style = style_id;
|
||||
self.attribs.border_color = window_style.border_color as i32;
|
||||
self.attribs.border_type = window_style.border_type as i32;
|
||||
self.attribs.display_effect = window_style.display_effect as i32;
|
||||
self.attribs.effect_direction = window_style.effect_direction as i32;
|
||||
self.attribs.effect_speed = window_style.effect_speed as i32;
|
||||
self.attribs.fill_color = window_style.fill_color as i32;
|
||||
self.attribs.fill_opacity = window_style.fill_opacity as i32;
|
||||
self.attribs.justify = window_style.justify as i32;
|
||||
self.attribs.print_direction = window_style.print_direction as i32;
|
||||
self.attribs.scroll_direction = window_style.scroll_direction as i32;
|
||||
self.attribs.word_wrap = window_style.word_wrap as i32;
|
||||
}
|
||||
pub fn set_pen_style(&mut self, preset: PenPreset) {
|
||||
let pen_style = PenStyle::new(preset);
|
||||
let pen = &mut self.pen_attribs_pattern;
|
||||
pen.pen_size = pen_style.pen_size as i32;
|
||||
pen.offset = pen_style.offset as i32;
|
||||
pen.edge_type = pen_style.edge_type as i32;
|
||||
pen.underline = pen_style.underline as i32;
|
||||
pen.italic = pen_style.italics as i32;
|
||||
|
||||
let pen_color = &mut self.pen_color_pattern;
|
||||
pen_color.fg_color = pen_style.color.fg_color as i32;
|
||||
pen_color.fg_opacity = pen_style.color.fg_opacity as i32;
|
||||
pen_color.bg_color = pen_style.color.bg_color as i32;
|
||||
pen_color.bg_opacity = pen_style.color.bg_opacity as i32;
|
||||
pen_color.edge_color = pen_style.color.edge_color as i32;
|
||||
}
|
||||
pub fn update_time_show(&mut self, timing: &mut ccx_common_timing_ctx) {
|
||||
self.time_ms_show = timing.get_visible_start(3);
|
||||
let time = get_time_str(self.time_ms_show);
|
||||
debug!("[W-{}] show time updated to {}", self.number, time);
|
||||
}
|
||||
pub fn update_time_hide(&mut self, timing: &mut ccx_common_timing_ctx) {
|
||||
self.time_ms_hide = timing.get_visible_end(3);
|
||||
let time = get_time_str(self.time_ms_hide);
|
||||
debug!("[W-{}] hide time updated to {}", self.number, time);
|
||||
}
|
||||
}
|
||||
|
||||
impl dtvcc_window_pd {
|
||||
pub fn new(direction: i32) -> Result<Self, String> {
|
||||
match direction {
|
||||
@@ -11,3 +59,296 @@ impl dtvcc_window_pd {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Window style for a specific window preset
|
||||
struct WindowStyle {
|
||||
justify: dtvcc_window_justify,
|
||||
print_direction: dtvcc_window_pd,
|
||||
scroll_direction: dtvcc_window_sd,
|
||||
/// Either 0 or 1
|
||||
word_wrap: u8,
|
||||
/// always snap
|
||||
display_effect: dtvcc_window_sde,
|
||||
/// N/A always 0
|
||||
effect_direction: u8,
|
||||
/// N/A always 0
|
||||
effect_speed: u8,
|
||||
/// Either N/A or black, still always 0
|
||||
fill_color: u8,
|
||||
fill_opacity: dtvcc_window_fo,
|
||||
/// always border_none
|
||||
border_type: dtvcc_window_border,
|
||||
/// N/A always 0
|
||||
border_color: u8,
|
||||
}
|
||||
|
||||
impl WindowStyle {
|
||||
pub fn new(preset: WindowPreset) -> Self {
|
||||
let effect_direction = 0;
|
||||
let effect_speed = 0;
|
||||
let fill_color = 0;
|
||||
let border_color = 0;
|
||||
let display_effect = dtvcc_window_sde::DTVCC_WINDOW_SDE_SNAP;
|
||||
let border_type = dtvcc_window_border::DTVCC_WINDOW_BORDER_NONE;
|
||||
|
||||
match preset {
|
||||
WindowPreset::NtscPopup => Self {
|
||||
justify: dtvcc_window_justify::DTVCC_WINDOW_JUSTIFY_LEFT,
|
||||
print_direction: dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT,
|
||||
scroll_direction: dtvcc_window_sd::DTVCC_WINDOW_SD_BOTTOM_TOP,
|
||||
word_wrap: 0,
|
||||
display_effect,
|
||||
effect_direction,
|
||||
effect_speed,
|
||||
fill_color,
|
||||
fill_opacity: dtvcc_window_fo::DTVCC_WINDOW_FO_SOLID,
|
||||
border_type,
|
||||
border_color,
|
||||
},
|
||||
WindowPreset::Popup => Self {
|
||||
justify: dtvcc_window_justify::DTVCC_WINDOW_JUSTIFY_LEFT,
|
||||
print_direction: dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT,
|
||||
scroll_direction: dtvcc_window_sd::DTVCC_WINDOW_SD_BOTTOM_TOP,
|
||||
word_wrap: 0,
|
||||
display_effect,
|
||||
effect_direction,
|
||||
effect_speed,
|
||||
fill_color,
|
||||
fill_opacity: dtvcc_window_fo::DTVCC_WINDOW_FO_TRANSPARENT,
|
||||
border_type,
|
||||
border_color,
|
||||
},
|
||||
WindowPreset::NtscCenteredPopup => Self {
|
||||
justify: dtvcc_window_justify::DTVCC_WINDOW_JUSTIFY_CENTER,
|
||||
print_direction: dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT,
|
||||
scroll_direction: dtvcc_window_sd::DTVCC_WINDOW_SD_BOTTOM_TOP,
|
||||
word_wrap: 0,
|
||||
display_effect,
|
||||
effect_direction,
|
||||
effect_speed,
|
||||
fill_color,
|
||||
fill_opacity: dtvcc_window_fo::DTVCC_WINDOW_FO_SOLID,
|
||||
border_type,
|
||||
border_color,
|
||||
},
|
||||
WindowPreset::NtscRollup => Self {
|
||||
justify: dtvcc_window_justify::DTVCC_WINDOW_JUSTIFY_LEFT,
|
||||
print_direction: dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT,
|
||||
scroll_direction: dtvcc_window_sd::DTVCC_WINDOW_SD_BOTTOM_TOP,
|
||||
word_wrap: 1,
|
||||
display_effect,
|
||||
effect_direction,
|
||||
effect_speed,
|
||||
fill_color,
|
||||
fill_opacity: dtvcc_window_fo::DTVCC_WINDOW_FO_SOLID,
|
||||
border_type,
|
||||
border_color,
|
||||
},
|
||||
WindowPreset::Rollup => Self {
|
||||
justify: dtvcc_window_justify::DTVCC_WINDOW_JUSTIFY_LEFT,
|
||||
print_direction: dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT,
|
||||
scroll_direction: dtvcc_window_sd::DTVCC_WINDOW_SD_BOTTOM_TOP,
|
||||
word_wrap: 1,
|
||||
display_effect,
|
||||
effect_direction,
|
||||
effect_speed,
|
||||
fill_color,
|
||||
fill_opacity: dtvcc_window_fo::DTVCC_WINDOW_FO_TRANSPARENT,
|
||||
border_type,
|
||||
border_color,
|
||||
},
|
||||
WindowPreset::NtscCenteredRollup => Self {
|
||||
justify: dtvcc_window_justify::DTVCC_WINDOW_JUSTIFY_CENTER,
|
||||
print_direction: dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT,
|
||||
scroll_direction: dtvcc_window_sd::DTVCC_WINDOW_SD_BOTTOM_TOP,
|
||||
word_wrap: 1,
|
||||
display_effect,
|
||||
effect_direction,
|
||||
effect_speed,
|
||||
fill_color,
|
||||
fill_opacity: dtvcc_window_fo::DTVCC_WINDOW_FO_SOLID,
|
||||
border_type,
|
||||
border_color,
|
||||
},
|
||||
WindowPreset::TickerTape => Self {
|
||||
justify: dtvcc_window_justify::DTVCC_WINDOW_JUSTIFY_LEFT,
|
||||
print_direction: dtvcc_window_pd::DTVCC_WINDOW_PD_TOP_BOTTOM,
|
||||
scroll_direction: dtvcc_window_sd::DTVCC_WINDOW_SD_RIGHT_LEFT,
|
||||
word_wrap: 0,
|
||||
display_effect,
|
||||
effect_direction,
|
||||
effect_speed,
|
||||
fill_color,
|
||||
fill_opacity: dtvcc_window_fo::DTVCC_WINDOW_FO_SOLID,
|
||||
border_type,
|
||||
border_color,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Predefined window style ids
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum WindowPreset {
|
||||
/// #1 NTSC Style Popup Captions
|
||||
NtscPopup = 1,
|
||||
/// #2 Popup Captions w/o Black Background
|
||||
Popup = 2,
|
||||
/// #3 NTSC Style Centered Popup Captions
|
||||
NtscCenteredPopup = 3,
|
||||
/// #4 NTSC Style Rollup Captions
|
||||
NtscRollup = 4,
|
||||
/// #4 Rollup Captions w/o Black Background
|
||||
Rollup = 5,
|
||||
/// #6 NTSC Style Centered Rollup Captions
|
||||
NtscCenteredRollup = 6,
|
||||
/// #7 Ticker Tape
|
||||
TickerTape = 7,
|
||||
}
|
||||
|
||||
impl WindowPreset {
|
||||
pub fn get_style(style_id: u8) -> Result<Self, String> {
|
||||
match style_id {
|
||||
1 => Ok(WindowPreset::NtscPopup),
|
||||
2 => Ok(WindowPreset::Popup),
|
||||
3 => Ok(WindowPreset::NtscCenteredPopup),
|
||||
4 => Ok(WindowPreset::NtscRollup),
|
||||
5 => Ok(WindowPreset::Rollup),
|
||||
6 => Ok(WindowPreset::NtscCenteredRollup),
|
||||
7 => Ok(WindowPreset::TickerTape),
|
||||
_ => Err("Invalid style".to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum PenPreset {
|
||||
/// #1 Default NTSC Style
|
||||
NtscStyle,
|
||||
/// #2 NTSC Style Mono w/ Serif
|
||||
NtscStyleMonoSerif,
|
||||
/// #3 NTSC Style Prop w/ Serif
|
||||
NtscStylePropSerif,
|
||||
/// #4 NTSC Style Mono w/o Serif
|
||||
NtscStyleMono,
|
||||
/// #5 NTSC Style Prop w/o Serif
|
||||
NtscStyleProp,
|
||||
/// #6 Mono w/o Serif, Bordered Text, No bg
|
||||
MonoBordered,
|
||||
/// #7 Prop w/o Serif, Bordered Text, No bg
|
||||
PropBordered,
|
||||
}
|
||||
|
||||
impl PenPreset {
|
||||
pub fn get_style(style_id: u8) -> Result<Self, String> {
|
||||
match style_id {
|
||||
1 => Ok(PenPreset::NtscStyle),
|
||||
2 => Ok(PenPreset::NtscStyleMonoSerif),
|
||||
3 => Ok(PenPreset::NtscStylePropSerif),
|
||||
4 => Ok(PenPreset::NtscStyleMono),
|
||||
5 => Ok(PenPreset::NtscStyleProp),
|
||||
6 => Ok(PenPreset::MonoBordered),
|
||||
7 => Ok(PenPreset::PropBordered),
|
||||
_ => Err("Invalid style".to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PenStyle {
|
||||
/// always standard pen size
|
||||
pen_size: dtvcc_pen_size,
|
||||
/// Font style, ranged from 1-7
|
||||
/// Not being used current in the C code(bindings)
|
||||
_font_style: dtvcc_pen_font_style,
|
||||
offset: dtvcc_pen_offset,
|
||||
/// always no, i.e. 0
|
||||
italics: u8,
|
||||
/// always no, i.e. 0
|
||||
underline: u8,
|
||||
edge_type: dtvcc_pen_edge,
|
||||
color: PenColor,
|
||||
}
|
||||
|
||||
impl PenStyle {
|
||||
pub fn new(preset: PenPreset) -> Self {
|
||||
let pen_size = dtvcc_pen_size::DTVCC_PEN_SIZE_STANDART;
|
||||
let offset = dtvcc_pen_offset::DTVCC_PEN_OFFSET_NORMAL;
|
||||
let italics = 0;
|
||||
let underline = 0;
|
||||
let bg_opacity = match preset {
|
||||
PenPreset::MonoBordered | PenPreset::PropBordered => Opacity::Transparent,
|
||||
_ => Opacity::Solid,
|
||||
};
|
||||
|
||||
let color = PenColor {
|
||||
/// White(2,2,2) i.e 10,10,10 i.e 42
|
||||
fg_color: 42,
|
||||
fg_opacity: Opacity::Solid,
|
||||
/// Either N/A or black, still always 0
|
||||
bg_color: 0,
|
||||
bg_opacity,
|
||||
/// Either N/A or black, still always 0
|
||||
edge_color: 0,
|
||||
};
|
||||
|
||||
let (font_style, edge_type) = match preset {
|
||||
PenPreset::NtscStyle => (
|
||||
dtvcc_pen_font_style::DTVCC_PEN_FONT_STYLE_DEFAULT_OR_UNDEFINED,
|
||||
dtvcc_pen_edge::DTVCC_PEN_EDGE_UNIFORM,
|
||||
),
|
||||
PenPreset::NtscStyleMonoSerif => (
|
||||
dtvcc_pen_font_style::DTVCC_PEN_FONT_STYLE_MONOSPACED_WITH_SERIFS,
|
||||
dtvcc_pen_edge::DTVCC_PEN_EDGE_UNIFORM,
|
||||
),
|
||||
PenPreset::NtscStylePropSerif => (
|
||||
dtvcc_pen_font_style::DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITH_SERIFS,
|
||||
dtvcc_pen_edge::DTVCC_PEN_EDGE_UNIFORM,
|
||||
),
|
||||
PenPreset::NtscStyleMono => (
|
||||
dtvcc_pen_font_style::DTVCC_PEN_FONT_STYLE_MONOSPACED_WITHOUT_SERIFS,
|
||||
dtvcc_pen_edge::DTVCC_PEN_EDGE_UNIFORM,
|
||||
),
|
||||
PenPreset::NtscStyleProp => (
|
||||
dtvcc_pen_font_style::DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITHOUT_SERIFS,
|
||||
dtvcc_pen_edge::DTVCC_PEN_EDGE_UNIFORM,
|
||||
),
|
||||
PenPreset::MonoBordered => (
|
||||
dtvcc_pen_font_style::DTVCC_PEN_FONT_STYLE_MONOSPACED_WITHOUT_SERIFS,
|
||||
dtvcc_pen_edge::DTVCC_PEN_EDGE_UNIFORM,
|
||||
),
|
||||
PenPreset::PropBordered => (
|
||||
dtvcc_pen_font_style::DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITHOUT_SERIFS,
|
||||
dtvcc_pen_edge::DTVCC_PEN_EDGE_UNIFORM,
|
||||
),
|
||||
};
|
||||
|
||||
Self {
|
||||
pen_size,
|
||||
_font_style: font_style,
|
||||
offset,
|
||||
italics,
|
||||
underline,
|
||||
edge_type,
|
||||
color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PenColor {
|
||||
/// Color of text forground body
|
||||
fg_color: u8,
|
||||
/// Opacity of text foreground body
|
||||
fg_opacity: Opacity,
|
||||
/// Color of background box surrounding the text
|
||||
bg_color: u8,
|
||||
/// Opacity of background box surrounding the text
|
||||
bg_opacity: Opacity,
|
||||
/// Color of the outlined edges of text
|
||||
edge_color: u8,
|
||||
}
|
||||
enum Opacity {
|
||||
Solid = 0,
|
||||
_Flash = 1,
|
||||
_Translucent = 2,
|
||||
Transparent = 3,
|
||||
}
|
||||
|
||||
@@ -19,10 +19,15 @@ pub mod bindings {
|
||||
}
|
||||
|
||||
pub mod decoder;
|
||||
mod utils;
|
||||
|
||||
use bindings::*;
|
||||
use utils::is_true;
|
||||
|
||||
extern "C" {
|
||||
static mut cb_708: c_int;
|
||||
static mut cb_field1: c_int;
|
||||
static mut cb_field2: c_int;
|
||||
}
|
||||
|
||||
/// Initialize env logger
|
||||
@@ -94,11 +99,13 @@ pub fn do_cb(ctx: &mut lib_cc_decode, dtvcc: &mut Dtvcc, cc_block: &[u8]) -> boo
|
||||
2 | 3 => {
|
||||
let current_time = unsafe { get_fts(ctx.timing, ctx.current_field) };
|
||||
ctx.current_field = 3;
|
||||
if ctx.extraction_start.set == 1 && current_time < ctx.extraction_start.time_in_ms {
|
||||
if is_true(ctx.extraction_start.set)
|
||||
&& current_time < ctx.extraction_start.time_in_ms
|
||||
{
|
||||
timeok = false;
|
||||
}
|
||||
|
||||
if ctx.extraction_end.set == 1 && current_time > ctx.extraction_end.time_in_ms {
|
||||
if is_true(ctx.extraction_end.set) && current_time > ctx.extraction_end.time_in_ms {
|
||||
timeok = false;
|
||||
ctx.processed_enough = 1;
|
||||
}
|
||||
|
||||
11
src/rust/src/utils.rs
Normal file
11
src/rust/src/utils.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
//! Some utility functions to deal with values from C bindings
|
||||
|
||||
/// Check if the value is true (Set to 1). Use only for values from C bindings
|
||||
pub fn is_true<T: Into<i32>>(val: T) -> bool {
|
||||
val.into() == 1
|
||||
}
|
||||
|
||||
/// Check if the value is false (Set to 0). Use only for values from C bindings
|
||||
pub fn is_false<T: Into<i32>>(val: T) -> bool {
|
||||
val.into() == 0
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
#include "../lib_ccx/ccx_decoders_common.h"
|
||||
#include "../lib_ccx/ccx_dtvcc.h"
|
||||
#include "../lib_ccx/ccx_decoders_708_output.h"
|
||||
#include "../lib_ccx/ccx_decoders_708_encoding.h"
|
||||
#include "../lib_ccx/ccx_common_timing.h"
|
||||
#include "../lib_ccx/lib_ccx.h"
|
||||
Reference in New Issue
Block a user