Compare commits

...

3 Commits
v0.90 ... v0.91

Author SHA1 Message Date
Carlos Fernandez Sanz
5127da50d1 Push version 0.90 -> 0.91 2021-07-26 09:53:45 -07:00
PunitLodha
352f035214 [rust] Add Pen Presets and timing functions (#1363)
* add pen presets and timing functions

* fix typo

* fix formatting
2021-07-22 19:26:06 -07:00
PunitLodha
f04ba8d0c4 Rust updates (#1361)
* add handlers for CLW, HDW, TGW, DLW, and CR

* refactor rust code

* fix clippy warnings

* add ccxr_  prefix to rust functions

* Add SPA, SPC, SPL and CWx commands

* Add DSW and DFx commands

* Add more C0 and extended commands

* Use slice instead of sending whole packet and pos
2021-07-18 10:48:14 -07:00
15 changed files with 990 additions and 209 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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])

View File

@@ -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])

View File

@@ -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')

View File

@@ -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:

View File

@@ -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"

View File

@@ -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
}
}
}

View File

@@ -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 }
}
}

View File

@@ -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");

View 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)
}

View File

@@ -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,
}

View File

@@ -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
View 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
}

View File

@@ -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"