Compare commits

...

8 Commits
v0.91 ... v0.92

Author SHA1 Message Date
Carlos Fernandez Sanz
ccf2a031e9 Bump version on Mac 0.91 -> 0.92 2021-08-10 04:24:39 -07:00
Carlos Fernandez Sanz
9784cd5bd1 Update CHANGES.TXT with last-minute Rust updates 2021-08-10 04:22:11 -07:00
Punit Lodha
5d8dc3b9eb Rust updates:- Add writers for transcripts and SAMI (#1372)
* add rust-iconv

* Add writer for transcipts and SAMI

* consistent import ordering
2021-08-10 04:21:22 -07:00
Carlos Fernandez Sanz
a42e847bcb Bump version 0.91 -> 0.92 2021-08-10 04:10:43 -07:00
Jayesh Nirve
b7a1dd1030 Update ISSUE_TEMPLATE.md (#1370) 2021-08-05 20:43:32 -07:00
Punit Lodha
b18e696c85 Rust updates: Added srt writer (#1368)
* pass -DENABLE_RUST to clang

* impl Default

* Update time str format

* Add SRT writer

* fmt
2021-08-03 13:59:31 -07:00
Willem
d58f078c38 Add missing DLL to the installer
Fixes #1367
2021-07-29 14:02:52 +02:00
PunitLodha
0bbdfc13ee Rust updates (#1364)
* add copy to screen

* Add tv_screen and more functions
2021-07-27 23:55:24 -07:00
21 changed files with 1118 additions and 195 deletions

View File

@@ -2,6 +2,8 @@ Please prefix your issue with one of the following: [BUG], [PROPOSAL], [QUESTION
To get the version of CCExtractor, you can use `--version`.
If this issue is related to the flutter GUI, please make the issue on the GUI repo [here](https://github.com/CCExtractor/ccextractorfluttergui/issues/new)
Please check all that apply and **remove the ones that do not**.
In the necessary information section, if this is a regression (something that used to work does not work anymore), make sure to specify the last known working version.

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.91
V = 0.92
DISTFILES = ccextractor.${V:S/.//}-src.zip
MASTER_SITES = ${MASTER_SITE_SOURCEFORGE:=ccextractor/}
DISTNAME = ccextractor-$V

View File

@@ -1,3 +1,10 @@
0.92 (2021-08-10)
-----------------
- Rust updates: Added srt writer
- Rust updates:-Added writers for transcripts and SAMI
- Added missing DLL to Windows installer
- Updated Windows GUI
0.91 (2021-07-26)
-----------------
- More Rust in the 708 decoder (Add Pen Presets and timing functions)

View File

@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([CCExtractor], [0.91], [carlos@ccextractor.org])
AC_INIT([CCExtractor], [0.92], [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.91], [carlos@ccextractor.org])
AC_INIT([CCExtractor], [0.92], [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.91
pkgver=0.92
pkgrel=1
pkgdesc="A closed captions and teletext subtitles extractor for video streams."
arch=('i686' 'x86_64')

View File

@@ -4,6 +4,10 @@
#include "utility.h"
#include "ccx_common_common.h"
#if defined(ENABLE_RUST) && defined(WIN32)
extern void ccxr_close_handle(void *handle);
#endif
int dtvcc_is_row_empty(dtvcc_tv_screen *tv, int row_index)
{
for (int j = 0; j < CCX_DTVCC_SCREENGRID_COLUMNS; j++)
@@ -434,6 +438,11 @@ void dtvcc_writer_init(dtvcc_writer_ctx *writer,
char *charset = cfg->all_services_charset ? cfg->all_services_charset : cfg->services_charsets[service_number - 1];
#ifdef ENABLE_RUST
writer->fhandle = NULL;
writer->charset = charset;
#endif
if (charset)
{
writer->cd = iconv_open("UTF-8", charset);
@@ -450,6 +459,10 @@ void dtvcc_writer_cleanup(dtvcc_writer_ctx *writer)
{
if (writer->fd >= 0 && writer->fd != STDOUT_FILENO)
close(writer->fd);
#if defined(ENABLE_RUST) && defined(WIN32)
ccxr_close_handle(writer->fhandle);
writer->charset = NULL;
#endif
free(writer->filename);
if (writer->cd == (iconv_t)-1)
{

View File

@@ -869,6 +869,10 @@ static int init_output_ctx(struct encoder_ctx *ctx, struct encoder_cfg *cfg)
if (!cfg->services_enabled[i])
{
ctx->dtvcc_writers[i].fd = -1;
#ifdef ENABLE_RUST
ctx->dtvcc_writers[i].fhandle = NULL;
ctx->dtvcc_writers[i].charset = NULL;
#endif
ctx->dtvcc_writers[i].filename = NULL;
ctx->dtvcc_writers[i].cd = (iconv_t)-1;
continue;
@@ -877,6 +881,10 @@ static int init_output_ctx(struct encoder_ctx *ctx, struct encoder_cfg *cfg)
if (cfg->cc_to_stdout)
{
ctx->dtvcc_writers[i].fd = STDOUT_FILENO;
#ifdef ENABLE_RUST
ctx->dtvcc_writers[i].fhandle = NULL;
ctx->dtvcc_writers[i].charset = NULL;
#endif
ctx->dtvcc_writers[i].filename = NULL;
ctx->dtvcc_writers[i].cd = (iconv_t)-1;
}

View File

@@ -24,6 +24,12 @@ if (ctx->buffer == NULL) { fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory for
typedef struct dtvcc_writer_ctx
{
int fd;
#ifdef ENABLE_RUST
// File handle used to work with files on windows
void *fhandle;
// Charset of the subtitle
char *charset;
#endif
char *filename;
iconv_t cd;
} dtvcc_writer_ctx;

View File

@@ -1,7 +1,7 @@
#ifndef CCX_CCEXTRACTOR_H
#define CCX_CCEXTRACTOR_H
#define VERSION "0.91"
#define VERSION "0.92"
// Load common includes and constants for library usage
#include "ccx_common_platform.h"

21
src/rust/Cargo.lock generated
View File

@@ -64,6 +64,7 @@ version = "0.1.0"
dependencies = [
"bindgen",
"env_logger",
"iconv",
"log",
]
@@ -108,6 +109,12 @@ dependencies = [
"vec_map",
]
[[package]]
name = "dyn_buf"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74c57ab96715773d9cb9789b38eb7cbf04b3c6f5624a9d98f51761603376767c"
[[package]]
name = "env_logger"
version = "0.8.4"
@@ -142,6 +149,16 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "iconv"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e6a7db0df823ef299ef75b6951975c7a1f9019910b3665614bac4161bab1a9"
dependencies = [
"dyn_buf",
"libc",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -156,9 +173,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.95"
version = "0.2.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36"
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
[[package]]
name = "libloading"

View File

@@ -13,6 +13,7 @@ crate-type = ["cdylib"]
[dependencies]
log = "0.4.0"
env_logger = "0.8.4"
iconv = "0.1.1"
[build-dependencies]
bindgen = "0.58.1"

View File

@@ -6,6 +6,7 @@ fn main() {
// The input header we would like to generate
// bindings for.
.header("wrapper.h")
.clang_arg("-DENABLE_RUST")
// Tell cargo to invalidate the built crate whenever any of the
// included header files changed.
.parse_callbacks(Box::new(bindgen::CargoCallbacks));
@@ -43,4 +44,4 @@ const ALLOWLIST_FUNCTIONS: &[&str] = &[
"writercwtdata",
];
const ALLOWLIST_TYPES: &[&str] = &[".*(?i)_?dtvcc_.*", "encoder_ctx", "lib_cc_decode"];
const RUSTIFIED_ENUMS: &[&str] = &["dtvcc_(window|pen)_.*"];
const RUSTIFIED_ENUMS: &[&str] = &["dtvcc_(window|pen)_.*", "ccx_output_format"];

View File

@@ -3,16 +3,22 @@
//! This module provides a CEA 708 decoder as defined by ANSI/CTA-708-E R-2018
mod commands;
mod output;
mod service_decoder;
mod timing;
mod tv_screen;
mod window;
use log::{debug, warn};
use crate::{bindings::*, utils::is_true};
use log::{debug, warn};
const CCX_DTVCC_MAX_PACKET_LENGTH: u8 = 128;
const CCX_DTVCC_NO_LAST_SEQUENCE: i32 = -1;
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;
/// Stores the context required for processing 708 data
pub struct Dtvcc<'a> {
@@ -185,12 +191,23 @@ impl<'a> Dtvcc<'a> {
impl dtvcc_symbol {
/// Create a new symbol
pub fn new(sym: u16) -> dtvcc_symbol {
dtvcc_symbol { init: 1, sym }
pub fn new(sym: u16) -> Self {
Self { init: 1, sym }
}
/// Create a new 16 bit symbol
pub fn new_16(data1: u8, data2: u8) -> dtvcc_symbol {
pub fn new_16(data1: u8, data2: u8) -> Self {
let sym = (data1 as u16) << 8 | data2 as u16;
dtvcc_symbol { init: 1, sym }
Self { init: 1, sym }
}
/// Check if symbol is initialized
pub fn is_set(&self) -> bool {
is_true(self.init)
}
}
impl Default for dtvcc_symbol {
/// Create a blank uninitialized symbol
fn default() -> Self {
Self { sym: 0, init: 0 }
}
}

View File

@@ -0,0 +1,81 @@
#[cfg(windows)]
use std::os::windows::io::{FromRawHandle, IntoRawHandle, RawHandle};
use std::{
fs::File,
io::Write,
os::unix::prelude::{FromRawFd, IntoRawFd},
};
use crate::{bindings::*, utils::is_true};
use log::debug;
// Context for writing subtitles to file
pub struct Writer<'a> {
pub cea_708_counter: &'a mut u32,
pub subs_delay: LLONG,
pub crlf: String,
pub write_format: ccx_output_format,
pub writer_ctx: &'a mut dtvcc_writer_ctx,
pub no_font_color: bool,
pub transcript_settings: &'a ccx_encoders_transcript_format,
pub no_bom: i32,
}
impl<'a> Writer<'a> {
pub fn new(
cea_708_counter: &'a mut u32,
subs_delay: LLONG,
write_format: ccx_output_format,
writer_ctx: &'a mut dtvcc_writer_ctx,
no_font_color: i32,
transcript_settings: &'a ccx_encoders_transcript_format,
no_bom: i32,
) -> Self {
Self {
cea_708_counter,
subs_delay,
crlf: "\r\n".to_owned(),
write_format,
writer_ctx,
no_font_color: is_true(no_font_color),
transcript_settings,
no_bom,
}
}
pub fn write_to_file(&mut self, buf: &[u8]) -> Result<(), String> {
#[cfg(unix)]
{
let mut file = unsafe { File::from_raw_fd(self.writer_ctx.fd) };
file.write_all(buf).map_err(|err| err.to_string())?;
self.writer_ctx.fd = file.into_raw_fd();
}
#[cfg(windows)]
{
let mut file = unsafe { File::from_raw_handle(self.writer_ctx.fhandle) };
file.write_all(buf).map_err(|err| err.to_string())?;
self.writer_ctx.fhandle = file.into_raw_handle();
}
Ok(())
}
}
pub fn write_char(sym: &dtvcc_symbol, buf: &mut Vec<u8>) {
if sym.sym >> 8 != 0 {
buf.push((sym.sym >> 8) as u8);
buf.push((sym.sym & 0xff) as u8);
} else {
buf.push(sym.sym as u8);
}
}
pub fn color_to_hex(color: u8) -> (u8, u8, u8) {
let red = color >> 4;
let green = (color >> 2) & 0x3;
let blue = color & 0x3;
debug!(
"Color: {} [{:06x}] {} {} {}",
color, color, red, green, blue
);
(red, green, blue)
}

View File

@@ -3,24 +3,23 @@ use std::{
os::raw::c_uchar,
};
use log::{debug, error, warn};
use super::commands::{self, C0CodeSet, C0Command, C1CodeSet, C1Command};
use super::window::{PenPreset, WindowPreset};
use super::{
commands::{self, C0CodeSet, C0Command, C1CodeSet, C1Command},
window::{PenPreset, WindowPreset},
CCX_DTVCC_MAX_COLUMNS, CCX_DTVCC_MAX_ROWS, CCX_DTVCC_SCREENGRID_COLUMNS,
CCX_DTVCC_SCREENGRID_ROWS,
};
use crate::{
bindings::*,
decoder::output::Writer,
utils::{is_false, is_true},
};
use log::{debug, error, warn};
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
@@ -46,7 +45,7 @@ impl dtvcc_service_decoder {
}
used as u8
} else {
let mut used = self.handle_extended_char(&block[i..]);
let mut used = self.handle_extended_char(&block[1..]);
used += 1; // Since we had CCX_DTVCC_C0_EXT1
used
};
@@ -220,31 +219,28 @@ impl dtvcc_service_decoder {
) {
debug!("dtvcc_handle_CLW_ClearWindows: windows:");
let mut screen_content_changed = false;
unsafe {
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);
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_copy_to_screen(self, window)
}
dtvcc_window_clear(self, i as i32);
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);
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);
self.copy_to_screen(&self.windows[i as usize]);
}
windows_bitmap >>= 1;
self.windows[i as usize].clear_text();
}
windows_bitmap >>= 1;
}
if screen_content_changed {
self.screen_print(encoder, timing);
}
}
if screen_content_changed {
self.screen_print(encoder, timing);
}
}
/// Handle HDW Hide Windows
@@ -259,26 +255,23 @@ impl dtvcc_service_decoder {
debug!("none");
} else {
let mut screen_content_changed = false;
unsafe {
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_true(window.visible) {
screen_content_changed = true;
window.visible = 0;
window.update_time_hide(timing);
let window_ctx = window as *mut dtvcc_window;
if is_false(window.is_empty) {
dtvcc_window_copy_to_screen(self, window_ctx);
}
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_true(window.visible) {
screen_content_changed = true;
window.visible = 0;
window.update_time_hide(timing);
if is_false(window.is_empty) {
self.copy_to_screen(&self.windows[i as usize]);
}
}
windows_bitmap >>= 1;
}
if screen_content_changed && dtvcc_decoder_has_visible_windows(self) == 0 {
self.screen_print(encoder, timing);
}
windows_bitmap >>= 1;
}
if screen_content_changed && !self.has_visible_windows() {
self.screen_print(encoder, timing);
}
}
}
@@ -294,30 +287,27 @@ impl dtvcc_service_decoder {
debug!("none");
} else {
let mut screen_content_changed = false;
unsafe {
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 && is_true(window.is_defined) {
if is_false(window.visible) {
debug!("[W-{}: 0->1]", i);
window.visible = 1;
window.update_time_show(timing);
} else {
debug!("[W-{}: 1->0]", i);
window.visible = 0;
window.update_time_hide(timing);
if is_false(window.is_empty) {
screen_content_changed = true;
dtvcc_window_copy_to_screen(self, window_ctx);
}
for i in 0..CCX_DTVCC_MAX_WINDOWS {
let window = &mut self.windows[i as usize];
if windows_bitmap & 1 == 1 && is_true(window.is_defined) {
if is_false(window.visible) {
debug!("[W-{}: 0->1]", i);
window.visible = 1;
window.update_time_show(timing);
} else {
debug!("[W-{}: 1->0]", i);
window.visible = 0;
window.update_time_hide(timing);
if is_false(window.is_empty) {
screen_content_changed = true;
self.copy_to_screen(&self.windows[i as usize]);
}
}
windows_bitmap >>= 1;
}
if screen_content_changed && dtvcc_decoder_has_visible_windows(self) == 0 {
self.screen_print(encoder, timing);
}
windows_bitmap >>= 1;
}
if screen_content_changed && !self.has_visible_windows() {
self.screen_print(encoder, timing);
}
}
}
@@ -330,41 +320,38 @@ impl dtvcc_service_decoder {
) {
debug!("dtvcc_handle_DLW_DeleteWindows: windows:");
let mut screen_content_changed = false;
unsafe {
if windows_bitmap == 0 {
debug!("none");
} else {
for i in 0..CCX_DTVCC_MAX_WINDOWS {
if windows_bitmap & 1 == 1 {
debug!("Deleting [W{}]", i);
let window = &mut self.windows[i as usize];
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_copy_to_screen(self, window);
if self.current_window == i as i32 {
self.screen_print(encoder, timing);
}
}
let window = &mut self.windows[i as usize];
window.is_defined = 0;
window.visible = 0;
window.time_ms_hide = -1;
window.time_ms_show = -1;
if windows_bitmap == 0 {
debug!("none");
} else {
for i in 0..CCX_DTVCC_MAX_WINDOWS {
if windows_bitmap & 1 == 1 {
debug!("Deleting [W{}]", i);
let window = &mut self.windows[i as usize];
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);
self.copy_to_screen(&self.windows[i as usize]);
if self.current_window == i as i32 {
self.current_window = -1;
self.screen_print(encoder, timing);
}
}
windows_bitmap >>= 1;
let window = &mut self.windows[i as usize];
window.is_defined = 0;
window.visible = 0;
window.time_ms_hide = -1;
window.time_ms_show = -1;
if self.current_window == i as i32 {
self.current_window = -1;
}
}
windows_bitmap >>= 1;
}
if screen_content_changed && dtvcc_decoder_has_visible_windows(self) == 0 {
self.screen_print(encoder, timing);
}
}
if screen_content_changed && !self.has_visible_windows() {
self.screen_print(encoder, timing);
}
}
/// Handle DSW Display Windows
@@ -507,56 +494,54 @@ impl dtvcc_service_decoder {
}
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.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 {
dealloc(window.rows[i] as *mut u8, layout.unwrap());
let ptr = unsafe { 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;
}
// ...also makes the defined windows the current window
self.handle_set_current_window(window_id);
window.is_defined = 1;
window.clear_text();
} else if do_clear_window {
window.clear_text();
}
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 {
unsafe { 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, block: &[c_uchar]) {
@@ -765,24 +750,21 @@ impl dtvcc_service_decoder {
};
if is_true(window.is_defined) {
debug!("dtvcc_process_cr: rolling up");
debug!("dtvcc_process_cr: rolling uper");
let pen_row = window.pen_row;
unsafe {
window.update_time_hide(timing);
let window = window as *mut dtvcc_window;
dtvcc_window_copy_to_screen(self, window);
self.screen_print(encoder, timing);
window.update_time_hide(timing);
self.copy_to_screen(&self.windows[self.current_window as usize]);
self.screen_print(encoder, timing);
if rollup_required {
if no_rollup {
dtvcc_window_clear_row(window, pen_row);
} else {
dtvcc_window_rollup(self, window);
}
if rollup_required {
if no_rollup {
self.windows[self.current_window as usize].clear_row(pen_row as usize);
} else {
self.windows[self.current_window as usize].rollup();
}
dtvcc_window_update_time_show(window, timing);
}
self.windows[self.current_window as usize].update_time_show(timing);
}
}
/// Process Horizontal Carriage Return (HCR)
@@ -793,9 +775,7 @@ impl dtvcc_service_decoder {
}
let window = &mut self.windows[self.current_window as usize];
window.pen_column = 0;
unsafe {
dtvcc_window_clear_row(window, window.pen_row);
}
window.clear_row(window.pen_row as usize);
}
/// Process Form Feed (FF)
pub fn process_ff(&mut self) {
@@ -874,16 +854,19 @@ impl dtvcc_service_decoder {
}
/// Handle RST Reset
pub fn handle_reset(&mut self) {
unsafe {
dtvcc_windows_reset(self);
for id in 0..CCX_DTVCC_MAX_WINDOWS as usize {
let window = &mut self.windows[id];
window.clear_text();
window.is_defined = 0;
window.visible = 0;
window.commands.fill(0);
}
self.current_window = -1;
unsafe { (*self.tv).clear() };
}
/// Process the character and add it to the current window
pub fn process_character(&mut self, sym: dtvcc_symbol) {
/* 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) {
@@ -949,16 +932,165 @@ impl dtvcc_service_decoder {
}
};
}
/// Print the contents of tv screen to the output file
pub fn screen_print(&mut self, encoder: &mut encoder_ctx, timing: &mut ccx_common_timing_ctx) {
debug!("dtvcc_screen_print rust");
self.cc_count += 1;
unsafe {
(*self.tv).cc_count += 1;
let sn = (*self.tv).service_number;
let writer = &mut encoder.dtvcc_writers[(sn - 1) as usize];
dtvcc_screen_update_time_hide(self.tv, get_visible_end(timing, 3));
dtvcc_writer_output(writer, self, encoder);
dtvcc_tv_clear(self);
let tv = &mut (*self.tv);
tv.cc_count += 1;
let sn = tv.service_number;
let writer_ctx = &mut encoder.dtvcc_writers[(sn - 1) as usize];
tv.update_time_hide(timing.get_visible_end(3));
let mut writer = Writer::new(
&mut encoder.cea_708_counter,
encoder.subs_delay,
encoder.write_format,
writer_ctx,
encoder.no_font_color,
&*encoder.transcript_settings,
encoder.no_bom,
);
tv.writer_output(&mut writer).unwrap();
tv.clear();
}
}
/// Copy the contents of window to the tv screen
pub fn copy_to_screen(&self, window: &dtvcc_window) {
if is_true(self.is_window_overlapping(window)) {
debug!("dtvcc_window_copy_to_screen : window needs to be skipped");
return;
} else {
debug!("dtvcc_window_copy_to_screen : no handling required");
}
debug!("dtvcc_window_copy_to_screen: W-{}", window.number);
let anchor = match dtvcc_pen_anchor_point::new(window.anchor_point) {
Ok(val) => val,
Err(e) => {
warn!("{}", e);
return;
}
};
let (mut top, mut left) = match anchor {
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_LEFT => {
(window.anchor_vertical, window.anchor_horizontal)
}
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_CENTER => (
window.anchor_vertical,
window.anchor_horizontal - window.col_count / 2,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_RIGHT => (
window.anchor_vertical,
window.anchor_horizontal - window.col_count,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_LEFT => (
window.anchor_vertical - window.row_count / 2,
window.anchor_horizontal,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_CENTER => (
window.anchor_vertical - window.row_count / 2,
window.anchor_horizontal - window.col_count / 2,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_RIGHT => (
window.anchor_vertical - window.row_count / 2,
window.anchor_horizontal - window.col_count,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_LEFT => (
window.anchor_vertical - window.row_count,
window.anchor_horizontal,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_CENTER => (
window.anchor_vertical - window.row_count,
window.anchor_horizontal - window.col_count / 2,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_RIGHT => (
window.anchor_vertical - window.row_count,
window.anchor_horizontal - window.col_count,
),
};
debug!(
"For window {}: Anchor point -> {}, size {}:{}, real position {}:{}",
window.number, window.anchor_point, window.row_count, window.col_count, top, left
);
debug!("we have top [{}] and left [{}]", top, left);
if top < 0 {
top = 0
}
if left < 0 {
left = 0
}
let copy_rows = if top + window.row_count >= CCX_DTVCC_SCREENGRID_ROWS as i32 {
CCX_DTVCC_SCREENGRID_ROWS as i32 - top
} else {
window.row_count
};
let copy_cols = if left + window.col_count >= CCX_DTVCC_SCREENGRID_COLUMNS as i32 {
CCX_DTVCC_SCREENGRID_COLUMNS as i32 - left
} else {
window.col_count
};
debug!("{}*{} will be copied to the TV.", copy_rows, copy_cols);
unsafe {
let tv = &mut *self.tv;
for row in 0..copy_rows as usize {
for col in 0..CCX_DTVCC_SCREENGRID_COLUMNS as usize {
if col < copy_cols as usize {
tv.chars[top as usize + row][col] = window.rows[row].add(col).read();
}
tv.pen_attribs[top as usize + row][col] = window.pen_attribs[row][col];
tv.pen_colors[top as usize + row][col] = window.pen_colors[row][col];
}
}
tv.update_time_show(window.time_ms_show);
tv.update_time_hide(window.time_ms_hide);
}
}
/// Check if the given window is overlapping other windows
pub fn is_window_overlapping(&self, window: &dtvcc_window) -> u8 {
let mut flag = 0;
let (a_x1, a_x2, a_y1, a_y2) = match window.get_dimensions() {
Ok(val) => val,
Err(e) => {
warn!("{}", e);
return 0;
}
};
for id in 0..CCX_DTVCC_MAX_WINDOWS as usize {
let win_compare = &self.windows[id];
let (b_x1, b_x2, b_y1, b_y2) = match win_compare.get_dimensions() {
Ok(val) => val,
Err(e) => {
warn!("{}", e);
return 0;
}
};
if a_x1 == b_x1 && a_x2 == b_x2 && a_y1 == b_y1 && a_y2 == b_y2 {
continue;
} else if (a_x1 < b_x2)
&& (a_x2 > b_x1)
&& (a_y1 < b_y2)
&& (a_y2 > b_y1)
&& is_true(win_compare.visible)
{
if win_compare.priority < window.priority {
flag = 1
} else {
flag = 0
}
}
}
flag
}
/// True if decoder has any visible window
pub fn has_visible_windows(&self) -> bool {
for id in 0..CCX_DTVCC_MAX_WINDOWS {
if is_true(self.windows[id as usize].visible) {
return true;
}
}
false
}
}

View File

@@ -39,5 +39,5 @@ pub fn get_time_str(time: LLONG) -> String {
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)
format!("{:02}:{:02}:{:02},{:03}", hh, mm, ss, ms)
}

View File

@@ -0,0 +1,431 @@
#[cfg(windows)]
use std::os::windows::io::IntoRawHandle;
use std::{ffi::CStr, fs::File, os::unix::prelude::IntoRawFd};
use super::output::{color_to_hex, write_char, Writer};
use super::timing::get_time_str;
use super::{CCX_DTVCC_SCREENGRID_COLUMNS, CCX_DTVCC_SCREENGRID_ROWS};
use crate::{
bindings::*,
utils::{is_false, is_true},
};
use log::{debug, warn};
impl dtvcc_tv_screen {
/// Clear text from tv screen
pub fn clear(&mut self) {
for row in 0..CCX_DTVCC_SCREENGRID_ROWS as usize {
self.chars[row].fill(dtvcc_symbol::default());
}
self.time_ms_hide = -1;
self.time_ms_show = -1;
}
pub fn update_time_show(&mut self, time: LLONG) {
let prev_time_str = get_time_str(self.time_ms_show);
let curr_time_str = get_time_str(time);
debug!("Screen show time: {} -> {}", prev_time_str, curr_time_str);
if self.time_ms_show == -1 || self.time_ms_show > time {
self.time_ms_show = time;
}
}
pub fn update_time_hide(&mut self, time: LLONG) {
let prev_time_str = get_time_str(self.time_ms_hide);
let curr_time_str = get_time_str(time);
debug!("Screen hide time: {} -> {}", prev_time_str, curr_time_str);
if self.time_ms_hide == -1 || self.time_ms_hide < time {
self.time_ms_hide = time;
}
}
pub fn writer_output(&self, writer: &mut Writer) -> Result<(), String> {
debug!(
"dtvcc_writer_output: writing... [{:?}][{}]",
unsafe { CStr::from_ptr(writer.writer_ctx.filename) },
writer.writer_ctx.fd
);
#[cfg(unix)]
if writer.writer_ctx.filename.is_null() && writer.writer_ctx.fd < 0 {
return Err("Filename missing".to_owned());
} else if writer.writer_ctx.fd < 0 {
let filename = unsafe {
CStr::from_ptr(writer.writer_ctx.filename)
.to_str()
.map_err(|err| err.to_string())
}?;
debug!("dtvcc_writer_output: creating {}", filename);
let file = File::create(filename).map_err(|err| err.to_string())?;
if is_false(writer.no_bom) {
let BOM = [0xef, 0xbb, 0xbf];
writer.write_to_file(&BOM)?;
}
writer.writer_ctx.fd = file.into_raw_fd();
}
#[cfg(windows)]
if writer.writer_ctx.filename.is_null() && writer.fhandle.is_null() {
return Err("Filename missing".to_owned())?;
} else if writer.fhandle.is_null() {
let filename = unsafe {
CStr::from_ptr(writer.writer_ctx.filename)
.to_str()
.map_err(|err| err.to_string())
}?;
debug!("dtvcc_writer_output: creating {}", filename);
let file = File::create(filename).map_err(|err| err.to_string())?;
if is_false(writer.no_bom) {
let BOM = [0xef, 0xbb, 0xbf];
writer.write_to_file(&BOM)?;
}
writer.fhandle = file.into_raw_handle();
}
self.write(writer);
Ok(())
}
pub fn get_write_interval(&self, row_index: usize) -> (usize, usize) {
let mut first = 0;
let mut last = CCX_DTVCC_SCREENGRID_COLUMNS as usize - 1;
for col in 0..CCX_DTVCC_SCREENGRID_COLUMNS as usize {
if self.chars[row_index][col].is_set() {
first = col;
break;
}
}
for col in (0..(CCX_DTVCC_SCREENGRID_COLUMNS as usize - 1)).rev() {
if self.chars[row_index][col].is_set() {
last = col;
break;
}
}
(first, last)
}
pub fn write(&self, writer: &mut Writer) {
let result = match writer.write_format {
ccx_output_format::CCX_OF_SRT => self.write_srt(writer),
ccx_output_format::CCX_OF_SAMI => self.write_sami(writer),
ccx_output_format::CCX_OF_TRANSCRIPT => self.write_transcript(writer),
_ => {
self.write_debug();
Err("Unsupported write format".to_owned())
}
};
if let Err(err) = result {
warn!("{}", err);
}
}
pub fn write_row(
&self,
writer: &mut Writer,
row_index: usize,
use_colors: bool,
) -> Result<(), String> {
let mut buf = Vec::new();
let mut pen_color = dtvcc_pen_color::default();
let mut pen_attribs = dtvcc_pen_attribs::default();
let (first, last) = self.get_write_interval(row_index);
debug!("First: {}, Last: {}", first, last);
for i in 0..last + 1 {
if use_colors {
self.change_pen_color(
&pen_color,
writer.no_font_color,
row_index,
i,
false,
&mut buf,
);
}
self.change_pen_attribs(
&pen_attribs,
writer.no_font_color,
row_index,
i,
false,
&mut buf,
);
self.change_pen_attribs(
&pen_attribs,
writer.no_font_color,
row_index,
i,
true,
&mut buf,
);
if use_colors {
self.change_pen_color(
&pen_color,
writer.no_font_color,
row_index,
i,
true,
&mut buf,
)
}
pen_color = self.pen_colors[row_index][i];
pen_attribs = self.pen_attribs[row_index][i];
if i < first {
buf.push(b' ');
} else {
write_char(&self.chars[row_index][i], &mut buf)
}
}
// there can be unclosed tags or colors after the last symbol in a row
if use_colors {
self.change_pen_color(
&pen_color,
writer.no_font_color,
row_index,
CCX_DTVCC_SCREENGRID_COLUMNS as usize,
false,
&mut buf,
)
}
self.change_pen_attribs(
&pen_attribs,
writer.no_font_color,
row_index,
CCX_DTVCC_SCREENGRID_COLUMNS as usize,
false,
&mut buf,
);
// Tags can still be crossed e.g <f><i>text</f></i>, but testing HTML code has shown that they still are handled correctly.
if writer.writer_ctx.cd != (-1_isize) as iconv_t {
if writer.writer_ctx.charset.is_null() {
debug!("Charset: null");
} else {
let charset = unsafe {
CStr::from_ptr(writer.writer_ctx.charset)
.to_str()
.map_err(|err| err.to_string())?
};
debug!("Charset: {}", charset);
let op = iconv::decode(&buf, charset).map_err(|err| err.to_string())?;
writer.write_to_file(op.as_bytes())?;
}
} else {
writer.write_to_file(&buf)?;
}
Ok(())
}
pub fn write_srt(&self, writer: &mut Writer) -> Result<(), String> {
if self.is_screen_empty(writer) {
return Ok(());
}
if self.time_ms_show + writer.subs_delay < 0 {
return Ok(());
}
let time_show = get_time_str(self.time_ms_show);
let time_hide = get_time_str(self.time_ms_hide);
let counter = *writer.cea_708_counter;
let line = format!(
"{}{}{} --> {}{}",
counter, "\r\n", time_show, time_hide, "\r\n"
);
writer.write_to_file(line.as_bytes())?;
for row_index in 0..CCX_DTVCC_SCREENGRID_ROWS as usize {
if !self.is_row_empty(row_index) {
self.write_row(writer, row_index, true)?;
writer.write_to_file(b"\r\n")?;
}
}
writer.write_to_file(b"\r\n")?;
Ok(())
}
pub fn write_transcript(&self, writer: &mut Writer) -> Result<(), String> {
if self.is_screen_empty(writer) {
return Ok(());
}
if self.time_ms_show + writer.subs_delay < 0 {
return Ok(());
}
let time_show = get_time_str(self.time_ms_show);
let time_hide = get_time_str(self.time_ms_hide);
for row_index in 0..CCX_DTVCC_SCREENGRID_ROWS as usize {
if !self.is_row_empty(row_index) {
let mut buf = String::new();
if is_true(writer.transcript_settings.showStartTime) {
buf.push_str(&time_show);
buf.push('|');
}
if is_true(writer.transcript_settings.showEndTime) {
buf.push_str(&time_hide);
buf.push('|');
}
if is_true(writer.transcript_settings.showCC) {
buf.push_str("CC1|"); //always CC1 because CEA-708 is field-independent
}
if is_true(writer.transcript_settings.showMode) {
buf.push_str("POP|"); //TODO caption mode(pop, rollup, etc.)
}
writer.write_to_file(buf.as_bytes())?;
self.write_row(writer, row_index, false)?;
writer.write_to_file(b"\r\n")?;
}
}
Ok(())
}
pub fn write_sami(&self, writer: &mut Writer) -> Result<(), String> {
if self.is_screen_empty(writer) {
return Err("Sami:- Screen is empty".to_owned());
}
if self.time_ms_show + writer.subs_delay < 0 {
return Err(format!(
"Sami:- timing is -ve, {}:{}",
self.time_ms_show, writer.subs_delay
));
}
if self.cc_count == 1 {
self.write_sami_header(writer)?;
}
let buf = format!(
"<sync start={}><p class=\"unknowncc\">\r\n",
self.time_ms_show + writer.subs_delay
);
writer.write_to_file(buf.as_bytes())?;
for row_index in 0..CCX_DTVCC_SCREENGRID_ROWS as usize {
if !self.is_row_empty(row_index) {
self.write_row(writer, row_index, true)?;
writer.write_to_file("<br>\r\n".as_bytes())?;
}
}
let buf = format!(
"<sync start={}><p class=\"unknowncc\">&nbsp;</p></sync>\r\n\r\n",
self.time_ms_hide + writer.subs_delay
);
writer.write_to_file(buf.as_bytes())?;
Ok(())
}
pub fn write_sami_header(&self, writer: &mut Writer) -> Result<(), String> {
let mut buf = String::new();
buf.push_str("<sami>\r\n");
buf.push_str("<head>\r\n");
buf.push_str("<style type=\"text/css\">\r\n");
buf.push_str("<!--\r\n");
buf.push_str(
"p {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;\r\n",
);
buf.push_str("text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}\r\n");
buf.push_str(".unknowncc {Name:Unknown; lang:en-US; SAMIType:CC;}\r\n");
buf.push_str("-->\r\n");
buf.push_str("</style>\r\n");
buf.push_str("</head>\r\n\r\n");
buf.push_str("<body>\r\n");
writer.write_to_file(buf.as_bytes())?;
Ok(())
}
pub fn write_debug(&self) {
let time_show = get_time_str(self.time_ms_show);
let time_hide = get_time_str(self.time_ms_hide);
debug!("{} --> {}", time_show, time_hide);
for row_index in 0..CCX_DTVCC_SCREENGRID_ROWS as usize {
if !self.is_row_empty(row_index) {
let mut buf = String::new();
let (first, last) = self.get_write_interval(row_index);
for sym in self.chars[row_index][first..=last].iter() {
buf.push_str(&format!("{:04X},", sym.sym));
}
debug!("{}", buf);
}
}
}
pub fn is_screen_empty(&self, writer: &mut Writer) -> bool {
for index in 0..CCX_DTVCC_SCREENGRID_ROWS as usize {
if !self.is_row_empty(index) {
// we will write subtitle
*writer.cea_708_counter += 1;
return false;
}
}
true
}
pub fn is_row_empty(&self, row_index: usize) -> bool {
for col_index in 0..CCX_DTVCC_SCREENGRID_COLUMNS as usize {
if self.chars[row_index][col_index].is_set() {
return false;
}
}
true
}
pub fn change_pen_attribs(
&self,
pen_attribs: &dtvcc_pen_attribs,
no_font_color: bool,
row_index: usize,
col_index: usize,
open: bool,
buf: &mut Vec<u8>,
) {
if no_font_color {
return;
}
let new_pen_attribs = if col_index >= CCX_DTVCC_SCREENGRID_COLUMNS as usize {
dtvcc_pen_attribs::default()
} else {
self.pen_attribs[row_index][col_index]
};
if pen_attribs.italic != new_pen_attribs.italic {
if is_true(pen_attribs.italic) && !open {
buf.extend_from_slice(b"</i>");
} else if is_false(pen_attribs.italic) && open {
buf.extend_from_slice(b"<i>");
}
}
if pen_attribs.underline != new_pen_attribs.underline {
if is_true(pen_attribs.underline) && !open {
buf.extend_from_slice(b"</u>");
} else if is_false(pen_attribs.underline) && open {
buf.extend_from_slice(b"<u>");
}
}
}
pub fn change_pen_color(
&self,
pen_color: &dtvcc_pen_color,
no_font_color: bool,
row_index: usize,
col_index: usize,
open: bool,
buf: &mut Vec<u8>,
) {
if no_font_color {
return;
}
let new_pen_color = if col_index >= CCX_DTVCC_SCREENGRID_COLUMNS as usize {
dtvcc_pen_color::default()
} else {
self.pen_colors[row_index][col_index]
};
if pen_color.fg_color != new_pen_color.fg_color {
if pen_color.fg_color != 0x3F && !open {
// should close older non-white color
buf.extend_from_slice(b"</font>");
} else if new_pen_color.fg_color != 0x3F && open {
debug!("Colors: {}", col_index);
let (mut red, mut green, mut blue) = color_to_hex(new_pen_color.fg_color as u8);
red *= 255 / 3;
green *= 255 / 3;
blue *= 255 / 3;
let font_tag = format!("<font color=\"{:02x}{:02x}{:02x}\">", red, green, blue);
buf.extend_from_slice(font_tag.as_bytes());
}
}
}
}

View File

@@ -1,7 +1,16 @@
use log::debug;
use std::{
alloc::{alloc_zeroed, dealloc, Layout},
intrinsics::copy_nonoverlapping,
};
use super::timing::get_time_str;
use crate::bindings::*;
use super::{
CCX_DTVCC_MAX_COLUMNS, CCX_DTVCC_MAX_ROWS, CCX_DTVCC_SCREENGRID_COLUMNS,
CCX_DTVCC_SCREENGRID_ROWS,
};
use crate::{bindings::*, utils::is_true};
use log::{debug, error};
impl dtvcc_window {
pub fn set_style(&mut self, preset: WindowPreset) {
@@ -46,6 +55,142 @@ impl dtvcc_window {
let time = get_time_str(self.time_ms_hide);
debug!("[W-{}] hide time updated to {}", self.number, time);
}
pub fn get_dimensions(&self) -> Result<(i32, i32, i32, i32), String> {
let anchor = dtvcc_pen_anchor_point::new(self.anchor_point)?;
let (mut x1, mut x2, mut y1, mut y2) = match anchor {
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_LEFT => (
self.anchor_vertical,
self.anchor_vertical + self.row_count,
self.anchor_horizontal,
self.anchor_horizontal + self.col_count,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_CENTER => (
self.anchor_vertical,
self.anchor_vertical + self.row_count,
self.anchor_horizontal - self.col_count,
self.anchor_horizontal + self.col_count / 2,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_RIGHT => (
self.anchor_vertical,
self.anchor_vertical + self.row_count,
self.anchor_horizontal - self.col_count,
self.anchor_horizontal,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_LEFT => (
self.anchor_vertical - self.row_count / 2,
self.anchor_vertical + self.row_count / 2,
self.anchor_horizontal,
self.anchor_horizontal + self.col_count,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_CENTER => (
self.anchor_vertical - self.row_count / 2,
self.anchor_vertical + self.row_count / 2,
self.anchor_horizontal - self.col_count / 2,
self.anchor_horizontal + self.col_count / 2,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_RIGHT => (
self.anchor_vertical - self.row_count / 2,
self.anchor_vertical + self.row_count / 2,
self.anchor_horizontal - self.col_count,
self.anchor_horizontal,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_LEFT => (
self.anchor_vertical - self.row_count,
self.anchor_vertical,
self.anchor_horizontal,
self.anchor_horizontal + self.col_count,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_CENTER => (
self.anchor_vertical - self.row_count,
self.anchor_vertical,
self.anchor_horizontal - self.col_count / 2,
self.anchor_horizontal + self.col_count / 2,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_RIGHT => (
self.anchor_vertical - self.row_count,
self.anchor_vertical,
self.anchor_horizontal - self.col_count,
self.anchor_horizontal,
),
};
if x1 < 0 {
x1 = 0
}
if y1 < 0 {
y1 = 0
}
if x2 > CCX_DTVCC_SCREENGRID_ROWS as i32 {
x2 = CCX_DTVCC_SCREENGRID_ROWS as i32
}
if y2 > CCX_DTVCC_SCREENGRID_COLUMNS as i32 {
y2 = CCX_DTVCC_SCREENGRID_COLUMNS as i32
}
Ok((x1, x2, y1, y2))
}
pub fn clear_text(&mut self) {
// Set pen color to default value
self.pen_color_pattern = dtvcc_pen_color::default();
// Set pen attributes to default value
self.pen_attribs_pattern = dtvcc_pen_attribs::default();
for row in 0..CCX_DTVCC_MAX_ROWS as usize {
self.clear_row(row);
}
self.is_empty = 1;
}
pub fn clear_row(&mut self, row_index: usize) {
if is_true(self.memory_reserved) {
unsafe {
let layout = Layout::array::<dtvcc_symbol>(CCX_DTVCC_MAX_COLUMNS as usize);
if let Err(e) = layout {
error!("clear_row: Incorrect Layout, {}", e);
} else {
let layout = layout.unwrap();
// deallocate previous memory
dealloc(self.rows[row_index] as *mut u8, layout);
// allocate new zero initialized memory
let ptr = alloc_zeroed(layout);
if ptr.is_null() {
error!("clear_row: Not enough memory",);
}
self.rows[row_index] = ptr as *mut dtvcc_symbol;
}
}
for col in 0..CCX_DTVCC_MAX_COLUMNS as usize {
// Set pen attributes to default value
self.pen_attribs[row_index][col] = dtvcc_pen_attribs {
pen_size: dtvcc_pen_size::DTVCC_PEN_SIZE_STANDART as i32,
offset: 0,
text_tag: dtvcc_pen_text_tag::DTVCC_PEN_TEXT_TAG_UNDEFINED_12 as i32,
font_tag: 0,
edge_type: dtvcc_pen_edge::DTVCC_PEN_EDGE_NONE as i32,
underline: 0,
italic: 0,
};
// Set pen color to default value
self.pen_colors[row_index][col] = dtvcc_pen_color {
fg_color: 0x3F,
fg_opacity: 0,
bg_color: 0,
bg_opacity: 0,
edge_color: 0,
};
}
}
}
pub fn rollup(&mut self) {
debug!("roller");
for row_index in 0..(self.row_count - 1) as usize {
let curr_row = self.rows[row_index];
let next_row = self.rows[row_index + 1] as *const dtvcc_symbol;
unsafe { copy_nonoverlapping(next_row, curr_row, CCX_DTVCC_MAX_COLUMNS as usize) };
for col_index in 0..CCX_DTVCC_MAX_COLUMNS as usize {
self.pen_colors[row_index][col_index] = self.pen_colors[row_index + 1][col_index];
self.pen_attribs[row_index][col_index] = self.pen_attribs[row_index + 1][col_index];
}
}
self.clear_row((self.row_count - 1) as usize);
}
}
impl dtvcc_window_pd {
@@ -60,6 +205,23 @@ impl dtvcc_window_pd {
}
}
impl dtvcc_pen_anchor_point {
pub fn new(anchor: i32) -> Result<Self, String> {
match anchor {
0 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_LEFT),
1 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_CENTER),
2 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_RIGHT),
3 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_LEFT),
4 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_CENTER),
5 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_RIGHT),
6 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_LEFT),
7 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_CENTER),
8 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_RIGHT),
_ => Err(String::from("Invalid pen anchor")),
}
}
}
/// Window style for a specific window preset
struct WindowStyle {
justify: dtvcc_window_justify,
@@ -352,3 +514,29 @@ enum Opacity {
_Translucent = 2,
Transparent = 3,
}
impl Default for dtvcc_pen_color {
fn default() -> Self {
Self {
fg_color: 0x3F,
fg_opacity: 0,
bg_color: 0,
bg_opacity: 0,
edge_color: 0,
}
}
}
impl Default for dtvcc_pen_attribs {
fn default() -> Self {
Self {
pen_size: dtvcc_pen_size::DTVCC_PEN_SIZE_STANDART as i32,
offset: 0,
text_tag: dtvcc_pen_text_tag::DTVCC_PEN_TEXT_TAG_UNDEFINED_12 as i32,
font_tag: 0,
edge_type: dtvcc_pen_edge::DTVCC_PEN_EDGE_NONE as i32,
underline: 0,
italic: 0,
}
}
}

View File

@@ -7,30 +7,32 @@
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use decoder::Dtvcc;
use env_logger::{builder, Target};
use log::{warn, LevelFilter};
use std::{io::Write, os::raw::c_int};
/// CCExtractor C bindings generated by bindgen
#[allow(clippy::all)]
pub mod bindings {
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
pub mod decoder;
mod utils;
pub mod utils;
#[cfg(windows)]
use std::os::windows::io::{FromRawHandle, RawHandle};
use std::{io::Write, os::raw::c_int};
use bindings::*;
use decoder::Dtvcc;
use utils::is_true;
use env_logger::{builder, Target};
use log::{warn, LevelFilter};
extern "C" {
static mut cb_708: c_int;
static mut cb_field1: c_int;
static mut cb_field2: c_int;
}
/// Initialize env logger
/// Initialize env logger with custom format, using stdout as target
#[no_mangle]
pub extern "C" fn ccxr_init_logger() {
builder()
@@ -83,8 +85,8 @@ pub fn do_cb(ctx: &mut lib_cc_decode, dtvcc: &mut Dtvcc, cc_block: &[u8]) -> boo
let cc_type = cc_block[0] & 3;
let mut timeok = true;
if ctx.write_format != ccx_output_format_CCX_OF_DVDRAW
&& ctx.write_format != ccx_output_format_CCX_OF_RAW
if ctx.write_format != ccx_output_format::CCX_OF_DVDRAW
&& ctx.write_format != ccx_output_format::CCX_OF_RAW
&& (cc_block[0] == 0xFA || cc_block[0] == 0xFC || cc_block[0] == 0xFD)
&& (cc_block[1] & 0x7F) == 0
&& (cc_block[2] & 0x7F) == 0
@@ -97,7 +99,7 @@ pub fn do_cb(ctx: &mut lib_cc_decode, dtvcc: &mut Dtvcc, cc_block: &[u8]) -> boo
match cc_type {
0 | 1 => {}
2 | 3 => {
let current_time = unsafe { get_fts(ctx.timing, ctx.current_field) };
let current_time = unsafe { (*ctx.timing).get_fts(ctx.current_field as u8) };
ctx.current_field = 3;
if is_true(ctx.extraction_start.set)
&& current_time < ctx.extraction_start.time_in_ms
@@ -110,7 +112,7 @@ pub fn do_cb(ctx: &mut lib_cc_decode, dtvcc: &mut Dtvcc, cc_block: &[u8]) -> boo
ctx.processed_enough = 1;
}
if timeok && ctx.write_format != ccx_output_format_CCX_OF_RAW {
if timeok && ctx.write_format != ccx_output_format::CCX_OF_RAW {
dtvcc.process_cc_data(cc_valid, cc_type, cc_block[1], cc_block[2]);
}
unsafe { cb_708 += 1 }
@@ -120,3 +122,17 @@ pub fn do_cb(ctx: &mut lib_cc_decode, dtvcc: &mut Dtvcc, cc_block: &[u8]) -> boo
}
true
}
#[cfg(windows)]
#[no_mangle]
extern "C" fn ccxr_close_handle(handle: RawHandle) {
use std::fs::File;
if handle.is_null() {
return;
}
unsafe {
// File will close automatically (due to Drop) once it goes out of scope
let file = from_raw_handle(handle);
}
}

View File

@@ -122,6 +122,9 @@
<Component Guid="{1B37F14A-3BA6-4837-8A6F-6EA01A25DA26}">
<File Source="./installer/file_selector_windows_plugin.dll" KeyPath="yes"/>
</Component>
<Component Guid="{B276C96D-9737-4B8C-B55B-60F392DED331}">
<File Source="./installer/url_launcher_windows_plugin.dll" KeyPath="yes"/>
</Component>
<Component Guid="{4B627AA9-55DD-40ED-99F9-54F67EC73887}">
<File Source="./installer/flutter_windows.dll" KeyPath="yes"/>
</Component>