-
Notifications
You must be signed in to change notification settings - Fork 420
/
lib.rs
171 lines (154 loc) · 5.04 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//! Rust library for CCExtractor
//!
//! Currently we are in the process of porting the 708 decoder to rust. See [decoder]
// Allow C naming style
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
/// CCExtractor C bindings generated by bindgen
#[allow(clippy::all)]
pub mod bindings {
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
pub mod decoder;
#[cfg(feature = "hardsubx_ocr")]
pub mod hardsubx;
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 with custom format, using stdout as target
#[no_mangle]
pub extern "C" fn ccxr_init_logger() {
builder()
.format(|buf, record| writeln!(buf, "[CEA-708] {}", record.args()))
.filter_level(LevelFilter::Debug)
.target(Target::Stdout)
.init();
}
/// Process cc_data
///
/// # Safety
/// dec_ctx should not be a null pointer
/// data should point to cc_data of length cc_count
#[no_mangle]
extern "C" fn ccxr_process_cc_data(
dec_ctx: *mut lib_cc_decode,
data: *const ::std::os::raw::c_uchar,
cc_count: c_int,
) -> c_int {
let mut ret = -1;
let mut cc_data: Vec<u8> = (0..cc_count * 3)
.map(|x| unsafe { *data.add(x as usize) })
.collect();
let dec_ctx = unsafe { &mut *dec_ctx };
let dtvcc_ctx = unsafe { &mut *dec_ctx.dtvcc };
let mut dtvcc = Dtvcc::new(dtvcc_ctx);
for cc_block in cc_data.chunks_exact_mut(3) {
if !validate_cc_pair(cc_block) {
continue;
}
let success = do_cb(dec_ctx, &mut dtvcc, cc_block);
if success {
ret = 0;
}
}
ret
}
/// Returns `true` if cc_block pair is valid
///
/// For CEA-708 data, only cc_valid is checked
/// For CEA-608 data, parity is also checked
pub fn validate_cc_pair(cc_block: &mut [u8]) -> bool {
let cc_valid = (cc_block[0] & 4) >> 2;
let cc_type = cc_block[0] & 3;
if cc_valid == 0 {
return false;
}
if cc_type == 0 || cc_type == 1 {
// For CEA-608 data we verify parity.
if verify_parity(cc_block[2]) {
// If the second byte doesn't pass parity, ignore pair
return false;
}
if verify_parity(cc_block[1]) {
// If the first byte doesn't pass parity,
// we replace it with a solid blank and process the pair.
cc_block[1] = 0x7F;
}
}
true
}
/// Returns `true` if data has odd parity
///
/// CC uses odd parity (i.e., # of 1's in byte is odd.)
pub fn verify_parity(data: u8) -> bool {
if data.count_ones() & 1 == 1 {
return true;
}
false
}
/// Process CC data according to its type
pub fn do_cb(ctx: &mut lib_cc_decode, dtvcc: &mut Dtvcc, cc_block: &[u8]) -> bool {
let cc_valid = (cc_block[0] & 4) >> 2;
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
&& (cc_block[0] == 0xFA || cc_block[0] == 0xFC || cc_block[0] == 0xFD)
&& (cc_block[1] & 0x7F) == 0
&& (cc_block[2] & 0x7F) == 0
{
return true;
}
if cc_valid == 1 || cc_type == 3 {
ctx.cc_stats[cc_type as usize] += 1;
match cc_type {
// Type 0 and 1 are for CEA-608 data. Handled by C code, do nothing
0 | 1 => {}
// Type 2 and 3 are for CEA-708 data.
2 | 3 => {
let current_time = unsafe { (*ctx.timing).get_fts(ctx.current_field as u8) };
ctx.current_field = 3;
// Check whether current time is within start and end bounds
if is_true(ctx.extraction_start.set)
&& current_time < ctx.extraction_start.time_in_ms
{
timeok = false;
}
if is_true(ctx.extraction_end.set) && current_time > ctx.extraction_end.time_in_ms {
timeok = false;
ctx.processed_enough = 1;
}
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 }
}
_ => warn!("Invalid cc_type"),
}
}
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 = File::from_raw_handle(handle);
}
}