diff --git a/src/lib_ccx/utility.c b/src/lib_ccx/utility.c index b1a0c22af..27c731fb4 100644 --- a/src/lib_ccx/utility.c +++ b/src/lib_ccx/utility.c @@ -9,6 +9,13 @@ int temp_debug = 0; // This is a convenience variable used to enable/disable debug on variable conditions. Find references to understand. volatile sig_atomic_t change_filename_requested = 0; +#ifndef DISABLE_RUST +extern void ccxr_timestamp_to_srttime(uint64_t timestamp, char *buffer); +extern void ccxr_timestamp_to_vtttime(uint64_t timestamp, char *buffer); +extern void ccxr_millis_to_date(uint64_t timestamp, char *buffer, enum ccx_output_date_format date_format, char millis_separator); +extern int ccxr_stringztoms(const char *s, struct ccx_boundary_time *bt); +#endif + static uint32_t crc32_table[] = { 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, @@ -92,7 +99,8 @@ int stringztoms(const char *s, struct ccx_boundary_time *bt) #ifndef DISABLE_RUST return ccxr_stringztoms(s, bt); #endif - unsigned ss = 0, mm = 0, hh = 0; + unsigned ss = 0, + mm = 0, hh = 0; int value = -1; int colons = 0; const char *c = s; diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index 5c4957377..4f96e52d6 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -165,10 +165,11 @@ dependencies = [ "palette", "pkg-config", "rsmpeg", - "strum", - "strum_macros", + "strum 0.25.0", + "strum_macros 0.25.3", "tesseract-sys", "time", + "url", ] [[package]] @@ -315,6 +316,15 @@ dependencies = [ "toml", ] +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "glob" version = "0.3.1" @@ -367,6 +377,16 @@ dependencies = [ "libc", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.0" @@ -417,8 +437,11 @@ version = "0.1.0" dependencies = [ "bitflags 2.6.0", "derive_more", + "strum 0.26.3", + "strum_macros 0.26.4", "thiserror", "time", + "url", ] [[package]] @@ -537,6 +560,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "phf" version = "0.11.2" @@ -773,6 +802,12 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + [[package]] name = "strum_macros" version = "0.25.3" @@ -786,6 +821,19 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.66", +] + [[package]] name = "syn" version = "1.0.109" @@ -880,6 +928,21 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "toml" version = "0.5.11" @@ -889,12 +952,38 @@ dependencies = [ "serde", ] +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "utf8parse" version = "0.2.1" @@ -937,9 +1026,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys", ] diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index 4c1e73dcf..da5e88652 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -27,6 +27,7 @@ time = "0.3.25" cfg-if = "1.0.0" num-integer = "0.1.45" lib_ccxr = { path = "lib_ccxr" } +url = "2.5.2" [build-dependencies] bindgen = "0.64.0" diff --git a/src/rust/build.rs b/src/rust/build.rs index 8d04bfdf4..7341eeebb 100644 --- a/src/rust/build.rs +++ b/src/rust/build.rs @@ -29,6 +29,8 @@ fn main() { "lib_cc_decode", "cc_subtitle", "ccx_output_format", + "ccx_boundary_time", + "gop_time_code", "ccx_s_options", "ccx_s_teletext_config", "ccx_output_format", @@ -87,4 +89,8 @@ fn main() { .expect("Couldn't write bindings!"); } -const RUSTIFIED_ENUMS: &[&str] = &["dtvcc_(window|pen)_.*", "ccx_output_format"]; +const RUSTIFIED_ENUMS: &[&str] = &[ + "dtvcc_(window|pen)_.*", + "ccx_output_format", + "ccx_output_date_format", +]; diff --git a/src/rust/lib_ccxr/Cargo.lock b/src/rust/lib_ccxr/Cargo.lock index aecb788a5..4fd82ee25 100644 --- a/src/rust/lib_ccxr/Cargo.lock +++ b/src/rust/lib_ccxr/Cargo.lock @@ -36,6 +36,31 @@ dependencies = [ "syn", ] +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "itoa" version = "1.0.11" @@ -48,8 +73,11 @@ version = "0.1.0" dependencies = [ "bitflags", "derive_more", + "strum", + "strum_macros", "thiserror", "time", + "url", ] [[package]] @@ -58,6 +86,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "powerfmt" version = "0.2.0" @@ -91,6 +125,12 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "semver" version = "1.0.23" @@ -99,29 +139,48 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "syn" -version = "2.0.71" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -130,18 +189,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -179,8 +238,49 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] diff --git a/src/rust/lib_ccxr/Cargo.toml b/src/rust/lib_ccxr/Cargo.toml index a54d59b6a..7911005e4 100644 --- a/src/rust/lib_ccxr/Cargo.toml +++ b/src/rust/lib_ccxr/Cargo.toml @@ -10,10 +10,17 @@ bitflags = "2.6.0" thiserror = "1.0.61" time = { version = "0.3.36", features = ["macros", "formatting"] } derive_more = "0.99.18" +url = "2.5.2" +strum = "0.26.3" +strum_macros = "0.26.4" [features] default = [ + "enable_sharing", + "wtv_debug", "enable_ffmpeg", + "debug", + "with_libcurl", ] enable_sharing = [] wtv_debug = [] diff --git a/src/rust/lib_ccxr/src/common/constants.rs b/src/rust/lib_ccxr/src/common/constants.rs index 82237bafc..979be426b 100644 --- a/src/rust/lib_ccxr/src/common/constants.rs +++ b/src/rust/lib_ccxr/src/common/constants.rs @@ -1,29 +1,55 @@ -//! Provides common constant types throughout the codebase. -//! Rust equivalent for `ccx_common_constants.c` file in C. -//! -//! # Conversion Guide -//! -//! | From | To | -//! |--------------------------------|--------------------------------------------| -//! | `ccx_avc_nal_types` | [`AvcNalType`] | -//! | `ccx_stream_type` | [`StreamType`] | -//! | `ccx_mpeg_descriptor` | [`MpegDescriptor`] | -//! | `ccx_datasource` | [`DataSource`] | -//! | `ccx_output_format` | [`OutputFormat`] | -//! | `ccx_stream_mode_enum` | [`StreamMode`] | -//! | `ccx_bufferdata_type` | [`BufferdataType`] | -//! | `ccx_frame_type` | [`FrameType`] | -//! | `ccx_code_type` | [`Codec`] | -//! | `cdp_section_type` | [`CdpSectionType`] | -//! | `cc_types[4]` | [`CCTypes`] | -//! | `CCX_TXT_*` macros | [`CcxTxt`] | -//! | `language[NB_LANGUAGE]` | [`Language`] | -//! | `DEF_VAL_*` macros | [`CreditTiming`] | -//! | `IS_FEASIBLE` macro | [`Codec::is_feasible`] | -//! | `IS_VALID_TELETEXT_DESC` macro | [`MpegDescriptor::is_valid_teletext_desc`] | - use std::ffi::OsStr; +use strum_macros::{EnumString, FromRepr}; + +pub const DTVCC_MAX_SERVICES: usize = 63; + +/// An enum of all the available formats for the subtitle output. +#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)] +pub enum OutputFormat { + #[default] + Raw, + Srt, + Sami, + Transcript, + Rcwt, + Null, + SmpteTt, + SpuPng, + DvdRaw, // See -d at http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_TOOLS.HTML#CCExtract + WebVtt, + SimpleXml, + G608, + Curl, + Ssa, + Mcc, + Scc, + Ccd, +} +// Provides common constant types throughout the codebase. +// Rust equivalent for `ccx_common_constants.c` file in C. +// +// # Conversion Guide +// +// | From | To | +// |--------------------------------|--------------------------------------------| +// | `ccx_avc_nal_types` | [`AvcNalType`] | +// | `ccx_stream_type` | [`StreamType`] | +// | `ccx_mpeg_descriptor` | [`MpegDescriptor`] | +// | `ccx_datasource` | [`DataSource`] | +// | `ccx_output_format` | [`OutputFormat`] | +// | `ccx_stream_mode_enum` | [`StreamMode`] | +// | `ccx_bufferdata_type` | [`BufferdataType`] | +// | `ccx_frame_type` | [`FrameType`] | +// | `ccx_code_type` | [`Codec`] | +// | `cdp_section_type` | [`CdpSectionType`] | +// | `cc_types[4]` | [`CCTypes`] | +// | `CCX_TXT_*` macros | [`CcxTxt`] | +// | `language[NB_LANGUAGE]` | [`Language`] | +// | `DEF_VAL_*` macros | [`CreditTiming`] | +// | `IS_FEASIBLE` macro | [`Codec::is_feasible`] | +// | `IS_VALID_TELETEXT_DESC` macro | [`MpegDescriptor::is_valid_teletext_desc`] | + // RCWT header (11 bytes): // byte(s) value description (All values below are hex numbers, not // actual numbers or values @@ -164,7 +190,9 @@ pub enum AvcNalType { } // MPEG-2 TS stream types +#[derive(Default, Debug, PartialEq, Eq, FromRepr, Clone, Copy)] pub enum StreamType { + #[default] Unknownstream = 0, /* The later constants are defined by MPEG-TS standard @@ -209,36 +237,18 @@ pub enum MpegDescriptor { DataComp = 0xfd, // Consider to change DESC to DSC } +#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] pub enum DataSource { + #[default] File, Stdin, Network, Tcp, } -/// An enum of all the available formats for the subtitle output. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum OutputFormat { - Raw, - Srt, - Sami, - Transcript, - Rcwt, - Null, - SmpteTt, - SpuPng, - DvdRaw, // See -d at http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_TOOLS.HTML#CCExtract - WebVtt, - SimpleXml, - G608, - Curl, - Ssa, - Mcc, - Scc, - Ccd, -} - +#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] pub enum StreamMode { + #[default] ElementaryOrNotFound = 0, Transport = 1, Program = 2, @@ -273,22 +283,20 @@ pub enum BufferdataType { DvdSubtitle, } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum FrameType { - ResetOrUnknown, - IFrame, - PFrame, - BFrame, - DFrame, -} - -#[derive(PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Codec { Any, Teletext, Dvb, IsdbCc, AtscCc, +} + +#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] +pub enum SelectCodec { + All, + Some(Codec), + #[default] None, } @@ -316,7 +324,10 @@ pub enum CcxTxt { InUse = 2, // Positive auto-detected, or forced, etc } +#[derive(Debug, Default, EnumString, Clone, Copy, PartialEq, Eq)] +#[strum(serialize_all = "lowercase")] pub enum Language { + #[default] Und, // Undefined Eng, Afr, diff --git a/src/rust/lib_ccxr/src/common/mod.rs b/src/rust/lib_ccxr/src/common/mod.rs index 7976d653e..61a0a8b59 100644 --- a/src/rust/lib_ccxr/src/common/mod.rs +++ b/src/rust/lib_ccxr/src/common/mod.rs @@ -1,3 +1,25 @@ //! Provides common types throughout the codebase. +//! +//! # Conversion Guide +//! +//! | From | To | +//! |-------------------------|----------------------------| +//! | `ccx_output_format` | [`OutputFormat`] | +//! | `ccx_avc_nal_types` | [`AvcNalType`] | +//! | `ccx_stream_type` | [`StreamType`] | +//! | `ccx_mpeg_descriptor` | [`MpegDescriptor`] | +//! | `ccx_datasource` | [`DataSource`] | +//! | `ccx_stream_mode_enum` | [`StreamMode`] | +//! | `ccx_bufferdata_type` | [`BufferdataType`] | +//! | `ccx_frame_type` | [`FrameType`] | +//! | `ccx_code_type` | [`Codec`], [`SelectCodec`] | +//! | `cdp_section_type` | [`CdpSectionType`] | +//! | `language[NB_LANGUAGE]` | [`Language`] | -pub mod constants; +mod constants; +mod options; +mod teletext; + +pub use constants::*; +pub use options::*; +pub use teletext::*; diff --git a/src/rust/lib_ccxr/src/common/options.rs b/src/rust/lib_ccxr/src/common/options.rs new file mode 100644 index 000000000..330f692d6 --- /dev/null +++ b/src/rust/lib_ccxr/src/common/options.rs @@ -0,0 +1,504 @@ +use url::Url; + +use std::path::PathBuf; + +use crate::common::{ + DataSource, Language, OutputFormat, SelectCodec, StreamMode, StreamType, DTVCC_MAX_SERVICES, +}; +use crate::hardsubx::{ColorHue, OcrMode}; +use crate::time::units::{Timestamp, TimestampFormat}; +use crate::util::encoding::Encoding; +use crate::util::log::{DebugMessageMask, OutputTarget}; +use crate::util::time::stringztoms; + +#[derive(Debug, Clone)] +pub struct EncodersTranscriptFormat { + pub show_start_time: bool, + pub show_end_time: bool, + pub show_mode: bool, + pub show_cc: bool, + pub relative_timestamp: bool, + pub xds: bool, + pub use_colors: bool, + pub is_final: bool, +} + +impl Default for EncodersTranscriptFormat { + fn default() -> Self { + Self { + show_start_time: false, + show_end_time: false, + show_mode: false, + show_cc: false, + relative_timestamp: true, + xds: false, + use_colors: true, + is_final: false, + } + } +} + +#[derive(Debug, Default, Copy, Clone)] +pub enum FrameType { + #[default] + ResetOrUnknown = 0, + IFrame = 1, + PFrame = 2, + BFrame = 3, + DFrame = 4, +} + +#[derive(Debug, Default, Clone, Copy)] +pub struct CommonTimingCtx { + pub pts_set: i32, // 0 = No, 1 = received, 2 = min_pts set + pub min_pts_adjusted: i32, // 0 = No, 1=Yes (don't adjust again) + pub current_pts: i64, + pub current_picture_coding_type: FrameType, + pub current_tref: i32, // Store temporal reference of current frame + pub min_pts: i64, + pub max_pts: i64, + pub sync_pts: i64, + pub minimum_fts: i64, // No screen should start before this FTS + pub fts_now: i64, // Time stamp of current file (w/ fts_offset, w/o fts_global) + pub fts_offset: i64, // Time before first sync_pts + pub fts_fc_offset: i64, // Time before first GOP + pub fts_max: i64, // Remember the maximum fts that we saw in current file + pub fts_global: i64, // Duration of previous files (-ve mode) + pub sync_pts2fts_set: i32, // 0 = No, 1 = Yes + pub sync_pts2fts_fts: i64, + pub sync_pts2fts_pts: i64, + pub pts_reset: i32, // 0 = No, 1 = Yes. PTS resets when current_pts is lower than prev +} + +impl Default for DecoderDtvccReport { + fn default() -> Self { + Self { + reset_count: 0, + services: [0; DTVCC_MAX_SERVICES], + } + } +} + +#[derive(Debug, Copy, Clone)] + +pub struct DecoderDtvccReport { + pub reset_count: i32, + pub services: [u32; DTVCC_MAX_SERVICES], +} + +impl Default for DecoderDtvccSettings { + fn default() -> Self { + Self { + enabled: false, + print_file_reports: true, + no_rollup: false, + report: None, + active_services_count: 0, + services_enabled: [false; DTVCC_MAX_SERVICES], + timing: CommonTimingCtx::default(), + } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct DecoderDtvccSettings { + pub enabled: bool, + pub print_file_reports: bool, + pub no_rollup: bool, + pub report: Option, + pub active_services_count: i32, + pub services_enabled: [bool; DTVCC_MAX_SERVICES], + pub timing: CommonTimingCtx, +} + +#[derive(Default, Debug, Copy, Clone)] +pub struct Decoder608Settings { + pub direct_rollup: i32, + pub force_rollup: i32, + pub no_rollup: bool, + pub default_color: Decoder608ColorCode, + pub screens_to_process: i32, + pub report: Option, +} + +#[derive(Debug, Default, Copy, Clone)] +pub struct Decoder608Report { + pub xds: bool, + pub cc_channels: [u8; 4], +} + +impl Default for Decoder608ColorCode { + fn default() -> Self { + Self::Userdefined + } +} + +#[derive(Debug, Copy, Clone)] +pub enum Decoder608ColorCode { + White = 0, + Green = 1, + Blue = 2, + Cyan = 3, + Red = 4, + Yellow = 5, + Magenta = 6, + Userdefined = 7, + Black = 8, + Transparent = 9, + Max, +} + +#[derive(Debug, Default, PartialEq, Eq, Clone)] +pub enum DtvccServiceCharset { + #[default] + None, + Same(String), + Unique(Box<[String; DTVCC_MAX_SERVICES]>), +} + +#[allow(dead_code)] +#[derive(Default, Debug, Clone)] +pub struct DemuxerConfig { + /// Regular TS or M2TS + pub m2ts: bool, + pub auto_stream: StreamMode, + + /* subtitle codec type */ + pub codec: SelectCodec, + pub nocodec: SelectCodec, + + /// Try to find a stream with captions automatically (no -pn needed) + pub ts_autoprogram: bool, + pub ts_allprogram: bool, + /// PID for stream that holds caption information + pub ts_cappids: Vec, + /// If 1, never mess with the selected PID + pub ts_forced_cappid: bool, + /// Specific program to process in TS files, if a forced program is given + pub ts_forced_program: Option, + /// User WANTED stream type (i.e. use the stream that has this type) + pub ts_datastreamtype: StreamType, + /// User selected (forced) stream type + pub ts_forced_streamtype: StreamType, +} + +impl Default for EncoderConfig { + fn default() -> Self { + Self { + extract: 1, + dtvcc_extract: false, + gui_mode_reports: false, + output_filename: String::default(), + write_format: OutputFormat::default(), + keep_output_closed: false, + force_flush: false, + append_mode: false, + ucla: false, + encoding: Encoding::default(), + date_format: TimestampFormat::default(), + autodash: false, + trim_subs: false, + sentence_cap: false, + splitbysentence: false, + curlposturl: None, + filter_profanity: false, + with_semaphore: false, + start_credits_text: String::default(), + end_credits_text: String::default(), + startcreditsnotbefore: stringztoms("0").unwrap_or_default(), + startcreditsnotafter: stringztoms("5:00").unwrap_or_default(), + startcreditsforatleast: stringztoms("2").unwrap_or_default(), + startcreditsforatmost: stringztoms("5").unwrap_or_default(), + endcreditsforatleast: stringztoms("2").unwrap_or_default(), + endcreditsforatmost: stringztoms("5").unwrap_or_default(), + transcript_settings: EncodersTranscriptFormat::default(), + send_to_srv: false, + no_bom: true, + first_input_file: String::default(), + multiple_files: false, + no_font_color: false, + no_type_setting: false, + cc_to_stdout: false, + line_terminator_lf: false, + subs_delay: Timestamp::default(), + program_number: 0, + in_format: 1, + nospupngocr: false, + force_dropframe: false, + render_font: PathBuf::default(), + render_font_italics: PathBuf::default(), + services_enabled: [false; DTVCC_MAX_SERVICES], + services_charsets: DtvccServiceCharset::None, + extract_only_708: false, + } + } +} + +#[allow(dead_code)] +#[derive(Debug, Clone)] +pub struct EncoderConfig { + /// Extract 1st (1), 2nd (2) or both fields (12) + pub extract: u8, + pub dtvcc_extract: bool, + // If true, output in stderr progress updates so the GUI can grab them + pub gui_mode_reports: bool, + pub output_filename: String, + pub write_format: OutputFormat, + pub keep_output_closed: bool, + /// Force flush on content write + pub force_flush: bool, + /// Append mode for output files + pub append_mode: bool, + /// true if -UCLA used, false if not + pub ucla: bool, + + pub encoding: Encoding, + pub date_format: TimestampFormat, + /// Add dashes (-) before each speaker automatically? + pub autodash: bool, + /// " Remove spaces at sides? " + pub trim_subs: bool, + /// FIX CASE? = Fix case? + pub sentence_cap: bool, + /// Split text into complete sentences and prorate time? + pub splitbysentence: bool, + + /// If out=curl, where do we send the data to? + #[cfg(feature = "with_libcurl")] + pub curlposturl: Option, + + /// Censors profane words from subtitles + pub filter_profanity: bool, + + /// Write a .sem file on file open and delete it on close? + pub with_semaphore: bool, + /* Credit stuff */ + pub start_credits_text: String, + pub end_credits_text: String, + pub startcreditsnotbefore: Timestamp, // Where to insert start credits, if possible + pub startcreditsnotafter: Timestamp, + pub startcreditsforatleast: Timestamp, // How long to display them? + pub startcreditsforatmost: Timestamp, + pub endcreditsforatleast: Timestamp, + pub endcreditsforatmost: Timestamp, + + /// Keeps the settings for generating transcript output files. + pub transcript_settings: EncodersTranscriptFormat, + pub send_to_srv: bool, + /// Set to true when no BOM (Byte Order Mark) should be used for files. + /// Note, this might make files unreadable in windows! + pub no_bom: bool, + pub first_input_file: String, + pub multiple_files: bool, + pub no_font_color: bool, + pub no_type_setting: bool, + /// If this is set to true, the stdout will be flushed when data was written to the screen during a process_608 call. + pub cc_to_stdout: bool, + /// false = CRLF, true = LF + pub line_terminator_lf: bool, + /// ms to delay (or advance) subs + pub subs_delay: Timestamp, + pub program_number: u32, + pub in_format: u8, + // true if we don't want to OCR bitmaps to add the text as comments in the XML file in spupng + pub nospupngocr: bool, + + // MCC File + /// true if dropframe frame count should be used. defaults to no drop frame. + pub force_dropframe: bool, + + // text -> png (text render) + /// The font used to render text if needed (e.g. teletext->spupng) + pub render_font: PathBuf, + pub render_font_italics: PathBuf, + + //CEA-708 + pub services_enabled: [bool; DTVCC_MAX_SERVICES], + pub services_charsets: DtvccServiceCharset, + // true if only 708 subs extraction is enabled + pub extract_only_708: bool, +} + +/// Options from user parameters +#[derive(Debug, Default, Clone)] +pub struct Options { + /// Extract 1st, 2nd or both fields. Can be 1, 2 or 12 respectively. + pub extract: u8, + /// Disable roll-up emulation (no duplicate output in generated file) + pub no_rollup: bool, + pub noscte20: bool, + pub webvtt_create_css: bool, + /// Channel we want to dump in srt mode + pub cc_channel: u8, + pub buffer_input: bool, + pub nofontcolor: bool, + pub nohtmlescape: bool, + pub notypesetting: bool, + /// The start of the segment we actually process + pub extraction_start: Timestamp, + /// The end of the segment we actually process + pub extraction_end: Timestamp, + pub print_file_reports: bool, + /// Contains the settings for the 608 decoder. + pub settings_608: Decoder608Settings, + /// Same for 708 decoder + pub settings_dtvcc: DecoderDtvccSettings, + /// Is 608 enabled by explicitly using flags(-1,-2,-12) + pub is_608_enabled: bool, + /// Is 708 enabled by explicitly using flags(-svc) + pub is_708_enabled: bool, + + /// Disabled by -ve or --videoedited + pub binary_concat: bool, + /// Use GOP instead of PTS timing (None=do as needed, true=always, false=never) + pub use_gop_as_pts: Option, + /// Replace 0000 with 8080 in HDTV (needed for some cards) + pub fix_padding: bool, + /// If true, output in stderr progress updates so the GUI can grab them + pub gui_mode_reports: bool, + /// If true, suppress the output of the progress to stdout + pub no_progress_bar: bool, + /// Extra capitalization word file + pub sentence_cap_file: PathBuf, + /// None -> Not a complete file but a live stream, without timeout + /// + /// 0 -> A regular file + /// + /// \> 0 -> Live stream with a timeout of this value in seconds + pub live_stream: Option, + /// Extra profanity word file + pub filter_profanity_file: PathBuf, + pub messages_target: OutputTarget, + /// If true, add WebVTT X-TIMESTAMP-MAP header + pub timestamp_map: bool, + /* Levenshtein's parameters, for string comparison */ + /// false => don't attempt to correct typos with this algorithm + pub dolevdist: bool, + /// Means 2 fails or less is "the same" + pub levdistmincnt: u8, + /// Means 10% or less is also "the same" + pub levdistmaxpct: u8, + /// Look for captions in all packets when everything else fails + pub investigate_packets: bool, + /// Disable pruning of padding cc blocks + pub fullbin: bool, + /// Disable syncing + pub nosync: bool, + /// If true, use PID=1003, process specially and so on + pub hauppauge_mode: bool, + /// Fix broken Windows 7 conversion + pub wtvconvertfix: bool, + pub wtvmpeg2: bool, + /// Use myth-tv mpeg code? false=no, true=yes, None=auto + pub auto_myth: Option, + /* MP4 related stuff */ + /// Process the video track even if a CC dedicated track exists. + pub mp4vidtrack: bool, + /// If true, extracts chapters (if present), from MP4 files. + pub extract_chapters: bool, + /* General settings */ + /// Force the use of pic_order_cnt_lsb in AVC/H.264 data streams + pub usepicorder: bool, + /// 1 = full output. 2 = live output. 3 = both + pub xmltv: u8, + /// interval in seconds between writing xmltv output files in live mode + pub xmltvliveinterval: Timestamp, + /// interval in seconds between writing xmltv full file output + pub xmltvoutputinterval: Timestamp, + pub xmltvonlycurrent: bool, + pub keep_output_closed: bool, + /// Force flush on content write + pub force_flush: bool, + /// Append mode for output files + pub append_mode: bool, + /// true if UCLA used, false if not + pub ucla: bool, + /// true if ticker text style burned in subs, false if not + pub tickertext: bool, + /// true if burned-in subtitles to be extracted + pub hardsubx: bool, + /// true if both burned-in and not burned in need to be extracted + pub hardsubx_and_common: bool, + /// The name of the language stream for DVB + pub dvblang: Option, + /// The name of the .traineddata file to be loaded with tesseract + pub ocrlang: PathBuf, + /// The Tesseract OEM mode, could be 0 (default), 1 or 2 + pub ocr_oem: u8, + /// How to quantize the bitmap before passing to to tesseract + /// (0 = no quantization at all, 1 = CCExtractor's internal, + /// 2 = reduce distinct color count in image for faster results.) + pub ocr_quantmode: u8, + /// The name of the language stream for MKV + pub mkvlang: Option, + /// If true, the video stream will be processed even if we're using a different one for subtitles. + pub analyze_video_stream: bool, + + /*HardsubX related stuff*/ + pub hardsubx_ocr_mode: OcrMode, + pub hardsubx_min_sub_duration: Timestamp, + pub hardsubx_detect_italics: bool, + pub hardsubx_conf_thresh: f64, + pub hardsubx_hue: ColorHue, + pub hardsubx_lum_thresh: f64, + + /// Keeps the settings for generating transcript output files. + pub transcript_settings: EncodersTranscriptFormat, + pub date_format: TimestampFormat, + pub send_to_srv: bool, + pub write_format: OutputFormat, + pub write_format_rewritten: bool, + pub use_ass_instead_of_ssa: bool, + pub use_webvtt_styling: bool, + + /* Networking */ + pub udpsrc: Option, + pub udpaddr: Option, + /// Non-zero => Listen for UDP packets on this port, no files. + pub udpport: u16, + pub tcpport: Option, + pub tcp_password: Option, + pub tcp_desc: Option, + pub srv_addr: Option, + pub srv_port: Option, + /// Do NOT set time automatically? + pub noautotimeref: bool, + /// Files, stdin or network + pub input_source: DataSource, + + pub output_filename: Option, + + /// List of files to process + pub inputfile: Option>, + pub demux_cfg: DemuxerConfig, + pub enc_cfg: EncoderConfig, + /// ms to delay (or advance) subs + pub subs_delay: Timestamp, + /// If true, the stdout will be flushed when data was written to the screen during a process_608 call. + pub cc_to_stdout: bool, + /// If true, the PES Header will be printed to console (debugging purposes) + pub pes_header_to_stdout: bool, + /// If true, the program will ignore PTS jumps. + /// Sometimes this parameter is required for DVB subs with > 30s pause time + pub ignore_pts_jumps: bool, + pub multiprogram: bool, + pub out_interval: i32, + pub segment_on_key_frames_only: bool, + pub debug_mask: DebugMessageMask, + + #[cfg(feature = "with_libcurl")] + pub curlposturl: Option, + + //CC sharing + #[cfg(feature = "enable_sharing")] + pub sharing_enabled: bool, + #[cfg(feature = "enable_sharing")] + pub sharing_url: Option, + #[cfg(feature = "enable_sharing")] + //Translating + pub translate_enabled: bool, + #[cfg(feature = "enable_sharing")] + pub translate_langs: Option, + #[cfg(feature = "enable_sharing")] + pub translate_key: Option, +} diff --git a/src/rust/lib_ccxr/src/common/teletext.rs b/src/rust/lib_ccxr/src/common/teletext.rs new file mode 100644 index 000000000..54bac7889 --- /dev/null +++ b/src/rust/lib_ccxr/src/common/teletext.rs @@ -0,0 +1,87 @@ +use crate::time::units::{Timestamp, TimestampFormat}; +use std::{cell::Cell, fmt}; + +use super::OutputFormat; + +#[derive(Debug)] +pub struct TeletextConfig { + /// should telxcc logging be verbose? + pub verbose: bool, + /// teletext page containing cc we want to filter + pub page: Cell, + /// Page selected by user, which MIGHT be different to `page` depending on autodetection stuff + pub user_page: u16, + /// false = Don't attempt to correct errors + pub dolevdist: bool, + /// Means 2 fails or less is "the same" + pub levdistmincnt: u8, + /// Means 10% or less is also "the same" + pub levdistmaxpct: u8, + /// Segment we actually process + pub extraction_start: Option, + /// Segment we actually process + pub extraction_end: Option, + pub write_format: OutputFormat, + pub date_format: TimestampFormat, + /// Do NOT set time automatically? + pub noautotimeref: bool, + pub nofontcolor: bool, + pub nohtmlescape: bool, + pub latrusmap: bool, +} + +impl Default for TeletextConfig { + fn default() -> Self { + Self { + verbose: true, + page: TeletextPageNumber(0).into(), + user_page: 0, + dolevdist: false, + levdistmincnt: 0, + levdistmaxpct: 0, + extraction_start: None, + extraction_end: None, + write_format: OutputFormat::default(), + date_format: TimestampFormat::default(), + noautotimeref: false, + nofontcolor: false, + nohtmlescape: false, + latrusmap: false, + } + } +} + +/// Represents a Teletext Page Number in its bitcode representation. +/// +/// It can be easily contructed from a [`u16`]. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct TeletextPageNumber(u16); + +impl From for TeletextPageNumber { + fn from(value: u16) -> TeletextPageNumber { + TeletextPageNumber(value) + } +} + +impl fmt::Display for TeletextPageNumber { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:03x}", self.0) + } +} + +impl TeletextPageNumber { + /// Return the magazine and packet bits. + pub fn magazine(&self) -> u8 { + ((self.0 >> 8) & 0x0f) as u8 + } + + /// Return the page bits. + pub fn page(&self) -> u8 { + (self.0 & 0xff) as u8 + } + + /// Return the page number after converting the page bits in bcd format to normal integer. + pub fn bcd_page_to_u16(&self) -> u16 { + ((self.0 & 0xf00) >> 8) * 100 + ((self.0 & 0xf0) >> 4) * 10 + (self.0 & 0xf) + } +} diff --git a/src/rust/lib_ccxr/src/hardsubx.rs b/src/rust/lib_ccxr/src/hardsubx.rs new file mode 100644 index 000000000..9cacb9f8d --- /dev/null +++ b/src/rust/lib_ccxr/src/hardsubx.rs @@ -0,0 +1,35 @@ +#[derive(Default, Debug, Clone, Copy)] +pub enum OcrMode { + #[default] + Frame = 0, + Word = 1, + Letter = 2, +} + +#[derive(Default, Debug, Clone, Copy)] +pub enum ColorHue { + #[default] + White, + Yellow, + Green, + Cyan, + Blue, + Magenta, + Red, + Custom(f64), +} + +impl ColorHue { + pub fn get_hue(&self) -> f64 { + match self { + ColorHue::White => 0.0, + ColorHue::Yellow => 60.0, + ColorHue::Green => 120.0, + ColorHue::Cyan => 180.0, + ColorHue::Blue => 240.0, + ColorHue::Magenta => 300.0, + ColorHue::Red => 0.0, + ColorHue::Custom(hue) => *hue, + } + } +} diff --git a/src/rust/lib_ccxr/src/lib.rs b/src/rust/lib_ccxr/src/lib.rs index c1172cf5c..15c1b3e68 100644 --- a/src/rust/lib_ccxr/src/lib.rs +++ b/src/rust/lib_ccxr/src/lib.rs @@ -1,3 +1,4 @@ pub mod common; +pub mod hardsubx; pub mod time; pub mod util; diff --git a/src/rust/lib_ccxr/src/time/units.rs b/src/rust/lib_ccxr/src/time/units.rs index 34d620827..99d9e3ccb 100644 --- a/src/rust/lib_ccxr/src/time/units.rs +++ b/src/rust/lib_ccxr/src/time/units.rs @@ -19,7 +19,7 @@ extern "C" { /// Represents a timestamp in milliseconds. /// /// The number can be negetive. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Add, Sub, Neg)] +#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Add, Sub, Neg)] pub struct Timestamp { millis: i64, } @@ -40,6 +40,7 @@ pub enum TimestampError { } /// Represents the different string formats for [`Timestamp`]. +#[derive(Default, Debug, PartialEq, Clone, Copy)] pub enum TimestampFormat { /// Format: blank string. /// @@ -50,6 +51,7 @@ pub enum TimestampFormat { /// let output = timestamp.to_formatted_time(TimestampFormat::None).unwrap(); /// assert_eq!(output, ""); /// ``` + #[default] None, /// Format: `{hour:02}:{minute:02}:{second:02}`. @@ -107,6 +109,17 @@ pub enum TimestampFormat { HHMMSSFFF, } +impl TimestampFormat { + /// Returns the millis_separator for the TimestampFormat + pub fn millis_separator(&self) -> char { + match self { + TimestampFormat::Seconds { millis_separator } => *millis_separator, + TimestampFormat::Date { millis_separator } => *millis_separator, + _ => ',', + } + } +} + impl Timestamp { /// Create a new [`Timestamp`] based on the number of milliseconds since the Unix Epoch. pub fn now() -> Timestamp { @@ -155,7 +168,7 @@ impl Timestamp { /// /// # Examples /// ```rust - /// # use lib_ccxr::time::units::Timestamp;; + /// # use lib_ccxr::time::units::Timestamp; /// let timestamp = Timestamp::from_millis(6524365); /// assert_eq!(timestamp.millis(), 6524365); /// ``` @@ -167,7 +180,7 @@ impl Timestamp { /// /// # Examples /// ```rust - /// # use lib_ccxr::time::units::Timestamp;; + /// # use lib_ccxr::time::units::Timestamp; /// let timestamp = Timestamp::from_millis(6524365); /// assert_eq!(timestamp.seconds(), 6524); /// ``` @@ -181,7 +194,7 @@ impl Timestamp { /// /// # Examples /// ```rust - /// # use lib_ccxr::time::units::Timestamp;; + /// # use lib_ccxr::time::units::Timestamp; /// let timestamp = Timestamp::from_millis(6524365); /// assert_eq!(timestamp.as_sec_millis().unwrap(), (6524, 365)); /// ``` @@ -199,7 +212,7 @@ impl Timestamp { /// /// # Examples /// ```rust - /// # use lib_ccxr::time::units::Timestamp;; + /// # use lib_ccxr::time::units::Timestamp; /// let timestamp = Timestamp::from_millis(6524365); /// assert_eq!(timestamp.as_hms_millis().unwrap(), (1, 48, 44, 365)); /// ``` diff --git a/src/rust/lib_ccxr/src/util/bits.rs b/src/rust/lib_ccxr/src/util/bits.rs index 6a7ca4bfa..0ec64e449 100644 --- a/src/rust/lib_ccxr/src/util/bits.rs +++ b/src/rust/lib_ccxr/src/util/bits.rs @@ -217,8 +217,8 @@ pub fn get_crc32_byte(value: u8) -> u32 { pub fn verify_crc32(buf: &[u8]) -> bool { let mut crc: i32 = -1; for &byte in buf { - let expr = (crc >> 24) as u8 ^ byte; - crc = (crc << 8) ^ get_crc32_byte(expr) as i32; + let expr = ((crc >> 24) ^ byte as i32) & 0xff; + crc = (crc << 8) ^ get_crc32_byte(expr as u8) as i32; } crc == 0 } diff --git a/src/rust/lib_ccxr/src/util/encoding.rs b/src/rust/lib_ccxr/src/util/encoding.rs index 5b8e76d53..6f0a77a35 100644 --- a/src/rust/lib_ccxr/src/util/encoding.rs +++ b/src/rust/lib_ccxr/src/util/encoding.rs @@ -39,11 +39,12 @@ //! | `utf8_to_latin1_map` | [`char_to_ucs2`], [`ucs2_to_latin1`] | /// Represents the different kinds of encoding that [`EncodedString`] can take. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] pub enum Encoding { Line21, // Same as `CCX_ENC_ASCII` in C Latin1, // Same as `CCX_ENC_LATIN_1` in C - Utf8, // Same as `CCX_ENC_UTF_8` in C + #[default] + Utf8, // Same as `CCX_ENC_UTF_8` in C Ucs2, // Same as `CCX_ENC_UNICODE` in C } diff --git a/src/rust/lib_ccxr/src/util/log.rs b/src/rust/lib_ccxr/src/util/log.rs index 397f6bb8a..582aeefcc 100644 --- a/src/rust/lib_ccxr/src/util/log.rs +++ b/src/rust/lib_ccxr/src/util/log.rs @@ -1,3 +1,4 @@ +//! Provides primitives for logging functionality //! //! The interface of this module is highly inspired by the famous log crate of rust. //! @@ -39,11 +40,12 @@ use std::sync::{OnceLock, RwLock, RwLockReadGuard, RwLockWriteGuard}; static LOGGER: OnceLock> = OnceLock::new(); /// The possible targets for logging messages. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)] pub enum OutputTarget { - Stdout, - Stderr, - Quiet, + #[default] + Quiet = 0, + Stdout = 1, + Stderr = 2, } bitflags! { @@ -52,7 +54,7 @@ bitflags! { /// Each debug message can belong to one or more of these types. The /// constants of this struct can be used as bitflags for one message to /// belong to more than one type. - #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct DebugMessageFlag: u16 { /// Show information related to parsing the container const PARSE = 0x1; @@ -135,7 +137,7 @@ pub enum GuiXdsMessage<'a> { /// /// This operates on one of the two modes: Normal Mode and Debug Mode. The mask used when in Debug Mode is a superset /// of the mask used when in Normal Mode. One can switch between the two modes by [`DebugMessageMask::set_debug_mode`]. -#[derive(Debug)] +#[derive(Debug, Default, Clone, Copy)] pub struct DebugMessageMask { debug_mode: bool, mask_on_normal: DebugMessageFlag, @@ -188,6 +190,14 @@ impl DebugMessageMask { self.mask_on_normal } } + /// Return the mask according to its mode. + pub fn normal_mask(&self) -> DebugMessageFlag { + self.mask_on_debug + } + /// Return the mask according to its mode. + pub fn debug_mask(&self) -> DebugMessageFlag { + self.mask_on_normal + } } impl ExitCause { diff --git a/src/rust/src/activity.rs b/src/rust/src/activity.rs index 46266fbad..73f9e4d8d 100644 --- a/src/rust/src/activity.rs +++ b/src/rust/src/activity.rs @@ -1,10 +1,13 @@ use std::io; use std::io::Write; -use crate::common::CcxOptions; +use lib_ccxr::common::Options; -impl CcxOptions { - pub fn activity_report_version(&mut self) { +pub trait ActivityExt { + fn activity_report_version(&mut self); +} +impl ActivityExt for Options { + fn activity_report_version(&mut self) { if self.gui_mode_reports { let mut stderr = io::stderr(); let version = env!("CARGO_PKG_VERSION"); diff --git a/src/rust/src/args.rs b/src/rust/src/args.rs index ec71b6d6c..95336dfef 100644 --- a/src/rust/src/args.rs +++ b/src/rust/src/args.rs @@ -361,14 +361,14 @@ pub struct Args { /// by its type (pick the stream that has this type in /// the PMT) #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] - pub datastreamtype: Option, + pub datastreamtype: Option, /// Assume the data is of this type, don't autodetect. This /// parameter may be needed if --datapid or --datastreamtype /// is used and CCExtractor cannot determine how to process /// the stream. The value will usually be 2 (MPEG video) or /// 6 (MPEG private data). #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] - pub streamtype: Option, + pub streamtype: Option, /// If the video was recorder using a Hauppauge card, it /// might need special processing. This parameter will /// force the special treatment. @@ -558,8 +558,8 @@ pub struct Args { #[arg(long, verbatim_doc_comment, value_name="x", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] pub xmltvoutputinterval: Option, /// Only print current events for xmltv output. - #[arg(long, verbatim_doc_comment, value_name="x", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] - pub xmltvonlycurrent: Option, + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub xmltvonlycurrent: bool, /// Create a .sem file for each output file that is open /// and delete it on file close. #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] @@ -586,7 +586,7 @@ pub struct Args { /// 1: Use CCExtractor's internal function (default). /// 2: Reduce distinct color count in image for faster results. #[arg(long, verbatim_doc_comment, value_name="mode", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] - pub quant: Option, + pub quant: Option, /// Select the OEM mode for Tesseract. /// Available modes : /// 0: OEM_TESSERACT_ONLY - the fastest mode. @@ -596,7 +596,7 @@ pub struct Args { /// Tesseract v3 : default mode is 0, /// Tesseract v4 : default mode is 1. #[arg(long, verbatim_doc_comment, value_name="mode", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] - pub oem: Option, + pub oem: Option, /// For MKV subtitles, select which language's caption /// stream will be processed. e.g. 'eng' for English. /// Language codes can be either the 3 letters bibliographic @@ -697,13 +697,13 @@ pub struct Args { /// --codec teletext /// select the teletext subtitle from elementary stream #[arg(long, verbatim_doc_comment, value_name="value", help_heading=OUTPUT_AFFECTING_CODEC)] - pub codec: Option, + pub codec: Option, /// --no-codec dvbsub /// ignore dvb subtitle and follow default behaviour /// --no-codec teletext /// ignore teletext subtitle #[arg(long, verbatim_doc_comment, conflicts_with="codec", value_name="value", help_heading=OUTPUT_AFFECTING_CODEC)] - pub no_codec: Option, + pub no_codec: Option, /// Write this text as start credits. If there are /// several lines, separate them with the /// characters \n, for example Line1\nLine 2. @@ -928,7 +928,7 @@ pub struct Args { } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] -pub enum Codec { +pub enum CCXCodec { Dvbsub, Teletext, } @@ -1004,6 +1004,7 @@ pub enum OutFormat { /// Prints to stdout information about captions in specified input. /// Don't produce any file output. Report, + SimpleXml, #[cfg(feature = "with_libcurl")] /// POST plain transcription frame-by-frame to a /// URL specified by --curlposturl. Don't produce diff --git a/src/rust/src/common.rs b/src/rust/src/common.rs index daff71a36..ee4adea4c 100644 --- a/src/rust/src/common.rs +++ b/src/rust/src/common.rs @@ -1,216 +1,385 @@ +use lib_ccxr::common::Codec; +use lib_ccxr::common::CommonTimingCtx; +use lib_ccxr::common::Decoder608Report; +use lib_ccxr::common::Decoder608Settings; +use lib_ccxr::common::DecoderDtvccReport; +use lib_ccxr::common::DecoderDtvccSettings; +use lib_ccxr::common::DemuxerConfig; +use lib_ccxr::common::DtvccServiceCharset; +use lib_ccxr::common::EncoderConfig; +use lib_ccxr::common::EncodersTranscriptFormat; +use lib_ccxr::common::Language; +use lib_ccxr::common::Options; +use lib_ccxr::common::OutputFormat; +use lib_ccxr::common::SelectCodec; +use lib_ccxr::common::StreamMode; +use lib_ccxr::common::StreamType; +use lib_ccxr::common::TeletextConfig; +use lib_ccxr::hardsubx::ColorHue; +use lib_ccxr::hardsubx::OcrMode; +use lib_ccxr::time::units::Timestamp; +use lib_ccxr::time::units::TimestampFormat; +use lib_ccxr::util::encoding::Encoding; + use crate::bindings::*; +use crate::utils::null_pointer; use crate::utils::string_to_c_char; use crate::utils::string_to_c_chars; -#[derive(Debug)] -pub struct CcxTeletextConfig { - pub verbose: bool, - pub page: u16, - pub tid: u16, - pub offset: f64, - pub bom: bool, - pub nonempty: bool, - pub user_page: u16, - pub dolevdist: i32, - pub levdistmincnt: i32, - pub levdistmaxpct: i32, - pub extraction_start: CcxBoundaryTime, - pub extraction_end: CcxBoundaryTime, - pub write_format: CcxOutputFormat, - pub gui_mode_reports: bool, - pub date_format: CcxOutputDateFormat, - pub noautotimeref: bool, - pub send_to_srv: bool, - pub encoding: CcxEncodingType, - pub nofontcolor: bool, - pub nohtmlescape: bool, - pub millis_separator: char, - pub latrusmap: bool, +pub trait FromC { + fn from_c(value: T) -> Self; } -impl Default for CcxTeletextConfig { - fn default() -> Self { - Self { - verbose: true, - page: 0, - tid: 0, - offset: 0.0, - bom: true, - nonempty: true, - user_page: 0, - dolevdist: 0, - levdistmincnt: 0, - levdistmaxpct: 0, - extraction_start: CcxBoundaryTime::default(), - extraction_end: CcxBoundaryTime::default(), - write_format: CcxOutputFormat::default(), - gui_mode_reports: false, - date_format: CcxOutputDateFormat::default(), - noautotimeref: false, - send_to_srv: false, - encoding: CcxEncodingType::default(), - nofontcolor: false, - nohtmlescape: false, - millis_separator: ',', - latrusmap: false, +pub trait CType { + /// # Safety + /// This function is unsafe because it dereferences the pointer passed to it. + unsafe fn to_ctype(&self) -> T; +} +pub trait CType2 { + /// # Safety + /// This function is unsafe because it dereferences the pointer passed to it. + unsafe fn to_ctype(&self, value: U) -> T; +} +pub trait FromRust { + /// # Safety + /// This function is unsafe because it dereferences the pointer passed to it. + unsafe fn copy_from_rust(&self, options: T); +} + +impl FromRust for *mut ccx_s_options { + /// # Safety + /// + /// This function is unsafe because it dereferences the pointer passed to it. + unsafe fn copy_from_rust(self: &*mut ccx_s_options, options: Options) { + (**self).extract = options.extract as _; + (**self).no_rollup = options.no_rollup as _; + (**self).noscte20 = options.noscte20 as _; + (**self).webvtt_create_css = options.webvtt_create_css as _; + (**self).cc_channel = options.cc_channel as _; + (**self).buffer_input = options.buffer_input as _; + (**self).nofontcolor = options.nofontcolor as _; + (**self).write_format = options.write_format.to_ctype(); + (**self).send_to_srv = options.send_to_srv as _; + (**self).nohtmlescape = options.nohtmlescape as _; + (**self).notypesetting = options.notypesetting as _; + (**self).extraction_start = options.extraction_start.to_ctype(); + (**self).extraction_end = options.extraction_end.to_ctype(); + (**self).print_file_reports = options.print_file_reports as _; + (**self).settings_608 = options.settings_608.to_ctype(); + (**self).settings_dtvcc = options.settings_dtvcc.to_ctype(); + (**self).is_608_enabled = options.is_608_enabled as _; + (**self).is_708_enabled = options.is_708_enabled as _; + (**self).millis_separator = options.date_format.millis_separator() as _; + (**self).binary_concat = options.binary_concat as _; + (**self).use_gop_as_pts = if let Some(usegops) = options.use_gop_as_pts { + usegops as _ + } else { + -1 + }; + (**self).fix_padding = options.fix_padding as _; + (**self).gui_mode_reports = options.gui_mode_reports as _; + (**self).no_progress_bar = options.no_progress_bar as _; + + if options.sentence_cap_file.try_exists().unwrap_or_default() { + (**self).sentence_cap_file = string_to_c_char( + options + .sentence_cap_file + .clone() + .to_str() + .unwrap_or_default(), + ); + } + + (**self).live_stream = if let Some(live_stream) = options.live_stream { + live_stream.seconds() as _ + } else { + -1 + }; + if options + .filter_profanity_file + .try_exists() + .unwrap_or_default() + { + (**self).filter_profanity_file = string_to_c_char( + options + .filter_profanity_file + .clone() + .to_str() + .unwrap_or_default(), + ); + } + (**self).messages_target = options.messages_target as _; + (**self).timestamp_map = options.timestamp_map as _; + (**self).dolevdist = options.dolevdist.into(); + (**self).levdistmincnt = options.levdistmincnt as _; + (**self).levdistmaxpct = options.levdistmaxpct as _; + (**self).investigate_packets = options.investigate_packets as _; + (**self).fullbin = options.fullbin as _; + (**self).nosync = options.nosync as _; + (**self).hauppauge_mode = options.hauppauge_mode as _; + (**self).wtvconvertfix = options.wtvconvertfix as _; + (**self).wtvmpeg2 = options.wtvmpeg2 as _; + (**self).auto_myth = if let Some(auto_myth) = options.auto_myth { + auto_myth as _ + } else { + -1 + }; + (**self).mp4vidtrack = options.mp4vidtrack as _; + (**self).extract_chapters = options.extract_chapters as _; + (**self).usepicorder = options.usepicorder as _; + (**self).xmltv = options.xmltv as _; + (**self).xmltvliveinterval = options.xmltvliveinterval.seconds() as _; + (**self).xmltvoutputinterval = options.xmltvoutputinterval.seconds() as _; + (**self).xmltvonlycurrent = options.xmltvonlycurrent.into(); + (**self).keep_output_closed = options.keep_output_closed as _; + (**self).force_flush = options.force_flush as _; + (**self).append_mode = options.append_mode as _; + (**self).ucla = options.ucla as _; + (**self).tickertext = options.tickertext as _; + (**self).hardsubx = options.hardsubx as _; + (**self).hardsubx_and_common = options.hardsubx_and_common as _; + if let Some(dvblang) = options.dvblang { + (**self).dvblang = string_to_c_char(dvblang.to_ctype().as_str()); + } + if options.ocrlang.try_exists().unwrap_or_default() { + (**self).ocrlang = string_to_c_char(options.ocrlang.to_str().unwrap()); + } + (**self).ocr_oem = options.ocr_oem as _; + (**self).ocr_quantmode = options.ocr_quantmode as _; + if let Some(mkvlang) = options.mkvlang { + (**self).mkvlang = string_to_c_char(mkvlang.to_ctype().as_str()); + } + (**self).analyze_video_stream = options.analyze_video_stream as _; + (**self).hardsubx_ocr_mode = options.hardsubx_ocr_mode.to_ctype(); + (**self).hardsubx_subcolor = options.hardsubx_hue.to_ctype(); + (**self).hardsubx_min_sub_duration = options.hardsubx_min_sub_duration.seconds() as _; + (**self).hardsubx_detect_italics = options.hardsubx_detect_italics as _; + (**self).hardsubx_conf_thresh = options.hardsubx_conf_thresh as _; + (**self).hardsubx_hue = options.hardsubx_hue.get_hue() as _; + (**self).hardsubx_lum_thresh = options.hardsubx_lum_thresh as _; + (**self).transcript_settings = options.transcript_settings.to_ctype(); + (**self).date_format = options.date_format.to_ctype(); + (**self).write_format_rewritten = options.write_format_rewritten as _; + (**self).use_ass_instead_of_ssa = options.use_ass_instead_of_ssa as _; + (**self).use_webvtt_styling = options.use_webvtt_styling as _; + (**self).debug_mask = options.debug_mask.normal_mask().bits() as _; + (**self).debug_mask_on_debug = options.debug_mask.debug_mask().bits() as _; + if options.udpsrc.is_some() { + (**self).udpsrc = string_to_c_char(&options.udpsrc.clone().unwrap()); + } + if options.udpaddr.is_some() { + (**self).udpaddr = string_to_c_char(&options.udpaddr.clone().unwrap()); + } + (**self).udpport = options.udpport as _; + if options.tcpport.is_some() { + (**self).tcpport = string_to_c_char(&options.tcpport.unwrap().to_string()); + } + if options.tcp_password.is_some() { + (**self).tcp_password = string_to_c_char(&options.tcp_password.clone().unwrap()); + } + if options.tcp_desc.is_some() { + (**self).tcp_desc = string_to_c_char(&options.tcp_desc.clone().unwrap()); + } + if options.srv_addr.is_some() { + (**self).srv_addr = string_to_c_char(&options.srv_addr.clone().unwrap()); + } + if options.srv_port.is_some() { + (**self).srv_port = string_to_c_char(&options.srv_port.unwrap().to_string()); + } + (**self).noautotimeref = options.noautotimeref as _; + (**self).input_source = options.input_source as _; + if options.output_filename.is_some() { + (**self).output_filename = string_to_c_char(&options.output_filename.clone().unwrap()); + } + if options.inputfile.is_some() { + (**self).inputfile = string_to_c_chars(options.inputfile.clone().unwrap()); + (**self).num_input_files = options.inputfile.as_ref().unwrap().len() as _; + } + (**self).demux_cfg = options.demux_cfg.to_ctype(); + (**self).enc_cfg = options.enc_cfg.to_ctype(); + (**self).subs_delay = options.subs_delay.millis(); + (**self).cc_to_stdout = options.cc_to_stdout as _; + (**self).pes_header_to_stdout = options.pes_header_to_stdout as _; + (**self).ignore_pts_jumps = options.ignore_pts_jumps as _; + (**self).multiprogram = options.multiprogram as _; + (**self).out_interval = options.out_interval; + (**self).segment_on_key_frames_only = options.segment_on_key_frames_only as _; + #[cfg(feature = "with_libcurl")] + { + if options.curlposturl.is_some() { + (**self).curlposturl = + string_to_c_char(&options.curlposturl.as_ref().unwrap_or_default().as_str()); + } + } + #[cfg(feature = "enable_sharing")] + { + (**self).sharing_enabled = options.sharing_enabled as _; + if options.sharing_url.is_some() { + (**self).sharing_url = + string_to_c_char(&options.sharing_url.as_ref().unwrap().as_str()); + } + (**self).translate_enabled = options.translate_enabled as _; + if options.translate_langs.is_some() { + (**self).translate_langs = string_to_c_char(&options.translate_langs.unwrap()); + } + if options.translate_key.is_some() { + (**self).translate_key = string_to_c_char(&options.translate_key.unwrap()); + } } } } -impl CcxTeletextConfig { - pub fn to_ctype(&self) -> ccx_s_teletext_config { +impl CType2 for TeletextConfig { + unsafe fn to_ctype(&self, value: &Options) -> ccx_s_teletext_config { let mut config = ccx_s_teletext_config { _bitfield_1: Default::default(), _bitfield_2: Default::default(), _bitfield_align_1: Default::default(), _bitfield_align_2: Default::default(), - page: self.page, - tid: self.tid, - offset: self.offset, + page: self.user_page, + tid: 0, + offset: 0.0, user_page: self.user_page, - dolevdist: self.dolevdist, - levdistmincnt: self.levdistmincnt, - levdistmaxpct: self.levdistmaxpct, + dolevdist: self.dolevdist.into(), + levdistmincnt: self.levdistmincnt.into(), + levdistmaxpct: self.levdistmaxpct.into(), extraction_start: self.extraction_start.to_ctype(), extraction_end: self.extraction_end.to_ctype(), - write_format: self.write_format.into(), - gui_mode_reports: self.gui_mode_reports as _, - date_format: self.date_format as _, - noautotimeref: self.noautotimeref as _, - send_to_srv: self.send_to_srv as _, - encoding: self.encoding as _, - nofontcolor: self.nofontcolor as _, - nohtmlescape: self.nohtmlescape as _, - millis_separator: self.millis_separator as _, - latrusmap: self.latrusmap as _, + write_format: self.write_format.to_ctype(), + gui_mode_reports: value.gui_mode_reports as _, + date_format: self.date_format.to_ctype(), + noautotimeref: self.noautotimeref.into(), + send_to_srv: value.send_to_srv.into(), + encoding: value.enc_cfg.encoding.to_ctype() as _, + nofontcolor: self.nofontcolor.into(), + nohtmlescape: self.nohtmlescape.into(), + millis_separator: self.date_format.millis_separator() as _, + latrusmap: self.latrusmap.into(), }; - config.set_verbose(if self.verbose { 1 } else { 0 }); - config.set_bom(if self.bom { 1 } else { 0 }); - config.set_nonempty(if self.nonempty { 1 } else { 0 }); + config.set_verbose(self.verbose.into()); + config.set_bom(1); + config.set_nonempty(1); config } } -#[derive(Debug, Default, Clone)] -pub struct CcxBoundaryTime { - pub hh: i32, - pub mm: i32, - pub ss: i32, - pub time_in_ms: i64, - pub set: bool, -} - -impl CcxBoundaryTime { - pub fn start_credits_not_before() -> CcxBoundaryTime { - CcxBoundaryTime { - hh: 0, - mm: 0, - ss: 0, - time_in_ms: 0, - set: true, - } - } - pub fn start_credits_not_after() -> CcxBoundaryTime { - CcxBoundaryTime { - hh: 0, - mm: 5, - ss: 0, - time_in_ms: 300000, - set: true, - } - } - pub fn start_credits_for_atleast() -> CcxBoundaryTime { - CcxBoundaryTime { - hh: 0, - mm: 0, - ss: 2, - time_in_ms: 2000, - set: true, +impl CType for Option { + /// Convert to C variant of `ccx_boundary_time`. + unsafe fn to_ctype(&self) -> ccx_boundary_time { + if self.is_none() { + return ccx_boundary_time { + hh: 0, + mm: 0, + ss: 0, + time_in_ms: 0, + set: 0, + }; } + self.unwrap().to_ctype() } - pub fn start_credits_for_atmost() -> CcxBoundaryTime { - CcxBoundaryTime { - hh: 0, - mm: 0, - ss: 5, - time_in_ms: 5000, - set: true, - } - } - pub fn end_credits_for_atleast() -> CcxBoundaryTime { - CcxBoundaryTime { - hh: 0, - mm: 0, - ss: 2, - time_in_ms: 2000, - set: true, - } - } - pub fn end_credits_for_atmost() -> CcxBoundaryTime { - CcxBoundaryTime { - hh: 0, - mm: 0, - ss: 5, - time_in_ms: 5000, - set: true, +} + +impl CType for Timestamp { + /// Convert to C variant of `ccx_boundary_time`. + unsafe fn to_ctype(&self) -> ccx_boundary_time { + let (hh, mm, ss, _) = self.as_hms_millis().unwrap(); + ccx_boundary_time { + hh: hh as _, + mm: mm as _, + ss: ss as _, + time_in_ms: self.millis(), + set: 1, } } +} - pub fn to_ctype(&self) -> ccx_boundary_time { - ccx_boundary_time { - hh: self.hh, - mm: self.mm, - ss: self.ss, - time_in_ms: self.time_in_ms, - set: self.set as _, +impl CType for TimestampFormat { + /// Convert to C variant of `ccx_boundary_time`. + unsafe fn to_ctype(&self) -> ccx_output_date_format { + match self { + TimestampFormat::None => ccx_output_date_format::ODF_NONE, + TimestampFormat::HHMMSS => ccx_output_date_format::ODF_HHMMSS, + TimestampFormat::HHMMSSFFF => ccx_output_date_format::ODF_HHMMSSMS, + TimestampFormat::Seconds { + millis_separator: _, + } => ccx_output_date_format::ODF_SECONDS, + TimestampFormat::Date { + millis_separator: _, + } => ccx_output_date_format::ODF_DATE, } } } -#[derive(Debug, Default, Copy, Clone)] -pub struct CcxDecoder608Report { - pub xds: bool, - pub cc_channels: [u8; 4], +impl CType for OutputFormat { + /// Convert to C variant of `ccx_output_format`. + unsafe fn to_ctype(&self) -> ccx_output_format { + match self { + OutputFormat::Raw => ccx_output_format::CCX_OF_RAW, + OutputFormat::Srt => ccx_output_format::CCX_OF_SRT, + OutputFormat::Sami => ccx_output_format::CCX_OF_SAMI, + OutputFormat::Transcript => ccx_output_format::CCX_OF_TRANSCRIPT, + OutputFormat::Rcwt => ccx_output_format::CCX_OF_RCWT, + OutputFormat::Null => ccx_output_format::CCX_OF_NULL, + OutputFormat::SmpteTt => ccx_output_format::CCX_OF_SMPTETT, + OutputFormat::SpuPng => ccx_output_format::CCX_OF_SPUPNG, + OutputFormat::DvdRaw => ccx_output_format::CCX_OF_DVDRAW, + OutputFormat::WebVtt => ccx_output_format::CCX_OF_WEBVTT, + OutputFormat::SimpleXml => ccx_output_format::CCX_OF_SIMPLE_XML, + OutputFormat::G608 => ccx_output_format::CCX_OF_G608, + OutputFormat::Curl => ccx_output_format::CCX_OF_CURL, + OutputFormat::Ssa => ccx_output_format::CCX_OF_SSA, + OutputFormat::Mcc => ccx_output_format::CCX_OF_MCC, + OutputFormat::Scc => ccx_output_format::CCX_OF_SCC, + OutputFormat::Ccd => ccx_output_format::CCX_OF_CCD, + } + } +} + +impl CType for Encoding { + /// Convert to C variant of `u32`. + unsafe fn to_ctype(&self) -> u32 { + match self { + Encoding::Line21 => ccx_encoding_type_CCX_ENC_ASCII as _, + Encoding::Latin1 => ccx_encoding_type_CCX_ENC_LATIN_1 as _, + Encoding::Utf8 => ccx_encoding_type_CCX_ENC_UTF_8 as _, + Encoding::Ucs2 => ccx_encoding_type_CCX_ENC_UNICODE as _, + } + } } -impl CcxDecoder608Report { - pub fn to_ctype(&self) -> ccx_decoder_608_report { - let mut decoder = ccx_decoder_608_report { - _bitfield_1: Default::default(), - _bitfield_align_1: Default::default(), - cc_channels: self.cc_channels, - }; - decoder.set_xds(if self.xds { 1 } else { 0 }); - decoder +impl CType for Language { + /// Convert to C variant of `String`. + unsafe fn to_ctype(&self) -> String { + self.to_str().to_lowercase() } } -#[derive(Debug, Copy, Clone)] -pub struct CcxDecoder608Settings { - pub direct_rollup: i32, - pub force_rollup: i32, - pub no_rollup: bool, - pub default_color: CcxDecoder608ColorCode, - pub screens_to_process: i32, - pub report: Option, +impl CType for OcrMode { + /// Convert to C variant of `i32`. + unsafe fn to_ctype(&self) -> i32 { + *self as i32 + } } -impl Default for CcxDecoder608Settings { - fn default() -> Self { - Self { - direct_rollup: 0, - force_rollup: 0, - no_rollup: false, - default_color: CcxDecoder608ColorCode::Transparent, - screens_to_process: -1, - report: None, +impl CType for ColorHue { + /// Convert to C variant of `i32`. + unsafe fn to_ctype(&self) -> i32 { + match self { + ColorHue::Custom(_) => 7, + ColorHue::White => 0, + ColorHue::Yellow => 1, + ColorHue::Green => 2, + ColorHue::Cyan => 3, + ColorHue::Blue => 4, + ColorHue::Magenta => 5, + ColorHue::Red => 6, } } } -impl CcxDecoder608Settings { - pub fn to_ctype(&self) -> ccx_decoder_608_settings { +impl CType for Decoder608Settings { + unsafe fn to_ctype(&self) -> ccx_decoder_608_settings { ccx_decoder_608_settings { direct_rollup: self.direct_rollup, force_rollup: self.force_rollup, @@ -226,47 +395,38 @@ impl CcxDecoder608Settings { } } -pub const CCX_DTVCC_MAX_SERVICES: usize = 63; - -impl Default for CcxFrameType { - fn default() -> Self { - Self::ResetOrUnknown +impl CType for Decoder608Report { + unsafe fn to_ctype(&self) -> ccx_decoder_608_report { + let mut decoder = ccx_decoder_608_report { + _bitfield_1: Default::default(), + _bitfield_align_1: Default::default(), + cc_channels: self.cc_channels, + }; + decoder.set_xds(if self.xds { 1 } else { 0 }); + decoder } } -#[derive(Debug, Copy, Clone)] -pub enum CcxFrameType { - ResetOrUnknown = 0, - // IFrame = 1, - // PFrame = 2, - // BFrame = 3, - // DFrame = 4, -} - -#[derive(Debug, Default)] -pub struct CcxCommonTimingCtx { - pub pts_set: i32, // 0 = No, 1 = received, 2 = min_pts set - pub min_pts_adjusted: i32, // 0 = No, 1=Yes (don't adjust again) - pub current_pts: i64, - pub current_picture_coding_type: CcxFrameType, - pub current_tref: i32, // Store temporal reference of current frame - pub min_pts: i64, - pub max_pts: i64, - pub sync_pts: i64, - pub minimum_fts: i64, // No screen should start before this FTS - pub fts_now: i64, // Time stamp of current file (w/ fts_offset, w/o fts_global) - pub fts_offset: i64, // Time before first sync_pts - pub fts_fc_offset: i64, // Time before first GOP - pub fts_max: i64, // Remember the maximum fts that we saw in current file - pub fts_global: i64, // Duration of previous files (-ve mode) - pub sync_pts2fts_set: i32, // 0 = No, 1 = Yes - pub sync_pts2fts_fts: i64, - pub sync_pts2fts_pts: i64, - pub pts_reset: i32, // 0 = No, 1 = Yes. PTS resets when current_pts is lower than prev +impl CType for DecoderDtvccSettings { + unsafe fn to_ctype(&self) -> ccx_decoder_dtvcc_settings { + ccx_decoder_dtvcc_settings { + enabled: self.enabled as _, + print_file_reports: self.print_file_reports as _, + no_rollup: self.no_rollup as _, + report: if let Some(value) = self.report { + &mut value.to_ctype() + } else { + std::ptr::null::() as *mut ccx_decoder_dtvcc_report + }, + active_services_count: self.active_services_count, + services_enabled: self.services_enabled.map(|b| if b { 1 } else { 0 }), + timing: &mut self.timing.to_ctype(), + } + } } -impl CcxCommonTimingCtx { - pub fn to_ctype(&self) -> ccx_common_timing_ctx { +impl CType for CommonTimingCtx { + unsafe fn to_ctype(&self) -> ccx_common_timing_ctx { ccx_common_timing_ctx { pts_set: self.pts_set, min_pts_adjusted: self.min_pts_adjusted, @@ -290,67 +450,8 @@ impl CcxCommonTimingCtx { } } -impl Default for CcxDecoderDtvccSettings { - fn default() -> Self { - Self { - enabled: false, - print_file_reports: true, - no_rollup: false, - report: None, - active_services_count: 0, - services_enabled: [false; CCX_DTVCC_MAX_SERVICES], - timing: CcxCommonTimingCtx::default(), - } - } -} - -#[derive(Debug)] -pub struct CcxDecoderDtvccSettings { - pub enabled: bool, - pub print_file_reports: bool, - pub no_rollup: bool, - pub report: Option, - pub active_services_count: i32, - pub services_enabled: [bool; CCX_DTVCC_MAX_SERVICES], - pub timing: CcxCommonTimingCtx, -} - -impl CcxDecoderDtvccSettings { - pub fn to_ctype(&self) -> ccx_decoder_dtvcc_settings { - ccx_decoder_dtvcc_settings { - enabled: self.enabled as _, - print_file_reports: self.print_file_reports as _, - no_rollup: self.no_rollup as _, - report: if let Some(value) = self.report { - &mut value.to_ctype() - } else { - std::ptr::null::() as *mut ccx_decoder_dtvcc_report - }, - active_services_count: self.active_services_count, - services_enabled: self.services_enabled.map(|b| if b { 1 } else { 0 }), - timing: &mut self.timing.to_ctype(), - } - } -} - -impl Default for CcxDecoderDtvccReport { - fn default() -> Self { - Self { - reset_count: 0, - services: [0; CCX_DTVCC_MAX_SERVICES], - } - } -} - -#[derive(Debug, Copy, Clone)] - -pub struct CcxDecoderDtvccReport { - pub reset_count: i32, - pub services: [u32; CCX_DTVCC_MAX_SERVICES], -} - -impl CcxDecoderDtvccReport { - pub fn to_ctype(&self) -> ccx_decoder_dtvcc_report { +impl CType for DecoderDtvccReport { + unsafe fn to_ctype(&self) -> ccx_decoder_dtvcc_report { ccx_decoder_dtvcc_report { reset_count: self.reset_count, services: self.services, @@ -358,35 +459,8 @@ impl CcxDecoderDtvccReport { } } -#[derive(Debug, Clone)] -pub struct CcxEncodersTranscriptFormat { - pub show_start_time: bool, - pub show_end_time: bool, - pub show_mode: bool, - pub show_cc: bool, - pub relative_timestamp: bool, - pub xds: bool, - pub use_colors: bool, - pub is_final: bool, -} - -impl Default for CcxEncodersTranscriptFormat { - fn default() -> Self { - Self { - show_start_time: false, - show_end_time: false, - show_mode: false, - show_cc: false, - relative_timestamp: true, - xds: false, - use_colors: true, - is_final: false, - } - } -} - -impl CcxEncodersTranscriptFormat { - fn to_ctype(&self) -> ccx_encoders_transcript_format { +impl CType for EncodersTranscriptFormat { + unsafe fn to_ctype(&self) -> ccx_encoders_transcript_format { ccx_encoders_transcript_format { showStartTime: self.show_start_time as _, showEndTime: self.show_end_time as _, @@ -400,917 +474,150 @@ impl CcxEncodersTranscriptFormat { } } -impl Default for CcxOutputDateFormat { - fn default() -> Self { - Self::None - } -} - -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum CcxOutputDateFormat { - None = 0, - // HHMMSS = 1, - Seconds = 2, - Date = 3, - HhMmSsMs = 4, // HH:MM:SS,MILIS (.srt style) -} - -impl Default for CcxStreamMode { - fn default() -> Self { - Self::Autodetect - } -} - -#[derive(PartialEq, Debug, Copy, Clone)] -pub enum CcxStreamMode { - ElementaryOrNotFound = 0, - Transport = 1, - Program = 2, - Asf = 3, - McpoodlesRaw = 4, - Rcwt = 5, // Raw Captions With Time, not used yet. - // Myth = 6, // Use the myth loop - Mp4 = 7, // MP4, ISO- - #[cfg(feature = "wtv_debug")] - HexDump = 8, // Hexadecimal dump generated by wtvccdump - Wtv = 9, - #[cfg(feature = "enable_ffmpeg")] - Ffmpeg = 10, - // Gxf = 11, - Mkv = 12, - Mxf = 13, - Autodetect = 16, -} - -impl CcxStreamMode { - pub fn to_ctype(&self) -> ccx_stream_mode_enum { - match self { - CcxStreamMode::ElementaryOrNotFound => { - ccx_stream_mode_enum_CCX_SM_ELEMENTARY_OR_NOT_FOUND - } - CcxStreamMode::Transport => ccx_stream_mode_enum_CCX_SM_TRANSPORT, - CcxStreamMode::Program => ccx_stream_mode_enum_CCX_SM_PROGRAM, - CcxStreamMode::Asf => ccx_stream_mode_enum_CCX_SM_ASF, - CcxStreamMode::McpoodlesRaw => ccx_stream_mode_enum_CCX_SM_MCPOODLESRAW, - CcxStreamMode::Rcwt => ccx_stream_mode_enum_CCX_SM_RCWT, - // CcxStreamMode::Myth => ccx_stream_mode_enum_CCX_SM_MYTH, - CcxStreamMode::Mp4 => ccx_stream_mode_enum_CCX_SM_MP4, - #[cfg(feature = "wtv_debug")] - CcxStreamMode::HexDump => ccx_stream_mode_enum_CCX_SM_HEX_DUMP, - CcxStreamMode::Wtv => ccx_stream_mode_enum_CCX_SM_WTV, - #[cfg(feature = "enable_ffmpeg")] - CcxStreamMode::Ffmpeg => ccx_stream_mode_enum_CCX_SM_FFMPEG, - // CcxStreamMode::Gxf => ccx_stream_mode_enum_CCX_SM_GXF, - CcxStreamMode::Mkv => ccx_stream_mode_enum_CCX_SM_MKV, - CcxStreamMode::Mxf => ccx_stream_mode_enum_CCX_SM_MXF, - CcxStreamMode::Autodetect => ccx_stream_mode_enum_CCX_SM_AUTODETECT, +impl CType for DemuxerConfig { + unsafe fn to_ctype(&self) -> demuxer_cfg { + demuxer_cfg { + m2ts: self.m2ts as _, + auto_stream: self.auto_stream.to_ctype() as _, + codec: self.codec.to_ctype() as _, + nocodec: self.nocodec.to_ctype() as _, + ts_autoprogram: self.ts_autoprogram as _, + ts_allprogram: self.ts_allprogram as _, + ts_cappids: self.ts_cappids.to_ctype(), + nb_ts_cappid: self.ts_cappids.len() as _, + ts_forced_cappid: self.ts_forced_cappid as _, + ts_forced_program: self.ts_forced_program.unwrap_or_default() as _, + ts_forced_program_selected: self.ts_forced_program.is_some() as _, + ts_datastreamtype: self.ts_datastreamtype.to_ctype() as _, + ts_forced_streamtype: self.ts_forced_streamtype.to_ctype() as _, } } } -impl Default for CcxCodeType { - fn default() -> Self { - Self::None - } -} - -#[derive(Debug, PartialEq, Copy, Clone)] -pub enum CcxCodeType { - Any = 0, - Teletext = 1, - Dvb = 2, - // IsdbCc = 3, - // AtscCc = 4, - None = 5, -} - -impl CcxCodeType { - pub fn to_ctype(&self) -> ccx_code_type { +impl CType for SelectCodec { + unsafe fn to_ctype(&self) -> u32 { match self { - CcxCodeType::Any => ccx_code_type_CCX_CODEC_ANY, - CcxCodeType::Teletext => ccx_code_type_CCX_CODEC_TELETEXT, - CcxCodeType::Dvb => ccx_code_type_CCX_CODEC_DVB, - // CcxCodeType::IsdbCc => ccx_code_type_CCX_CODEC_ISDB_CC, - // CcxCodeType::AtscCc => ccx_code_type_CCX_CODEC_ATSC_CC, - CcxCodeType::None => ccx_code_type_CCX_CODEC_NONE, - } - } -} - -impl Default for CcxDemuxerCfg { - fn default() -> Self { - Self { - m2ts: false, - auto_stream: CcxStreamMode::default(), - codec: CcxCodeType::Any, - nocodec: CcxCodeType::None, - ts_autoprogram: false, - ts_allprogram: false, - ts_cappids: [0; 128], - nb_ts_cappid: 0, - ts_forced_cappid: 0, - ts_forced_program: -1, - ts_forced_program_selected: false, - ts_datastreamtype: 0, - ts_forced_streamtype: 0, - } - } -} - -#[derive(Debug)] -pub struct CcxDemuxerCfg { - pub m2ts: bool, - pub auto_stream: CcxStreamMode, - pub codec: CcxCodeType, - pub nocodec: CcxCodeType, - pub ts_autoprogram: bool, - pub ts_allprogram: bool, - pub ts_cappids: [u32; 128], - pub nb_ts_cappid: i32, - pub ts_forced_cappid: u32, - pub ts_forced_program: i32, - pub ts_forced_program_selected: bool, - pub ts_datastreamtype: i32, - pub ts_forced_streamtype: u32, -} - -impl CcxDemuxerCfg { - pub fn to_ctype(&self) -> demuxer_cfg { - demuxer_cfg { - m2ts: if self.m2ts { 1 } else { 0 }, - auto_stream: self.auto_stream.to_ctype(), - codec: self.codec.to_ctype(), - nocodec: self.nocodec.to_ctype(), - ts_autoprogram: if self.ts_autoprogram { 1 } else { 0 }, - ts_allprogram: if self.ts_allprogram as _ { 1 } else { 0 }, - ts_cappids: self.ts_cappids, - nb_ts_cappid: self.nb_ts_cappid, - ts_forced_cappid: self.ts_forced_cappid, - ts_forced_program: self.ts_forced_program, - ts_forced_program_selected: if self.ts_forced_program_selected { - 1 - } else { - 0 + SelectCodec::Some(codec) => match codec { + Codec::Teletext => ccx_code_type_CCX_CODEC_TELETEXT as _, + Codec::Dvb => ccx_code_type_CCX_CODEC_DVB as _, + Codec::IsdbCc => ccx_code_type_CCX_CODEC_ISDB_CC as _, + Codec::AtscCc => ccx_code_type_CCX_CODEC_ATSC_CC as _, + Codec::Any => ccx_code_type_CCX_CODEC_ANY as _, }, - ts_datastreamtype: self.ts_datastreamtype, - ts_forced_streamtype: self.ts_forced_streamtype, + SelectCodec::None => ccx_code_type_CCX_CODEC_NONE as _, + SelectCodec::All => ccx_code_type_CCX_CODEC_ANY as _, } } } -impl Default for CcxOutputFormat { - fn default() -> Self { - Self::Raw +impl CType for StreamType { + unsafe fn to_ctype(&self) -> i32 { + *self as i32 } } -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum CcxOutputFormat { - Raw = 0, - Srt = 1, - Sami = 2, - Transcript = 3, - Rcwt = 4, - Null = 5, - Smptett = 6, - Spupng = 7, - Dvdraw = 8, // See -d at http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_TOOLS.HTML#CCExtract - Webvtt = 9, - // SimpleXml = 10, - G608 = 11, - Curl = 12, - Ssa = 13, - Mcc = 14, - Scc = 15, - Ccd = 16, -} - -impl From for ccx_output_format { - fn from(value: CcxOutputFormat) -> ccx_output_format { - match value { - CcxOutputFormat::Raw => ccx_output_format::CCX_OF_RAW, - CcxOutputFormat::Srt => ccx_output_format::CCX_OF_SRT, - CcxOutputFormat::Sami => ccx_output_format::CCX_OF_SAMI, - CcxOutputFormat::Transcript => ccx_output_format::CCX_OF_TRANSCRIPT, - CcxOutputFormat::Rcwt => ccx_output_format::CCX_OF_RCWT, - CcxOutputFormat::Null => ccx_output_format::CCX_OF_NULL, - CcxOutputFormat::Smptett => ccx_output_format::CCX_OF_SMPTETT, - CcxOutputFormat::Spupng => ccx_output_format::CCX_OF_SPUPNG, - CcxOutputFormat::Dvdraw => ccx_output_format::CCX_OF_DVDRAW, - CcxOutputFormat::Webvtt => ccx_output_format::CCX_OF_WEBVTT, - // CcxOutputFormat::SimpleXml => ccx_output_format::CCX_OF_SIMPLE_XML, - CcxOutputFormat::G608 => ccx_output_format::CCX_OF_G608, - CcxOutputFormat::Curl => ccx_output_format::CCX_OF_CURL, - CcxOutputFormat::Ssa => ccx_output_format::CCX_OF_SSA, - CcxOutputFormat::Mcc => ccx_output_format::CCX_OF_MCC, - CcxOutputFormat::Scc => ccx_output_format::CCX_OF_SCC, - CcxOutputFormat::Ccd => ccx_output_format::CCX_OF_CCD, - } - } -} - -impl Default for CcxEncodingType { - fn default() -> Self { - Self::Utf8 - } -} - -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum CcxEncodingType { - Unicode = 0, - Latin1 = 1, - Utf8 = 2, - // Ascii = 3, -} - -impl Default for CcxEncoderCfg { - fn default() -> Self { - Self { - extract: 1, - dtvcc_extract: false, - gui_mode_reports: false, - output_filename: String::default(), - write_format: CcxOutputFormat::default(), - keep_output_closed: false, - force_flush: false, - append_mode: false, - ucla: false, - encoding: CcxEncodingType::default(), - date_format: CcxOutputDateFormat::default(), - millis_separator: ',', - autodash: false, - trim_subs: false, - sentence_cap: false, - splitbysentence: false, - curlposturl: None, - filter_profanity: false, - with_semaphore: false, - start_credits_text: String::default(), - end_credits_text: String::default(), - startcreditsnotbefore: CcxBoundaryTime::start_credits_not_before(), - startcreditsnotafter: CcxBoundaryTime::start_credits_not_after(), - startcreditsforatleast: CcxBoundaryTime::start_credits_for_atleast(), - startcreditsforatmost: CcxBoundaryTime::start_credits_for_atmost(), - endcreditsforatleast: CcxBoundaryTime::end_credits_for_atleast(), - endcreditsforatmost: CcxBoundaryTime::end_credits_for_atmost(), - transcript_settings: CcxEncodersTranscriptFormat::default(), - send_to_srv: false, - no_bom: true, - first_input_file: String::default(), - multiple_files: false, - no_font_color: false, - no_type_setting: false, - cc_to_stdout: false, - line_terminator_lf: false, - subs_delay: 0, - program_number: 0, - in_format: 1, - nospupngocr: false, - force_dropframe: false, - render_font: String::default(), - render_font_italics: String::default(), - services_enabled: [false; CCX_DTVCC_MAX_SERVICES], - services_charsets: vec![], - all_services_charset: String::default(), - extract_only_708: false, - } - } -} - -#[derive(Debug)] -pub struct CcxEncoderCfg { - pub extract: i32, - pub dtvcc_extract: bool, - pub gui_mode_reports: bool, - pub output_filename: String, - pub write_format: CcxOutputFormat, - pub keep_output_closed: bool, - pub force_flush: bool, - pub append_mode: bool, - pub ucla: bool, - pub encoding: CcxEncodingType, - pub date_format: CcxOutputDateFormat, - pub millis_separator: char, - pub autodash: bool, - pub trim_subs: bool, - pub sentence_cap: bool, - pub splitbysentence: bool, - pub curlposturl: Option, - pub filter_profanity: bool, - pub with_semaphore: bool, - pub start_credits_text: String, - pub end_credits_text: String, - pub startcreditsnotbefore: CcxBoundaryTime, - pub startcreditsnotafter: CcxBoundaryTime, - pub startcreditsforatleast: CcxBoundaryTime, - pub startcreditsforatmost: CcxBoundaryTime, - pub endcreditsforatleast: CcxBoundaryTime, - pub endcreditsforatmost: CcxBoundaryTime, - pub transcript_settings: CcxEncodersTranscriptFormat, - pub send_to_srv: bool, - pub no_bom: bool, - pub first_input_file: String, - pub multiple_files: bool, - pub no_font_color: bool, - pub no_type_setting: bool, - pub cc_to_stdout: bool, - pub line_terminator_lf: bool, - pub subs_delay: i64, - pub program_number: i32, - pub in_format: u8, - pub nospupngocr: bool, - pub force_dropframe: bool, - pub render_font: String, - pub render_font_italics: String, - pub services_enabled: [bool; CCX_DTVCC_MAX_SERVICES], - pub services_charsets: Vec, - pub all_services_charset: String, - pub extract_only_708: bool, -} - -impl CcxEncoderCfg { - pub fn to_ctype(&self) -> encoder_cfg { - unsafe { - encoder_cfg { - extract: self.extract, - dtvcc_extract: self.dtvcc_extract as _, - gui_mode_reports: self.gui_mode_reports as _, - output_filename: string_to_c_char(&self.output_filename), - write_format: self.write_format.into(), - keep_output_closed: self.keep_output_closed as _, - force_flush: self.force_flush as _, - append_mode: self.append_mode as _, - ucla: self.ucla as _, - encoding: self.encoding as _, - date_format: self.date_format as _, - millis_separator: self.millis_separator as _, - autodash: self.autodash as _, - trim_subs: self.trim_subs as _, - sentence_cap: self.sentence_cap as _, - splitbysentence: self.splitbysentence as _, - #[cfg(feature = "with_libcurl")] - curlposturl: string_to_c_char(&self.curlposturl.clone().unwrap()), - filter_profanity: self.filter_profanity as _, - with_semaphore: self.with_semaphore as _, - start_credits_text: string_to_c_char(&self.start_credits_text), - end_credits_text: string_to_c_char(&self.end_credits_text), - startcreditsnotbefore: self.startcreditsnotbefore.to_ctype(), - startcreditsnotafter: self.startcreditsnotafter.to_ctype(), - startcreditsforatleast: self.startcreditsforatleast.to_ctype(), - startcreditsforatmost: self.startcreditsforatmost.to_ctype(), - endcreditsforatleast: self.endcreditsforatleast.to_ctype(), - endcreditsforatmost: self.endcreditsforatmost.to_ctype(), - transcript_settings: self.transcript_settings.to_ctype(), - send_to_srv: self.send_to_srv as _, - no_bom: self.no_bom as _, - first_input_file: string_to_c_char(&self.first_input_file), - multiple_files: self.multiple_files as _, - no_font_color: self.no_font_color as _, - no_type_setting: self.no_type_setting as _, - cc_to_stdout: self.cc_to_stdout as _, - line_terminator_lf: self.line_terminator_lf as _, - subs_delay: self.subs_delay, - program_number: self.program_number, - in_format: self.in_format, - nospupngocr: self.nospupngocr as _, - force_dropframe: self.force_dropframe as _, - render_font: string_to_c_char(&self.render_font), - render_font_italics: string_to_c_char(&self.render_font_italics), - services_enabled: self.services_enabled.map(|b| if b { 1 } else { 0 }), - services_charsets: string_to_c_chars(self.services_charsets.clone()), - all_services_charset: string_to_c_char(&self.all_services_charset), - extract_only_708: self.extract_only_708 as _, +impl CType for StreamMode { + unsafe fn to_ctype(&self) -> u32 { + match self { + StreamMode::ElementaryOrNotFound => { + ccx_stream_mode_enum_CCX_SM_ELEMENTARY_OR_NOT_FOUND as _ } + StreamMode::Transport => ccx_stream_mode_enum_CCX_SM_TRANSPORT as _, + StreamMode::Program => ccx_stream_mode_enum_CCX_SM_PROGRAM as _, + StreamMode::Asf => ccx_stream_mode_enum_CCX_SM_ASF as _, + StreamMode::McpoodlesRaw => ccx_stream_mode_enum_CCX_SM_MCPOODLESRAW as _, + StreamMode::Rcwt => ccx_stream_mode_enum_CCX_SM_RCWT as _, + StreamMode::Myth => ccx_stream_mode_enum_CCX_SM_MYTH as _, + StreamMode::Mp4 => ccx_stream_mode_enum_CCX_SM_MP4 as _, + #[cfg(feature = "wtv_debug")] + StreamMode::HexDump => ccx_stream_mode_enum_CCX_SM_HEX_DUMP as _, + StreamMode::Wtv => ccx_stream_mode_enum_CCX_SM_WTV as _, + #[cfg(feature = "enable_ffmpeg")] + StreamMode::Ffmpeg => ccx_stream_mode_enum_CCX_SM_FFMPEG as _, + StreamMode::Gxf => ccx_stream_mode_enum_CCX_SM_GXF as _, + StreamMode::Mkv => ccx_stream_mode_enum_CCX_SM_MKV as _, + StreamMode::Mxf => ccx_stream_mode_enum_CCX_SM_MXF as _, + StreamMode::Autodetect => ccx_stream_mode_enum_CCX_SM_AUTODETECT as _, + _ => ccx_stream_mode_enum_CCX_SM_ELEMENTARY_OR_NOT_FOUND as _, } } } -#[derive(Debug)] -pub struct CcxOptions { - pub extract: i32, - pub no_rollup: bool, - pub noscte20: bool, - pub webvtt_create_css: bool, - pub cc_channel: i32, - pub buffer_input: bool, - pub nofontcolor: bool, - pub write_format: CcxOutputFormat, - pub send_to_srv: bool, - pub nohtmlescape: bool, - pub notypesetting: bool, - pub extraction_start: CcxBoundaryTime, - pub extraction_end: CcxBoundaryTime, - pub print_file_reports: bool, - pub settings_608: CcxDecoder608Settings, - pub settings_dtvcc: CcxDecoderDtvccSettings, - pub is_608_enabled: bool, - pub is_708_enabled: bool, - pub millis_separator: char, - pub binary_concat: bool, - pub use_gop_as_pts: i32, - pub fix_padding: bool, - pub gui_mode_reports: bool, - pub no_progress_bar: bool, - pub sentence_cap_file: Option, - pub live_stream: i32, - pub filter_profanity_file: Option, - pub messages_target: i32, - pub timestamp_map: bool, - pub dolevdist: i32, - pub levdistmincnt: i32, - pub levdistmaxpct: i32, - pub investigate_packets: bool, - pub fullbin: bool, - pub nosync: bool, - pub hauppauge_mode: bool, - pub wtvconvertfix: bool, - pub wtvmpeg2: bool, - pub auto_myth: i32, - pub mp4vidtrack: bool, - pub extract_chapters: bool, - pub usepicorder: bool, - pub xmltv: i32, - pub xmltvliveinterval: i32, - pub xmltvoutputinterval: i32, - pub xmltvonlycurrent: i32, - pub keep_output_closed: bool, - pub force_flush: bool, - pub append_mode: bool, - pub ucla: bool, - pub tickertext: bool, - pub hardsubx: bool, - pub hardsubx_and_common: bool, - pub dvblang: Option, - pub ocrlang: Option, - pub ocr_oem: i32, - pub ocr_quantmode: i32, - pub mkvlang: Option, - pub analyze_video_stream: bool, - pub hardsubx_ocr_mode: i32, - pub hardsubx_subcolor: i32, - pub hardsubx_min_sub_duration: f32, - pub hardsubx_detect_italics: bool, - pub hardsubx_conf_thresh: f32, - pub hardsubx_hue: f32, - pub hardsubx_lum_thresh: f32, - pub transcript_settings: CcxEncodersTranscriptFormat, - pub date: CcxOutputDateFormat, - pub write_format_rewritten: bool, - pub use_ass_instead_of_ssa: bool, - pub use_webvtt_styling: bool, - pub debug_mask: CcxDebugMessageTypes, - pub debug_mask_on_debug: i64, - pub udpsrc: Option, - pub udpaddr: Option, - pub udpport: u32, - pub tcpport: Option, - pub tcp_password: Option, - pub tcp_desc: Option, - pub srv_addr: Option, - pub srv_port: Option, - pub noautotimeref: bool, - pub input_source: CcxDatasource, - pub output_filename: Option, - pub inputfile: Option>, - pub num_input_files: i32, - pub demux_cfg: CcxDemuxerCfg, - pub enc_cfg: CcxEncoderCfg, - pub subs_delay: i64, - pub cc_to_stdout: bool, - pub pes_header_to_stdout: bool, - pub ignore_pts_jumps: bool, - pub multiprogram: bool, - pub out_interval: i32, - pub segment_on_key_frames_only: bool, - pub curlposturl: Option, - pub sharing_enabled: bool, - pub sharing_url: Option, - pub translate_enabled: bool, - pub translate_langs: Option, - pub translate_key: Option, -} - -impl Default for CcxOptions { - fn default() -> Self { - Self { - extract: 1, - no_rollup: false, - noscte20: false, - webvtt_create_css: false, - cc_channel: 1, - buffer_input: false, - nofontcolor: false, - write_format: CcxOutputFormat::Srt, - send_to_srv: false, - nohtmlescape: false, - notypesetting: false, - extraction_start: CcxBoundaryTime::default(), - extraction_end: CcxBoundaryTime::default(), - print_file_reports: false, - settings_608: CcxDecoder608Settings::default(), - settings_dtvcc: CcxDecoderDtvccSettings::default(), - is_608_enabled: false, - is_708_enabled: false, - millis_separator: ',', - binary_concat: true, - use_gop_as_pts: 0, - fix_padding: false, - gui_mode_reports: false, - no_progress_bar: false, - sentence_cap_file: None, - live_stream: 0, - filter_profanity_file: None, - messages_target: 1, - timestamp_map: false, - dolevdist: 1, - levdistmincnt: 2, - levdistmaxpct: 10, - investigate_packets: false, - fullbin: false, - nosync: false, - hauppauge_mode: false, - wtvconvertfix: false, - wtvmpeg2: false, - auto_myth: 2, - mp4vidtrack: false, - extract_chapters: false, - usepicorder: false, - xmltv: 0, - xmltvliveinterval: 10, - xmltvoutputinterval: 0, - xmltvonlycurrent: 0, - keep_output_closed: false, - force_flush: false, - append_mode: false, - ucla: false, - tickertext: false, - hardsubx: false, - hardsubx_and_common: false, - dvblang: None, - ocrlang: None, - ocr_oem: -1, - ocr_quantmode: 1, - mkvlang: None, - analyze_video_stream: false, - hardsubx_ocr_mode: 0, - hardsubx_subcolor: 0, - hardsubx_min_sub_duration: 0.5, - hardsubx_detect_italics: false, - hardsubx_conf_thresh: 0.0, - hardsubx_hue: 0.0, - hardsubx_lum_thresh: 95.0, - transcript_settings: CcxEncodersTranscriptFormat::default(), - date: CcxOutputDateFormat::default(), - write_format_rewritten: false, - use_ass_instead_of_ssa: false, - use_webvtt_styling: false, - debug_mask: CcxDebugMessageTypes::GenericNotices, - debug_mask_on_debug: 8, - udpsrc: None, - udpaddr: None, - udpport: 0, - tcpport: None, - tcp_password: None, - tcp_desc: None, - srv_addr: None, - srv_port: None, - noautotimeref: false, - input_source: CcxDatasource::File, - output_filename: None, - inputfile: None, - num_input_files: 0, - demux_cfg: CcxDemuxerCfg::default(), - enc_cfg: CcxEncoderCfg::default(), - subs_delay: 0, - cc_to_stdout: false, - pes_header_to_stdout: false, - ignore_pts_jumps: true, - multiprogram: false, - out_interval: -1, - segment_on_key_frames_only: false, - curlposturl: None, - sharing_enabled: false, - sharing_url: None, - translate_enabled: false, - translate_langs: None, - translate_key: None, +impl CType<[u32; 128]> for Vec { + unsafe fn to_ctype(&self) -> [u32; 128] { + let mut array = [0; 128]; + for (i, value) in self.iter().enumerate() { + array[i] = *value; } + array } } -impl CcxOptions { - /// # Safety - /// - /// This function is unsafe because it dereferences the pointer passed to it. - pub unsafe fn to_ctype(&self, options: *mut ccx_s_options) { - (*options).extract = self.extract; - (*options).no_rollup = self.no_rollup as _; - (*options).noscte20 = self.noscte20 as _; - (*options).webvtt_create_css = self.webvtt_create_css as _; - (*options).cc_channel = self.cc_channel; - (*options).buffer_input = self.buffer_input as _; - (*options).nofontcolor = self.nofontcolor as _; - (*options).write_format = self.write_format.into(); - (*options).send_to_srv = self.send_to_srv as _; - (*options).nohtmlescape = self.nohtmlescape as _; - (*options).notypesetting = self.notypesetting as _; - (*options).extraction_start = self.extraction_start.to_ctype(); - (*options).extraction_end = self.extraction_end.to_ctype(); - (*options).print_file_reports = self.print_file_reports as _; - (*options).settings_608 = self.settings_608.to_ctype(); - (*options).settings_dtvcc = self.settings_dtvcc.to_ctype(); - (*options).is_608_enabled = self.is_608_enabled as _; - (*options).is_708_enabled = self.is_708_enabled as _; - (*options).millis_separator = self.millis_separator as _; - (*options).binary_concat = self.binary_concat as _; - (*options).use_gop_as_pts = self.use_gop_as_pts; - (*options).fix_padding = self.fix_padding as _; - (*options).gui_mode_reports = self.gui_mode_reports as _; - (*options).no_progress_bar = self.no_progress_bar as _; - - if self.sentence_cap_file.is_some() { - (*options).sentence_cap_file = - string_to_c_char(&self.sentence_cap_file.clone().unwrap()); - } - - (*options).live_stream = self.live_stream; - if self.filter_profanity_file.is_some() { - (*options).filter_profanity_file = - string_to_c_char(&self.filter_profanity_file.clone().unwrap()); - } - (*options).messages_target = self.messages_target; - (*options).timestamp_map = self.timestamp_map as _; - (*options).dolevdist = self.dolevdist; - (*options).levdistmincnt = self.levdistmincnt; - (*options).levdistmaxpct = self.levdistmaxpct; - (*options).investigate_packets = self.investigate_packets as _; - (*options).fullbin = self.fullbin as _; - (*options).nosync = self.nosync as _; - (*options).hauppauge_mode = self.hauppauge_mode as _; - (*options).wtvconvertfix = self.wtvconvertfix as _; - (*options).wtvmpeg2 = self.wtvmpeg2 as _; - (*options).auto_myth = self.auto_myth; - (*options).mp4vidtrack = self.mp4vidtrack as _; - (*options).extract_chapters = self.extract_chapters as _; - (*options).usepicorder = self.usepicorder as _; - (*options).xmltv = self.xmltv; - (*options).xmltvliveinterval = self.xmltvliveinterval; - (*options).xmltvoutputinterval = self.xmltvoutputinterval; - (*options).xmltvonlycurrent = self.xmltvonlycurrent; - (*options).keep_output_closed = self.keep_output_closed as _; - (*options).force_flush = self.force_flush as _; - (*options).append_mode = self.append_mode as _; - (*options).ucla = self.ucla as _; - (*options).tickertext = self.tickertext as _; - (*options).hardsubx = self.hardsubx as _; - (*options).hardsubx_and_common = self.hardsubx_and_common as _; - if self.dvblang.is_some() { - (*options).dvblang = string_to_c_char(&self.dvblang.clone().unwrap()); - } - if self.ocrlang.is_some() { - (*options).ocrlang = string_to_c_char(&self.ocrlang.clone().unwrap()); - } - (*options).ocr_oem = self.ocr_oem; - (*options).ocr_quantmode = self.ocr_quantmode; - if self.mkvlang.is_some() { - (*options).mkvlang = string_to_c_char(&self.mkvlang.clone().unwrap()); - } - (*options).analyze_video_stream = self.analyze_video_stream as _; - (*options).hardsubx_ocr_mode = self.hardsubx_ocr_mode; - (*options).hardsubx_subcolor = self.hardsubx_subcolor; - (*options).hardsubx_min_sub_duration = self.hardsubx_min_sub_duration; - (*options).hardsubx_detect_italics = self.hardsubx_detect_italics as _; - (*options).hardsubx_conf_thresh = self.hardsubx_conf_thresh; - (*options).hardsubx_hue = self.hardsubx_hue; - (*options).hardsubx_lum_thresh = self.hardsubx_lum_thresh; - (*options).transcript_settings = self.transcript_settings.to_ctype(); - (*options).date_format = self.date as _; - (*options).write_format_rewritten = self.write_format_rewritten as _; - (*options).use_ass_instead_of_ssa = self.use_ass_instead_of_ssa as _; - (*options).use_webvtt_styling = self.use_webvtt_styling as _; - (*options).debug_mask = self.debug_mask as _; - (*options).debug_mask_on_debug = self.debug_mask_on_debug; - if self.udpsrc.is_some() { - (*options).udpsrc = string_to_c_char(&self.udpsrc.clone().unwrap()); - } - if self.udpaddr.is_some() { - (*options).udpaddr = string_to_c_char(&self.udpaddr.clone().unwrap()); - } - (*options).udpport = self.udpport; - if self.tcpport.is_some() { - (*options).tcpport = string_to_c_char(&self.tcpport.unwrap().to_string()); - } - if self.tcp_password.is_some() { - (*options).tcp_password = string_to_c_char(&self.tcp_password.clone().unwrap()); - } - if self.tcp_desc.is_some() { - (*options).tcp_desc = string_to_c_char(&self.tcp_desc.clone().unwrap()); - } - if self.srv_addr.is_some() { - (*options).srv_addr = string_to_c_char(&self.srv_addr.clone().unwrap()); - } - if self.srv_port.is_some() { - (*options).srv_port = string_to_c_char(&self.srv_port.unwrap().to_string()); - } - (*options).noautotimeref = self.noautotimeref as _; - (*options).input_source = self.input_source as _; - if self.output_filename.is_some() { - (*options).output_filename = string_to_c_char(&self.output_filename.clone().unwrap()); - } - if self.inputfile.is_some() { - (*options).inputfile = string_to_c_chars(self.inputfile.clone().unwrap()); - } - (*options).num_input_files = self.num_input_files; - (*options).demux_cfg = self.demux_cfg.to_ctype(); - (*options).enc_cfg = self.enc_cfg.to_ctype(); - (*options).subs_delay = self.subs_delay; - (*options).cc_to_stdout = self.cc_to_stdout as _; - (*options).pes_header_to_stdout = self.pes_header_to_stdout as _; - (*options).ignore_pts_jumps = self.ignore_pts_jumps as _; - (*options).multiprogram = self.multiprogram as _; - (*options).out_interval = self.out_interval; - (*options).segment_on_key_frames_only = self.segment_on_key_frames_only as _; - #[cfg(feature = "with_libcurl")] - { - if self.curlposturl.is_some() { - (*options).curlposturl = string_to_c_char(&self.curlposturl.unwrap()); - } - } - #[cfg(feature = "enable_sharing")] - { - (*options).sharing_enabled = self.sharing_enabled as _; - if self.sharing_url.is_some() { - (*options).sharing_url = string_to_c_char(&self.sharing_url.unwrap()); - } - (*options).translate_enabled = self.translate_enabled as _; - if self.translate_langs.is_some() { - (*options).translate_langs = string_to_c_char(&self.translate_langs.unwrap()); - } - if self.translate_key.is_some() { - (*options).translate_key = string_to_c_char(&self.translate_key.unwrap()); - } +impl CType for EncoderConfig { + unsafe fn to_ctype(&self) -> encoder_cfg { + encoder_cfg { + extract: self.extract as _, + dtvcc_extract: self.dtvcc_extract as _, + gui_mode_reports: self.gui_mode_reports as _, + output_filename: unsafe { string_to_c_char(&self.output_filename) }, + write_format: self.write_format.to_ctype(), + keep_output_closed: self.keep_output_closed as _, + force_flush: self.force_flush as _, + append_mode: self.append_mode as _, + ucla: self.ucla as _, + encoding: self.encoding as _, + date_format: self.date_format.to_ctype(), + millis_separator: self.date_format.millis_separator() as _, + autodash: self.autodash as _, + trim_subs: self.trim_subs as _, + sentence_cap: self.sentence_cap as _, + splitbysentence: self.splitbysentence as _, + #[cfg(feature = "with_libcurl")] + curlposturl: string_to_c_char(&self.curlposturl.clone().unwrap()), + filter_profanity: self.filter_profanity as _, + with_semaphore: self.with_semaphore as _, + start_credits_text: string_to_c_char(&self.start_credits_text), + end_credits_text: string_to_c_char(&self.end_credits_text), + startcreditsnotbefore: self.startcreditsnotbefore.to_ctype(), + startcreditsnotafter: self.startcreditsnotafter.to_ctype(), + startcreditsforatleast: self.startcreditsforatleast.to_ctype(), + startcreditsforatmost: self.startcreditsforatmost.to_ctype(), + endcreditsforatleast: self.endcreditsforatleast.to_ctype(), + endcreditsforatmost: self.endcreditsforatmost.to_ctype(), + transcript_settings: self.transcript_settings.to_ctype(), + send_to_srv: self.send_to_srv as _, + no_bom: self.no_bom as _, + first_input_file: string_to_c_char(&self.first_input_file), + multiple_files: self.multiple_files as _, + no_font_color: self.no_font_color as _, + no_type_setting: self.no_type_setting as _, + cc_to_stdout: self.cc_to_stdout as _, + line_terminator_lf: self.line_terminator_lf as _, + subs_delay: self.subs_delay.millis(), + program_number: self.program_number as _, + in_format: self.in_format, + nospupngocr: self.nospupngocr as _, + force_dropframe: self.force_dropframe as _, + render_font: string_to_c_char(self.render_font.to_str().unwrap_or_default()), + render_font_italics: string_to_c_char( + self.render_font_italics.to_str().unwrap_or_default(), + ), + services_enabled: self.services_enabled.map(|b| if b { 1 } else { 0 }), + services_charsets: if let DtvccServiceCharset::Unique(vbox) = + self.services_charsets.clone() + { + string_to_c_chars(vbox.to_vec()) + } else { + null_pointer() + }, + all_services_charset: if let DtvccServiceCharset::Same(string) = + self.services_charsets.clone() + { + string_to_c_char(string.as_str()) + } else { + null_pointer() + }, + extract_only_708: self.extract_only_708 as _, } } } - -impl Default for HardsubxOcrMode { - fn default() -> Self { - Self::Frame - } -} -#[allow(dead_code)] -pub enum HardsubxOcrMode { - Frame = 0, - Word = 1, - Letter = 2, -} - -impl Default for CcxDatasource { - fn default() -> Self { - Self::None - } -} - -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum CcxDatasource { - None = -1, - File = 0, - Stdin = 1, - Network = 2, - Tcp = 3, -} - -impl Default for CcxEia608Format { - fn default() -> Self { - Self::CcScreen - } -} -pub enum CcxEia608Format { - CcScreen, - // CcLine, - // Xds, -} - -impl Default for CcModes { - fn default() -> Self { - Self::Text - } -} - -#[derive(Debug)] -pub enum CcModes { - // Popon = 0, - // Rollup2 = 1, - // Rollup3 = 2, - // Rollup4 = 3, - Text = 4, - // Painton = 5, - // FakeRollup1 = 100, -} - -impl Default for FontBits { - fn default() -> Self { - Self::Regular - } -} - -#[derive(Debug)] -pub enum FontBits { - Regular = 0, - // Italics = 1, - // Underlined = 2, - // UnderlinedItalics = 3, -} - -impl Default for CcxDecoder608ColorCode { - fn default() -> Self { - Self::Userdefined - } -} - -#[derive(Debug, Copy, Clone)] -pub enum CcxDecoder608ColorCode { - White = 0, - Green = 1, - Blue = 2, - Cyan = 3, - Red = 4, - Yellow = 5, - Magenta = 6, - Userdefined = 7, - Black = 8, - Transparent = 9, - Max, -} - -#[derive(Copy, Clone, Debug)] -pub enum ExitCode { - NoInputFiles = 2, - TooManyInputFiles = 3, - IncompatibleParameters = 4, - UnableToDetermineFileSize = 6, - MalformedParameter = 7, - ReadError = 8, - NoCaptions = 10, - WithHelp = 11, - NotClassified = 300, - ErrorInCapitalizationFile = 501, - BufferFull = 502, - MissingASFHeader = 1001, - MissingRCWTHeader = 1002, - FileCreationFailed = 5, - Unsupported = 9, - NotEnoughMemory = 500, - BugBug = 1000, -} - -// #[derive(Copy, Clone, Debug)] -// pub enum CCXResult { -// Ok = 0, -// EAGAIN = -100, -// EOF = -101, -// EINVAL = -102, -// ENOSUPP = -103, -// ENOMEM = -104, -// } - -#[derive(Copy, Clone, Debug)] -#[allow(dead_code)] -pub enum HardsubxColorType { - White = 0, - Yellow = 1, - Green = 2, - Cyan = 3, - Blue = 4, - Magenta = 5, - Red = 6, - Custom = 7, -} - -impl Default for CcxDebugMessageTypes { - fn default() -> Self { - Self::None - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum CcxDebugMessageTypes { - None = 0, - Parse = 1, - Vides = 2, - Time = 4, - Verbose = 8, - Decoder608 = 0x10, - Decoder708 = 0x20, - DecoderXds = 0x40, - Cbraw = 0x80, - GenericNotices = 0x100, - Teletext = 0x200, - Pat = 0x400, - Pmt = 0x800, - Levenshtein = 0x1000, - Dvb = 0x2000, - Dumpdef = 0x4000, - #[cfg(feature = "enable_sharing")] - Share = 0x8000, -} diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs index 7c2829d26..5de2aac02 100644 --- a/src/rust/src/lib.rs +++ b/src/rust/src/lib.rs @@ -30,16 +30,19 @@ use std::{io::Write, os::raw::c_char, os::raw::c_int}; use args::Args; use bindings::*; use clap::{error::ErrorKind, Parser}; -use common::{CcxOptions, CcxTeletextConfig}; +use common::{CType2, FromRust}; use decoder::Dtvcc; +use lib_ccxr::{ + common::{Options, TeletextConfig}, + util::log::ExitCause, +}; +use parser::OptionsExt; use utils::is_true; use env_logger::{builder, Target}; use log::{warn, LevelFilter}; use std::ffi::CStr; -use crate::common::ExitCode; - #[cfg(test)] static mut cb_708: c_int = 0; #[cfg(test)] @@ -54,6 +57,7 @@ extern "C" { static mut cb_field2: c_int; } +#[allow(dead_code)] extern "C" { static mut MPEG_CLOCK_FREQ: c_int; static mut tlt_config: ccx_s_teletext_config; @@ -197,6 +201,7 @@ extern "C" fn ccxr_close_handle(handle: RawHandle) { extern "C" { fn version(location: *const c_char); + #[allow(dead_code)] fn set_binary_mode(); } @@ -222,7 +227,7 @@ pub unsafe extern "C" fn ccxr_parse_parameters( .collect::>(); if args.len() <= 1 { - return ExitCode::NoInputFiles as _; + return ExitCause::NoInputFiles.exit_code(); } let args: Args = match Args::try_parse_from(args) { @@ -234,11 +239,11 @@ pub unsafe extern "C" fn ccxr_parse_parameters( ErrorKind::DisplayHelp => { // Print the help string println!("{}", e); - return ExitCode::WithHelp as _; + return ExitCause::WithHelp.exit_code(); } ErrorKind::DisplayVersion => { version(*argv); - return ExitCode::WithHelp as _; + return ExitCause::WithHelp.exit_code(); } ErrorKind::UnknownArgument => { println!("Unknown Argument"); @@ -253,13 +258,13 @@ pub unsafe extern "C" fn ccxr_parse_parameters( } }; - let mut opt = CcxOptions::default(); - let mut _tlt_config = CcxTeletextConfig::default(); + let mut opt = Options::default(); + let mut _tlt_config = TeletextConfig::default(); opt.parse_parameters(&args, &mut _tlt_config); - tlt_config = _tlt_config.to_ctype(); + tlt_config = _tlt_config.to_ctype(&opt); // Convert the rust struct (CcxOptions) to C struct (ccx_s_options), so that it can be used by the C code - opt.to_ctype(_options); + _options.copy_from_rust(opt); 0 } diff --git a/src/rust/src/libccxr_exports/mod.rs b/src/rust/src/libccxr_exports/mod.rs index eb869a4c9..de1e4b43c 100644 --- a/src/rust/src/libccxr_exports/mod.rs +++ b/src/rust/src/libccxr_exports/mod.rs @@ -45,7 +45,6 @@ pub unsafe extern "C" fn ccxr_init_basic_logger(ccx_options: *const ccx_s_option 2 => OutputTarget::Quiet, _ => panic!("incorrect value for messages_target"), }; - set_logger(CCExtractorLogger::new( messages_target, mask, diff --git a/src/rust/src/libccxr_exports/time.rs b/src/rust/src/libccxr_exports/time.rs index b83e109cc..9b3cdb164 100644 --- a/src/rust/src/libccxr_exports/time.rs +++ b/src/rust/src/libccxr_exports/time.rs @@ -53,16 +53,15 @@ pub extern "C" fn ccxr_millis_to_date( let mut s = String::new(); let timestamp = Timestamp::from_millis(timestamp as i64); let date_format = match date_format { - ccx_output_date_format_ODF_NONE => TimestampFormat::None, - ccx_output_date_format_ODF_HHMMSS => TimestampFormat::HHMMSS, - ccx_output_date_format_ODF_HHMMSSMS => TimestampFormat::HHMMSSFFF, - ccx_output_date_format_ODF_SECONDS => TimestampFormat::Seconds { + ccx_output_date_format::ODF_NONE => TimestampFormat::None, + ccx_output_date_format::ODF_HHMMSS => TimestampFormat::HHMMSS, + ccx_output_date_format::ODF_HHMMSSMS => TimestampFormat::HHMMSSFFF, + ccx_output_date_format::ODF_SECONDS => TimestampFormat::Seconds { millis_separator: millis_separator as u8 as char, }, - ccx_output_date_format_ODF_DATE => TimestampFormat::Date { + ccx_output_date_format::ODF_DATE => TimestampFormat::Date { millis_separator: millis_separator as u8 as char, }, - _ => TimestampFormat::None, }; let _ = millis_to_date(timestamp, &mut s, date_format); diff --git a/src/rust/src/parser.rs b/src/rust/src/parser.rs index a079c6813..47341ec84 100644 --- a/src/rust/src/parser.rs +++ b/src/rust/src/parser.rs @@ -1,23 +1,29 @@ use args::{Args, OutFormat}; +use lib_ccxr::time::units::{Timestamp, TimestampFormat}; +use lib_ccxr::util::encoding::Encoding; +use lib_ccxr::util::log::{DebugMessageFlag, DebugMessageMask, ExitCause, OutputTarget}; +use lib_ccxr::util::time::stringztoms; use num_integer::Integer; - +use std::cell::Cell; use std::convert::TryInto; use std::fs::File; use std::io::{prelude::*, BufReader}; +use std::path::PathBuf; use std::ptr::addr_of_mut; +use std::str::FromStr; use std::string::String; +use lib_ccxr::common::*; + use cfg_if::cfg_if; -use common::CcxOptions; use time::OffsetDateTime; use crate::args::{self, InFormat}; use crate::ccx_encoders_helpers::{ CAPITALIZATION_LIST, CAPITALIZED_BUILTIN, PROFANE, PROFANE_BUILTIN, }; -use crate::common; -use crate::{args::Codec, common::CcxDebugMessageTypes, common::*}; +use crate::{activity::ActivityExt, args::CCXCodec}; cfg_if! { if #[cfg(test)] { @@ -46,43 +52,6 @@ pub static mut UTC_REFVALUE: u64 = 0; const CCX_DECODER_608_SCREEN_WIDTH: u16 = 32; static mut inputfile_capacity: i32 = 0; -fn to_ms(value: &str) -> CcxBoundaryTime { - let mut parts = value.rsplit(':'); - - let seconds: i32 = parts - .next() - .unwrap_or_else(|| { - println!("Malformed timecode: {}", value); - std::process::exit(ExitCode::MalformedParameter as i32); - }) - .parse() - .unwrap(); - - let mut minutes: i32 = 0; - - if let Some(mins) = parts.next() { - minutes = mins.parse().unwrap(); - }; - let mut hours: i32 = 0; - - if let Some(hrs) = parts.next() { - hours = hrs.parse().unwrap(); - }; - - if seconds > 60 || minutes > 60 { - println!("Malformed timecode: {}", value); - std::process::exit(ExitCode::MalformedParameter as i32); - } - - CcxBoundaryTime { - hh: hours, - mm: minutes, - ss: seconds, - time_in_ms: (hours + 60 * minutes + 3600 * seconds) as i64, - set: true, - } -} - fn get_vector_words(string_array: &[&str]) -> Vec { let mut vector = Vec::new(); for string in string_array { @@ -128,7 +97,7 @@ where Ok(val) => val, Err(_) => { println!("Malformed parameter: {}", s); - std::process::exit(ExitCode::MalformedParameter as i32) + std::process::exit(ExitCause::MalformedParameter.exit_code()); } } } @@ -170,14 +139,14 @@ fn mkvlang_params_check(lang: &str) { if _present - initial < 3 || _present - initial > 6 { println!("language codes should be xxx,xxx,xxx,....\n"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } if _present - initial == 6 { let sub_slice = &lang[initial.._present]; if !sub_slice.contains('-') { println!("language code is not of the form xxx-xx\n"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } } @@ -197,42 +166,58 @@ fn mkvlang_params_check(lang: &str) { if _present - initial < 2 || _present - initial > 5 { println!("last language code should be xxx.\n"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } if _present - initial == 5 { let sub_slice = &lang[initial.._present]; if !sub_slice.contains('-') { println!("last language code is not of the form xxx-xx\n"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } } } -impl CcxOptions { +pub trait OptionsExt { + fn set_output_format_type(&mut self, out_format: OutFormat); + fn set_output_format(&mut self, args: &Args); + fn set_input_format_type(&mut self, input_format: InFormat); + fn set_input_format(&mut self, args: &Args); + fn parse_708_services(&mut self, s: &str); + fn append_file_to_queue(&mut self, filename: &str) -> i32; + fn add_file_sequence(&mut self, filename: &mut String) -> i32; + fn parse_parameters(&mut self, args: &Args, tlt_config: &mut TeletextConfig); + fn is_inputfile_empty(&self) -> bool; +} + +impl OptionsExt for Options { + fn is_inputfile_empty(&self) -> bool { + self.inputfile.is_none() || self.inputfile.as_ref().is_some_and(|v| v.is_empty()) + } + fn set_output_format_type(&mut self, out_format: OutFormat) { match out_format { #[cfg(feature = "with_libcurl")] - OutFormat::Curl => self.write_format = CcxOutputFormat::Curl, - OutFormat::Ass => self.write_format = CcxOutputFormat::Ssa, - OutFormat::Ccd => self.write_format = CcxOutputFormat::Ccd, - OutFormat::Scc => self.write_format = CcxOutputFormat::Scc, - OutFormat::Srt => self.write_format = CcxOutputFormat::Srt, - OutFormat::Ssa => self.write_format = CcxOutputFormat::Ssa, - OutFormat::Webvtt => self.write_format = CcxOutputFormat::Webvtt, + OutFormat::Curl => self.write_format = OutputFormat::Curl, + OutFormat::Ass => self.write_format = OutputFormat::Ssa, + OutFormat::Ccd => self.write_format = OutputFormat::Ccd, + OutFormat::Scc => self.write_format = OutputFormat::Scc, + OutFormat::Srt => self.write_format = OutputFormat::Srt, + OutFormat::Ssa => self.write_format = OutputFormat::Ssa, + OutFormat::Webvtt => self.write_format = OutputFormat::WebVtt, OutFormat::WebvttFull => { - self.write_format = CcxOutputFormat::Webvtt; + self.write_format = OutputFormat::WebVtt; self.use_webvtt_styling = true; } - OutFormat::Sami => self.write_format = CcxOutputFormat::Sami, + OutFormat::Sami => self.write_format = OutputFormat::Sami, OutFormat::Txt => { - self.write_format = CcxOutputFormat::Transcript; + self.write_format = OutputFormat::Transcript; self.settings_dtvcc.no_rollup = true; } OutFormat::Ttxt => { - self.write_format = CcxOutputFormat::Transcript; - if self.date == CcxOutputDateFormat::None { - self.date = CcxOutputDateFormat::HhMmSsMs; + self.write_format = OutputFormat::Transcript; + if self.date_format == TimestampFormat::None { + self.date_format = TimestampFormat::HHMMSS; } // Sets the right things so that timestamps and the mode are printed. if !self.transcript_settings.is_final { @@ -243,20 +228,20 @@ impl CcxOptions { } } OutFormat::Report => { - self.write_format = CcxOutputFormat::Null; - self.messages_target = 0; + self.write_format = OutputFormat::Null; + self.messages_target = OutputTarget::Quiet; self.print_file_reports = true; self.demux_cfg.ts_allprogram = true; } - OutFormat::Raw => self.write_format = CcxOutputFormat::Raw, - OutFormat::Smptett => self.write_format = CcxOutputFormat::Smptett, - OutFormat::Bin => self.write_format = CcxOutputFormat::Rcwt, - OutFormat::Null => self.write_format = CcxOutputFormat::Null, - OutFormat::Dvdraw => self.write_format = CcxOutputFormat::Dvdraw, - OutFormat::Spupng => self.write_format = CcxOutputFormat::Spupng, - // OutFormat::SimpleXml => self.write_format = CcxOutputFormat::SimpleXml, - OutFormat::G608 => self.write_format = CcxOutputFormat::G608, - OutFormat::Mcc => self.write_format = CcxOutputFormat::Mcc, + OutFormat::Raw => self.write_format = OutputFormat::Raw, + OutFormat::Smptett => self.write_format = OutputFormat::SmpteTt, + OutFormat::Bin => self.write_format = OutputFormat::Rcwt, + OutFormat::Null => self.write_format = OutputFormat::Null, + OutFormat::Dvdraw => self.write_format = OutputFormat::DvdRaw, + OutFormat::Spupng => self.write_format = OutputFormat::SpuPng, + OutFormat::SimpleXml => self.write_format = OutputFormat::SimpleXml, + OutFormat::G608 => self.write_format = OutputFormat::G608, + OutFormat::Mcc => self.write_format = OutputFormat::Mcc, } } @@ -293,23 +278,23 @@ impl CcxOptions { fn set_input_format_type(&mut self, input_format: InFormat) { match input_format { #[cfg(feature = "wtv_debug")] - InFormat::Hex => self.demux_cfg.auto_stream = CcxStreamMode::HexDump, - InFormat::Es => self.demux_cfg.auto_stream = CcxStreamMode::ElementaryOrNotFound, - InFormat::Ts => self.demux_cfg.auto_stream = CcxStreamMode::Transport, - InFormat::M2ts => self.demux_cfg.auto_stream = CcxStreamMode::Transport, - InFormat::Ps => self.demux_cfg.auto_stream = CcxStreamMode::Program, - InFormat::Asf => self.demux_cfg.auto_stream = CcxStreamMode::Asf, - InFormat::Wtv => self.demux_cfg.auto_stream = CcxStreamMode::Wtv, - InFormat::Raw => self.demux_cfg.auto_stream = CcxStreamMode::McpoodlesRaw, - InFormat::Bin => self.demux_cfg.auto_stream = CcxStreamMode::Rcwt, - InFormat::Mp4 => self.demux_cfg.auto_stream = CcxStreamMode::Mp4, - InFormat::Mkv => self.demux_cfg.auto_stream = CcxStreamMode::Mkv, - InFormat::Mxf => self.demux_cfg.auto_stream = CcxStreamMode::Mxf, + InFormat::Hex => self.demux_cfg.auto_stream = StreamMode::HexDump, + InFormat::Es => self.demux_cfg.auto_stream = StreamMode::ElementaryOrNotFound, + InFormat::Ts => self.demux_cfg.auto_stream = StreamMode::Transport, + InFormat::M2ts => self.demux_cfg.auto_stream = StreamMode::Transport, + InFormat::Ps => self.demux_cfg.auto_stream = StreamMode::Program, + InFormat::Asf => self.demux_cfg.auto_stream = StreamMode::Asf, + InFormat::Wtv => self.demux_cfg.auto_stream = StreamMode::Wtv, + InFormat::Raw => self.demux_cfg.auto_stream = StreamMode::McpoodlesRaw, + InFormat::Bin => self.demux_cfg.auto_stream = StreamMode::Rcwt, + InFormat::Mp4 => self.demux_cfg.auto_stream = StreamMode::Mp4, + InFormat::Mkv => self.demux_cfg.auto_stream = StreamMode::Mkv, + InFormat::Mxf => self.demux_cfg.auto_stream = StreamMode::Mxf, } } fn set_input_format(&mut self, args: &Args) { - if self.input_source == CcxDatasource::Tcp { + if self.input_source == DataSource::Tcp { println!("Input format is changed to bin\n"); self.set_input_format_type(InFormat::Bin); return; @@ -329,7 +314,7 @@ impl CcxOptions { self.set_input_format_type(InFormat::Wtv); } else { println!("Unknown input file format: {}\n", args.input.unwrap()); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } } @@ -338,15 +323,14 @@ impl CcxOptions { let charset = if s.len() > 3 { &s[4..s.len() - 1] } else { "" }; self.settings_dtvcc.enabled = true; self.enc_cfg.dtvcc_extract = true; - charset.clone_into(&mut self.enc_cfg.all_services_charset); - self.enc_cfg.services_charsets = vec![String::new(); CCX_DTVCC_MAX_SERVICES]; + self.enc_cfg.services_charsets = DtvccServiceCharset::Same(charset.to_string()); - for i in 0..CCX_DTVCC_MAX_SERVICES { + for i in 0..DTVCC_MAX_SERVICES { self.settings_dtvcc.services_enabled[i] = true; self.enc_cfg.services_enabled[i] = true; } - self.settings_dtvcc.active_services_count = CCX_DTVCC_MAX_SERVICES as _; + self.settings_dtvcc.active_services_count = DTVCC_MAX_SERVICES as _; return; } @@ -378,13 +362,15 @@ impl CcxOptions { charsets.push(charset.clone()); } - self.enc_cfg.services_charsets = vec![String::new(); CCX_DTVCC_MAX_SERVICES]; + const ARRAY_REPEAT_VALUE: std::string::String = String::new(); + self.enc_cfg.services_charsets = + DtvccServiceCharset::Unique(Box::new([ARRAY_REPEAT_VALUE; DTVCC_MAX_SERVICES])); for (i, service) in services.iter().enumerate() { let svc = service.parse::().unwrap(); - if !(1..=CCX_DTVCC_MAX_SERVICES).contains(&svc) { - println!("[CEA-708] Malformed parameter: Invalid service number ({}), valid range is 1-{}.\n", svc, CCX_DTVCC_MAX_SERVICES); - std::process::exit(ExitCode::MalformedParameter as i32); + if !(1..=DTVCC_MAX_SERVICES).contains(&svc) { + println!("[CEA-708] Malformed parameter: Invalid service number ({}), valid range is 1-{}.\n", svc, DTVCC_MAX_SERVICES); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } self.settings_dtvcc.services_enabled[svc - 1] = true; self.enc_cfg.services_enabled[svc - 1] = true; @@ -393,16 +379,15 @@ impl CcxOptions { self.settings_dtvcc.active_services_count += 1; if charsets.len() > i && charsets[i].is_some() { - charsets[i] - .as_ref() - .unwrap() - .clone_into(&mut self.enc_cfg.services_charsets[svc - 1]); + if let DtvccServiceCharset::Unique(unique) = &mut self.enc_cfg.services_charsets { + unique[svc - 1].clone_from(charsets[i].as_ref().unwrap()); + } } } if self.settings_dtvcc.active_services_count == 0 { println!("[CEA-708] Malformed parameter: no services\n"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } } @@ -414,7 +399,12 @@ impl CcxOptions { let new_size: usize; unsafe { - if self.num_input_files >= inputfile_capacity { + let num_input_files = if let Some(ref inputfile) = self.inputfile { + inputfile.len() + } else { + 0 + }; + if num_input_files >= inputfile_capacity as _ { inputfile_capacity += 10; } @@ -427,13 +417,11 @@ impl CcxOptions { if let Some(ref mut inputfile) = self.inputfile { inputfile.resize(new_size, String::new()); - let index = self.num_input_files as usize; + let index = num_input_files; inputfile[index] = filename.to_string(); } } - self.num_input_files += 1; - 0 } @@ -494,13 +482,13 @@ impl CcxOptions { 0 } - pub fn parse_parameters(&mut self, args: &Args, tlt_config: &mut CcxTeletextConfig) { + fn parse_parameters(&mut self, args: &Args, tlt_config: &mut TeletextConfig) { if args.stdin { unsafe { set_binary_mode(); } - self.input_source = CcxDatasource::Stdin; - self.live_stream = -1; + self.input_source = DataSource::Stdin; + self.live_stream = None; } if let Some(ref files) = args.inputfile { for inputfile in files { @@ -514,18 +502,19 @@ impl CcxOptions { if rc < 0 { println!("Fatal: Not enough memory to parse parameters.\n"); - std::process::exit(ExitCode::NotEnoughMemory as i32); + std::process::exit(ExitCause::NotEnoughMemory.exit_code()); } } } - if self.num_input_files == 0 { + if self.inputfile.is_none() { println!("No input file specified\n"); - std::process::exit(ExitCode::NoInputFiles as i32); + std::process::exit(ExitCause::NoInputFiles.exit_code()); } #[cfg(feature = "hardsubx_ocr")] { + use lib_ccxr::hardsubx::*; if args.hardsubx { self.hardsubx = true; @@ -535,65 +524,57 @@ impl CcxOptions { if let Some(ref ocr_mode) = args.ocr_mode { let ocr_mode = match ocr_mode.as_str() { - "simple" | "frame" => Some(HardsubxOcrMode::Frame), - "word" => Some(HardsubxOcrMode::Word), - "letter" | "symbol" => Some(HardsubxOcrMode::Letter), + "simple" | "frame" => Some(OcrMode::Frame), + "word" => Some(OcrMode::Word), + "letter" | "symbol" => Some(OcrMode::Letter), _ => None, }; if ocr_mode.is_none() { println!("Invalid OCR mode"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } - self.hardsubx_ocr_mode = ocr_mode.unwrap() as i32; + self.hardsubx_ocr_mode = ocr_mode.unwrap_or_default(); } if let Some(ref subcolor) = args.subcolor { match subcolor.as_str() { "white" => { - self.hardsubx_subcolor = HardsubxColorType::White as i32; - self.hardsubx_hue = 0.0; + self.hardsubx_hue = ColorHue::White; } "yellow" => { - self.hardsubx_subcolor = HardsubxColorType::Yellow as i32; - self.hardsubx_hue = 60.0; + self.hardsubx_hue = ColorHue::Yellow; } "green" => { - self.hardsubx_subcolor = HardsubxColorType::Green as i32; - self.hardsubx_hue = 120.0; + self.hardsubx_hue = ColorHue::Green; } "cyan" => { - self.hardsubx_subcolor = HardsubxColorType::Cyan as i32; - self.hardsubx_hue = 180.0; + self.hardsubx_hue = ColorHue::Cyan; } "blue" => { - self.hardsubx_subcolor = HardsubxColorType::Blue as i32; - self.hardsubx_hue = 240.0; + self.hardsubx_hue = ColorHue::Blue; } "magenta" => { - self.hardsubx_subcolor = HardsubxColorType::Magenta as i32; - self.hardsubx_hue = 300.0; + self.hardsubx_hue = ColorHue::Magenta; } "red" => { - self.hardsubx_subcolor = HardsubxColorType::Red as i32; - self.hardsubx_hue = 0.0; + self.hardsubx_hue = ColorHue::Red; } _ => { - let result = subcolor.parse::(); + let result = subcolor.parse::(); if result.is_err() { println!("Invalid Hue value"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } - let hue: f32 = result.unwrap(); + let hue: f64 = result.unwrap(); if hue <= 0.0 || hue > 360.0 { println!("Invalid Hue value"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } - self.hardsubx_subcolor = HardsubxColorType::Custom as i32; - self.hardsubx_hue = hue; + self.hardsubx_hue = ColorHue::Custom(hue); } } } @@ -601,9 +582,9 @@ impl CcxOptions { if let Some(ref value) = args.min_sub_duration { if *value == 0.0 { println!("Invalid minimum subtitle duration"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } - self.hardsubx_min_sub_duration = *value; + self.hardsubx_min_sub_duration = Timestamp::from_millis((1000.0 * *value) as _); } if args.detect_italics { @@ -613,17 +594,17 @@ impl CcxOptions { if let Some(ref value) = args.conf_thresh { if !(0.0..=100.0).contains(value) { println!("Invalid confidence threshold, valid values are between 0 & 100"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } - self.hardsubx_conf_thresh = *value; + self.hardsubx_conf_thresh = *value as _; } if let Some(ref value) = args.whiteness_thresh { if !(0.0..=100.0).contains(value) { println!("Invalid whiteness threshold, valid values are between 0 & 100"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } - self.hardsubx_lum_thresh = *value; + self.hardsubx_lum_thresh = *value as _; } } } // END OF HARDSUBX @@ -708,38 +689,38 @@ impl CcxOptions { if let Some(ref codec) = args.codec { match codec { - Codec::Teletext => { - self.demux_cfg.codec = CcxCodeType::Teletext; + CCXCodec::Teletext => { + self.demux_cfg.codec = SelectCodec::Some(Codec::Teletext); } - Codec::Dvbsub => { - self.demux_cfg.codec = CcxCodeType::Dvb; + CCXCodec::Dvbsub => { + self.demux_cfg.codec = SelectCodec::Some(Codec::Dvb); } } } if let Some(ref codec) = args.no_codec { match codec { - Codec::Dvbsub => { - self.demux_cfg.nocodec = CcxCodeType::Dvb; + CCXCodec::Dvbsub => { + self.demux_cfg.nocodec = SelectCodec::Some(Codec::Dvb); } - Codec::Teletext => { - self.demux_cfg.nocodec = CcxCodeType::Teletext; + CCXCodec::Teletext => { + self.demux_cfg.nocodec = SelectCodec::Some(Codec::Teletext); } } } if let Some(ref lang) = args.dvblang { - self.dvblang = Some(lang.clone()); + self.dvblang = Some(Language::from_str(lang.as_str()).unwrap()); } - if let Some(ref lang) = args.ocrlang { - self.ocrlang = Some(lang.clone()); + if let Some(ref ocrlang) = args.ocrlang { + self.ocrlang = PathBuf::from_str(ocrlang.as_str()).unwrap_or_default(); } if let Some(ref quant) = args.quant { if !(0..=2).contains(quant) { println!("Invalid quant value"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } self.ocr_quantmode = *quant; } @@ -751,13 +732,13 @@ impl CcxOptions { if let Some(ref oem) = args.oem { if !(0..=2).contains(oem) { println!("Invalid oem value"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } self.ocr_oem = *oem; } if let Some(ref lang) = args.mkvlang { - self.mkvlang = Some(lang.to_string()); + self.mkvlang = Some(Language::from_str(lang.as_str()).unwrap()); let str = lang.as_str(); mkvlang_params_check(str); } @@ -780,18 +761,22 @@ impl CcxOptions { } if let Some(ref startcreditsnotbefore) = args.startcreditsnotbefore { - self.enc_cfg.startcreditsnotbefore = to_ms(startcreditsnotbefore.clone().as_str()); + self.enc_cfg.startcreditsnotbefore = + stringztoms(startcreditsnotbefore.clone().as_str()).unwrap(); } if let Some(ref startcreditsnotafter) = args.startcreditsnotafter { - self.enc_cfg.startcreditsnotafter = to_ms(startcreditsnotafter.clone().as_str()); + self.enc_cfg.startcreditsnotafter = + stringztoms(startcreditsnotafter.clone().as_str()).unwrap(); } if let Some(ref startcreditsforatleast) = args.startcreditsforatleast { - self.enc_cfg.startcreditsforatleast = to_ms(startcreditsforatleast.clone().as_str()); + self.enc_cfg.startcreditsforatleast = + stringztoms(startcreditsforatleast.clone().as_str()).unwrap(); } if let Some(ref startcreditsforatmost) = args.startcreditsforatmost { - self.enc_cfg.startcreditsforatmost = to_ms(startcreditsforatmost.clone().as_str()); + self.enc_cfg.startcreditsforatmost = + stringztoms(startcreditsforatmost.clone().as_str()).unwrap(); } if let Some(ref endcreditstext) = args.endcreditstext { @@ -799,11 +784,13 @@ impl CcxOptions { } if let Some(ref endcreditsforatleast) = args.endcreditsforatleast { - self.enc_cfg.endcreditsforatleast = to_ms(endcreditsforatleast.clone().as_str()); + self.enc_cfg.endcreditsforatleast = + stringztoms(endcreditsforatleast.clone().as_str()).unwrap(); } if let Some(ref endcreditsforatmost) = args.endcreditsforatmost { - self.enc_cfg.endcreditsforatmost = to_ms(endcreditsforatmost.clone().as_str()); + self.enc_cfg.endcreditsforatmost = + stringztoms(endcreditsforatmost.clone().as_str()).unwrap(); } /* More stuff */ @@ -812,10 +799,10 @@ impl CcxOptions { } if args.goptime { - self.use_gop_as_pts = 1; + self.use_gop_as_pts = Some(true); } if args.no_goptime { - self.use_gop_as_pts = -1; + self.use_gop_as_pts = Some(false); } if args.fixpadding { @@ -878,7 +865,7 @@ impl CcxOptions { if let Some(ref capfile) = args.capfile { self.enc_cfg.sentence_cap = true; - self.sentence_cap_file = Some(capfile.clone()); + self.sentence_cap_file = PathBuf::from_str(capfile.as_str()).unwrap_or_default(); } if args.kf { @@ -887,12 +874,12 @@ impl CcxOptions { if let Some(ref profanity_file) = args.profanity_file { self.enc_cfg.filter_profanity = true; - self.filter_profanity_file = Some(profanity_file.clone()); + self.filter_profanity_file = + PathBuf::from_str(profanity_file.as_str()).unwrap_or_default(); } if let Some(ref program_number) = args.program_number { - self.demux_cfg.ts_forced_program = get_atoi_hex(program_number.as_str()); - self.demux_cfg.ts_forced_program_selected = true; + self.demux_cfg.ts_forced_program = Some(get_atoi_hex(program_number.as_str())); } if args.autoprogram { @@ -905,22 +892,24 @@ impl CcxOptions { } if let Some(ref stream) = args.stream { - self.live_stream = get_atoi_hex(stream.as_str()); + self.live_stream = Some(Timestamp::from_millis( + 1000 * get_atoi_hex::(stream.as_str()), + )); } if let Some(ref defaultcolor) = args.defaultcolor { unsafe { if defaultcolor.len() != 7 || !defaultcolor.starts_with('#') { println!("Invalid default color"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } USERCOLOR_RGB.clone_from(defaultcolor); - self.settings_608.default_color = CcxDecoder608ColorCode::Userdefined; + self.settings_608.default_color = Decoder608ColorCode::Userdefined; } } if let Some(ref delay) = args.delay { - self.subs_delay = *delay; + self.subs_delay = Timestamp::from_millis(*delay); } if let Some(ref screenfuls) = args.screenfuls { @@ -928,10 +917,10 @@ impl CcxOptions { } if let Some(ref startat) = args.startat { - self.extraction_start = to_ms(startat.clone().as_str()); + self.extraction_start = stringztoms(startat.clone().as_str()).unwrap(); } if let Some(ref endat) = args.endat { - self.extraction_end = to_ms(endat.clone().as_str()); + self.extraction_end = stringztoms(endat.clone().as_str()).unwrap(); } if args.cc2 { @@ -945,14 +934,14 @@ impl CcxOptions { self.extract = 12; } else { println!("Invalid output field"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } self.is_608_enabled = true; } if args.stdout { - if self.messages_target == 1 { - self.messages_target = 2; + if self.messages_target == OutputTarget::Stdout { + self.messages_target = OutputTarget::Stderr; } self.cc_to_stdout = true; } @@ -962,7 +951,8 @@ impl CcxOptions { } if args.debugdvdsub { - self.debug_mask = CcxDebugMessageTypes::Dvb; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::DVB, DebugMessageFlag::VERBOSE); } if args.ignoreptsjumps { @@ -974,23 +964,26 @@ impl CcxOptions { } if args.quiet { - self.messages_target = 0; + self.messages_target = OutputTarget::Quiet; } if args.debug { - self.debug_mask = CcxDebugMessageTypes::Verbose; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::VERBOSE, DebugMessageFlag::VERBOSE); } if args.eia608 { - self.debug_mask = CcxDebugMessageTypes::Decoder608; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::DECODER_608, DebugMessageFlag::VERBOSE); } if args.deblev { - self.debug_mask = CcxDebugMessageTypes::Levenshtein; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::LEVENSHTEIN, DebugMessageFlag::VERBOSE); } if args.no_levdist { - self.dolevdist = 0; + self.dolevdist = false; } if let Some(ref levdistmincnt) = args.levdistmincnt { @@ -1001,15 +994,18 @@ impl CcxOptions { } if args.eia708 { - self.debug_mask = CcxDebugMessageTypes::Parse; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::DECODER_708, DebugMessageFlag::VERBOSE); } if args.goppts { - self.debug_mask = CcxDebugMessageTypes::Time; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::TIME, DebugMessageFlag::VERBOSE); } if args.vides { - self.debug_mask = CcxDebugMessageTypes::Vides; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::VIDEO_STREAM, DebugMessageFlag::VERBOSE); self.analyze_video_stream = true; } @@ -1022,23 +1018,28 @@ impl CcxOptions { } if args.xdsdebug { self.transcript_settings.xds = true; - self.debug_mask = CcxDebugMessageTypes::DecoderXds; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::DECODER_XDS, DebugMessageFlag::VERBOSE); } if args.parsedebug { - self.debug_mask = CcxDebugMessageTypes::Parse; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::PARSE, DebugMessageFlag::VERBOSE); } if args.parse_pat { - self.debug_mask = CcxDebugMessageTypes::Pat; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::PAT, DebugMessageFlag::VERBOSE); } if args.parse_pmt { - self.debug_mask = CcxDebugMessageTypes::Pmt; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::PMT, DebugMessageFlag::VERBOSE); } if args.dumpdef { - self.debug_mask = CcxDebugMessageTypes::Dumpdef; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::DUMP_DEF, DebugMessageFlag::VERBOSE); } if args.investigate_packets { @@ -1046,18 +1047,21 @@ impl CcxOptions { } if args.cbraw { - self.debug_mask = CcxDebugMessageTypes::Cbraw; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::CB_RAW, DebugMessageFlag::VERBOSE); } if args.tverbose { - self.debug_mask = CcxDebugMessageTypes::Teletext; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::TELETEXT, DebugMessageFlag::VERBOSE); tlt_config.verbose = true; } #[cfg(feature = "enable_sharing")] { if args.sharing_debug { - self.debug_mask = CcxDebugMessageTypes::Share; + self.debug_mask = + DebugMessageMask::new(DebugMessageFlag::SHARE, DebugMessageFlag::VERBOSE); tlt_config.verbose = true; } } @@ -1079,25 +1083,25 @@ impl CcxOptions { } if args.unicode { - self.enc_cfg.encoding = CcxEncodingType::Unicode; + self.enc_cfg.encoding = Encoding::Ucs2; } if args.utf8 { - self.enc_cfg.encoding = CcxEncodingType::Utf8; + self.enc_cfg.encoding = Encoding::Utf8; } if args.latin1 { - self.enc_cfg.encoding = CcxEncodingType::Latin1; + self.enc_cfg.encoding = Encoding::Latin1; } if args.usepicorder { self.usepicorder = true; } if args.myth { - self.auto_myth = 1; + self.auto_myth = Some(true); } if args.no_myth { - self.auto_myth = 0; + self.auto_myth = Some(false); } if args.wtvconvertfix { @@ -1118,28 +1122,32 @@ impl CcxOptions { } if let Some(ref datapid) = args.datapid { - self.demux_cfg.ts_cappids[self.demux_cfg.nb_ts_cappid as usize] = - get_atoi_hex(datapid.as_str()); - self.demux_cfg.nb_ts_cappid += 1; + self.demux_cfg + .ts_cappids + .push(get_atoi_hex(datapid.as_str())); } if let Some(ref datastreamtype) = args.datastreamtype { - self.demux_cfg.ts_datastreamtype = datastreamtype.clone().parse().unwrap(); + self.demux_cfg.ts_datastreamtype = + StreamType::from_repr((*datastreamtype).into()).unwrap_or_default(); + // TODO: Should I panick? } if let Some(ref streamtype) = args.streamtype { - self.demux_cfg.ts_forced_streamtype = streamtype.clone().parse().unwrap(); + self.demux_cfg.ts_forced_streamtype = + StreamType::from_repr((*streamtype).into()).unwrap_or_default(); } if let Some(ref tpage) = args.tpage { - tlt_config.page = get_atoi_hex(tpage.as_str()); - tlt_config.user_page = tlt_config.page; + tlt_config.user_page = get_atoi_hex::(tpage.as_str()) as _; + tlt_config.page = Cell::new(TeletextPageNumber::from(tlt_config.user_page)); } + let mut millis_separator = ','; // Red Hen/ UCLA Specific stuff if args.ucla { self.ucla = true; - self.millis_separator = '.'; + millis_separator = '.'; self.enc_cfg.no_bom = true; if !self.transcript_settings.is_final { @@ -1181,14 +1189,16 @@ impl CcxOptions { } if let Some(ref xmltvliveinterval) = args.xmltvliveinterval { - self.xmltvliveinterval = get_atoi_hex(xmltvliveinterval.as_str()); + self.xmltvliveinterval = + Timestamp::from_millis(1000 * get_atoi_hex::(xmltvliveinterval.as_str())); } if let Some(ref xmltvoutputinterval) = args.xmltvoutputinterval { - self.xmltvoutputinterval = get_atoi_hex(xmltvoutputinterval.as_str()); + self.xmltvoutputinterval = + Timestamp::from_millis(1000 * get_atoi_hex::(xmltvoutputinterval.as_str())); } - if let Some(ref xmltvonlycurrent) = args.xmltvonlycurrent { - self.xmltvonlycurrent = get_atoi_hex(xmltvonlycurrent.as_str()); + if args.xmltvonlycurrent { + self.xmltvonlycurrent = true; } if let Some(ref unixts) = args.unixts { @@ -1204,25 +1214,25 @@ impl CcxOptions { } if args.sects { - self.date = CcxOutputDateFormat::Seconds; + self.date_format = TimestampFormat::Seconds { millis_separator }; } if args.datets { - self.date = CcxOutputDateFormat::Date; + self.date_format = TimestampFormat::Date { millis_separator }; } if args.teletext { - self.demux_cfg.codec = CcxCodeType::Teletext; + self.demux_cfg.codec = SelectCodec::Some(Codec::Teletext); } if args.no_teletext { - self.demux_cfg.nocodec = CcxCodeType::Teletext; + self.demux_cfg.nocodec = SelectCodec::Some(Codec::Teletext); } if let Some(ref customtxt) = args.customtxt { if customtxt.to_string().len() == 7 { - if self.date == CcxOutputDateFormat::None { - self.date = CcxOutputDateFormat::HhMmSsMs; + if self.date_format == TimestampFormat::None { + self.date_format = TimestampFormat::HHMMSSFFF; } if !self.transcript_settings.is_final { @@ -1237,7 +1247,7 @@ impl CcxOptions { } } else { println!("Invalid customtxt value. It must be 7 digits long"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } } @@ -1262,7 +1272,7 @@ impl CcxOptions { self.udpport = udp.parse().unwrap(); } - self.input_source = CcxDatasource::Network; + self.input_source = DataSource::Network; } if let Some(ref addr) = args.sendto { @@ -1270,7 +1280,7 @@ impl CcxOptions { self.set_output_format_type(OutFormat::Bin); self.xmltv = 2; - self.xmltvliveinterval = 2; + self.xmltvliveinterval = Timestamp::from_millis(2000); let mut _addr: String = addr.to_string(); if let Some(saddr) = addr.strip_prefix('[') { @@ -1279,7 +1289,7 @@ impl CcxOptions { let result = _addr.find(']'); if result.is_none() { println!("Wrong address format, for IPv6 use [address]:port\n"); - std::process::exit(ExitCode::IncompatibleParameters as i32); + std::process::exit(ExitCause::IncompatibleParameters.exit_code()); } let mut br = result.unwrap(); _addr = _addr.replace(']', ""); @@ -1301,7 +1311,7 @@ impl CcxOptions { if let Some(ref tcp) = args.tcp { self.tcpport = Some(*tcp); - self.input_source = CcxDatasource::Tcp; + self.input_source = DataSource::Tcp; self.set_input_format_type(InFormat::Bin); } @@ -1314,16 +1324,20 @@ impl CcxOptions { } if let Some(ref font) = args.font { - self.enc_cfg.render_font = font.to_string(); + self.enc_cfg.render_font = PathBuf::from_str(font).unwrap_or_default(); + // TODO: Check if Panic on wrong path } if let Some(ref italics) = args.italics { - self.enc_cfg.render_font_italics = italics.to_string(); + self.enc_cfg.render_font_italics = PathBuf::from_str(italics).unwrap_or_default(); } #[cfg(feature = "with_libcurl")] - if let Some(ref curlposturl) = args.curlposturl { - self.curlposturl = Some(curlposturl.to_string()); + { + use url::Url; + if let Some(ref curlposturl) = args.curlposturl { + self.curlposturl = Url::from_str(curlposturl).ok(); + } } #[cfg(feature = "enable_sharing")] @@ -1333,7 +1347,7 @@ impl CcxOptions { } if let Some(ref sharingurl) = args.sharing_url { - self.sharing_url = Some(sharingurl.to_string()); + self.sharing_url = Some(sharingurl.to_string().parse().unwrap()); } if let Some(ref translate) = args.translate { @@ -1347,11 +1361,9 @@ impl CcxOptions { } } - if self.demux_cfg.auto_stream == CcxStreamMode::Mp4 - && self.input_source == CcxDatasource::Stdin - { + if self.demux_cfg.auto_stream == StreamMode::Mp4 && self.input_source == DataSource::Stdin { println!("MP4 requires an actual file, it's not possible to read from a stream, including stdin."); - std::process::exit(ExitCode::IncompatibleParameters as i32); + std::process::exit(ExitCause::IncompatibleParameters.exit_code()); } if self.extract_chapters { @@ -1369,13 +1381,15 @@ impl CcxOptions { if self.enc_cfg.sentence_cap { unsafe { CAPITALIZATION_LIST = get_vector_words(&CAPITALIZED_BUILTIN); - if let Some(ref sentence_cap_file) = self.sentence_cap_file { - let result = - process_word_file(sentence_cap_file, addr_of_mut!(CAPITALIZATION_LIST)); - - if result.is_err() { - println!("There was an error processing the capitalization file.\n"); - std::process::exit(ExitCode::ErrorInCapitalizationFile as i32); + if self.sentence_cap_file.exists() { + if let Some(sentence_cap_file) = self.sentence_cap_file.to_str() { + let result = + process_word_file(sentence_cap_file, addr_of_mut!(CAPITALIZATION_LIST)); + + if result.is_err() { + println!("There was an error processing the capitalization file.\n"); + std::process::exit(ExitCause::ErrorInCapitalizationFile.exit_code()); + } } } } @@ -1383,83 +1397,76 @@ impl CcxOptions { if self.enc_cfg.filter_profanity { unsafe { PROFANE = get_vector_words(&PROFANE_BUILTIN); - if let Some(ref profanityfile) = self.filter_profanity_file { - let result = process_word_file(profanityfile.as_str(), addr_of_mut!(PROFANE)); + if self.filter_profanity_file.exists() { + if let Some(profanityfile) = self.filter_profanity_file.to_str() { + let result = process_word_file(profanityfile, addr_of_mut!(PROFANE)); - if result.is_err() { - println!("There was an error processing the profanity file.\n"); - std::process::exit(ExitCode::ErrorInCapitalizationFile as i32); + if result.is_err() { + println!("There was an error processing the profanity file.\n"); + std::process::exit(ExitCause::ErrorInCapitalizationFile.exit_code()); + } } } } } - if self.demux_cfg.ts_forced_program != -1 { - self.demux_cfg.ts_forced_program_selected = true; - } - // Init telexcc redundant options tlt_config.dolevdist = self.dolevdist; tlt_config.levdistmincnt = self.levdistmincnt; tlt_config.levdistmaxpct = self.levdistmaxpct; - tlt_config.extraction_start = self.extraction_start.clone(); - tlt_config.extraction_end = self.extraction_end.clone(); + tlt_config.extraction_start = Some(self.extraction_start); + tlt_config.extraction_end = Some(self.extraction_end); tlt_config.write_format = self.write_format; - tlt_config.gui_mode_reports = self.gui_mode_reports; - tlt_config.date_format = self.date; + tlt_config.date_format = self.date_format; tlt_config.noautotimeref = self.noautotimeref; - tlt_config.send_to_srv = self.send_to_srv; tlt_config.nofontcolor = self.nofontcolor; tlt_config.nohtmlescape = self.nohtmlescape; - tlt_config.millis_separator = self.millis_separator; // teletext page number out of range - if tlt_config.page != 0 && (tlt_config.page < 100 || tlt_config.page > 899) { + if tlt_config.user_page != 0 && (tlt_config.user_page < 100 || tlt_config.user_page > 899) { println!("Teletext page number out of range (100-899)"); - std::process::exit(ExitCode::NotClassified as i32); + std::process::exit(ExitCause::NotClassified.exit_code()); } - if self.num_input_files == 0 && self.input_source == CcxDatasource::File { - std::process::exit(ExitCode::NoInputFiles as i32); + if self.is_inputfile_empty() && self.input_source == DataSource::File { + std::process::exit(ExitCause::NoInputFiles.exit_code()); } - if self.num_input_files != 0 && self.live_stream != 0 { + if !self.is_inputfile_empty() && self.live_stream.unwrap_or_default().millis() != 0 { println!("Live stream mode only supports one input file"); - std::process::exit(ExitCode::TooManyInputFiles as i32); + std::process::exit(ExitCause::TooManyInputFiles.exit_code()); } - if self.num_input_files != 0 && self.input_source == CcxDatasource::Network { + if !self.is_inputfile_empty() && self.input_source == DataSource::Network { println!("UDP mode is not compatible with input files"); - std::process::exit(ExitCode::TooManyInputFiles as i32); + std::process::exit(ExitCause::TooManyInputFiles.exit_code()); } - if self.input_source == CcxDatasource::Network || self.input_source == CcxDatasource::Tcp { - // TODO(prateekmedia): Check why we use ccx_options instead of opts - // currently using same] + if self.input_source == DataSource::Network || self.input_source == DataSource::Tcp { self.buffer_input = true; } - if self.num_input_files != 0 && self.input_source == CcxDatasource::Tcp { + if !self.is_inputfile_empty() && self.input_source == DataSource::Tcp { println!("TCP mode is not compatible with input files"); - std::process::exit(ExitCode::TooManyInputFiles as i32); + std::process::exit(ExitCause::TooManyInputFiles.exit_code()); } - if self.demux_cfg.auto_stream == CcxStreamMode::McpoodlesRaw - && self.write_format == CcxOutputFormat::Raw + if self.demux_cfg.auto_stream == StreamMode::McpoodlesRaw + && self.write_format == OutputFormat::Raw { println!("-in=raw can only be used if the output is a subtitle file."); - std::process::exit(ExitCode::IncompatibleParameters as i32); + std::process::exit(ExitCause::IncompatibleParameters.exit_code()); } - if self.demux_cfg.auto_stream == CcxStreamMode::Rcwt - && self.write_format == CcxOutputFormat::Rcwt + if self.demux_cfg.auto_stream == StreamMode::Rcwt + && self.write_format == OutputFormat::Rcwt && self.output_filename.is_none() { println!("CCExtractor's binary format can only be used simultaneously for input and\noutput if the output file name is specified given with -o.\n"); - std::process::exit(ExitCode::IncompatibleParameters as i32); + std::process::exit(ExitCause::IncompatibleParameters.exit_code()); } - if self.write_format != CcxOutputFormat::Dvdraw + if self.write_format != OutputFormat::DvdRaw && self.cc_to_stdout && self.extract != 0 && self.extract == 12 @@ -1467,47 +1474,44 @@ impl CcxOptions { println!( "You can't extract both fields to stdout at the same time in broadcast mode.\n", ); - std::process::exit(ExitCode::IncompatibleParameters as i32); + std::process::exit(ExitCause::IncompatibleParameters.exit_code()); } - if self.write_format == CcxOutputFormat::Spupng && self.cc_to_stdout { + if self.write_format == OutputFormat::SpuPng && self.cc_to_stdout { println!("You cannot use --out=spupng with -stdout.\n"); - std::process::exit(ExitCode::IncompatibleParameters as i32); + std::process::exit(ExitCause::IncompatibleParameters.exit_code()); } - if self.write_format == CcxOutputFormat::Webvtt - && self.enc_cfg.encoding != CcxEncodingType::Utf8 - { - self.enc_cfg.encoding = CcxEncodingType::Utf8; + if self.write_format == OutputFormat::WebVtt && self.enc_cfg.encoding != Encoding::Utf8 { + self.enc_cfg.encoding = Encoding::Utf8; println!("Note: Output format is WebVTT, forcing UTF-8"); - std::process::exit(ExitCode::IncompatibleParameters as i32); + std::process::exit(ExitCause::IncompatibleParameters.exit_code()); } // Check WITH_LIBCURL #[cfg(feature = "with_libcurl")] { - if self.write_format == CcxOutputFormat::Curl && self.curlposturl.is_none() { + if self.write_format == OutputFormat::Curl && self.curlposturl.is_none() { println!("You must pass a URL (--curlposturl) if output format is curl"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } - if self.write_format != CcxOutputFormat::Curl && self.curlposturl.is_some() { + if self.write_format != OutputFormat::Curl && self.curlposturl.is_some() { println!("--curlposturl requires that the format is curl"); - std::process::exit(ExitCode::MalformedParameter as i32); + std::process::exit(ExitCause::MalformedParameter.exit_code()); } } // Initialize some Encoder Configuration self.enc_cfg.extract = self.extract; - if self.num_input_files > 0 { + if !self.is_inputfile_empty() { self.enc_cfg.multiple_files = true; self.enc_cfg.first_input_file = self.inputfile.as_ref().unwrap()[0].to_string(); } self.enc_cfg.cc_to_stdout = self.cc_to_stdout; self.enc_cfg.write_format = self.write_format; self.enc_cfg.send_to_srv = self.send_to_srv; - self.enc_cfg.date_format = self.date; + self.enc_cfg.date_format = self.date_format; self.enc_cfg.transcript_settings = self.transcript_settings.clone(); - self.enc_cfg.millis_separator = self.millis_separator; self.enc_cfg.no_font_color = self.nofontcolor; self.enc_cfg.force_flush = self.force_flush; self.enc_cfg.append_mode = self.append_mode; @@ -1516,12 +1520,25 @@ impl CcxOptions { self.enc_cfg.subs_delay = self.subs_delay; self.enc_cfg.gui_mode_reports = self.gui_mode_reports; - if self.enc_cfg.render_font.is_empty() { - self.enc_cfg.render_font = DEFAULT_FONT_PATH.to_string(); + if self + .enc_cfg + .render_font + .to_str() + .unwrap_or_default() + .is_empty() + { + self.enc_cfg.render_font = PathBuf::from_str(DEFAULT_FONT_PATH).unwrap_or_default(); } - if self.enc_cfg.render_font_italics.is_empty() { - self.enc_cfg.render_font_italics = DEFAULT_FONT_PATH_ITALICS.to_string(); + if self + .enc_cfg + .render_font_italics + .to_str() + .unwrap_or_default() + .is_empty() + { + self.enc_cfg.render_font_italics = + PathBuf::from_str(DEFAULT_FONT_PATH_ITALICS).unwrap_or_default(); } if self.output_filename.is_some() && !self.multiprogram { @@ -1549,21 +1566,25 @@ impl CcxOptions { } #[cfg(test)] pub mod tests { - use crate::{args::*, common::*, parser::*}; + use crate::{args::*, parser::*}; use clap::Parser; + use lib_ccxr::{ + common::{OutputFormat, SelectCodec, StreamMode, StreamType}, + util::{encoding::Encoding, log::DebugMessageFlag}, + }; #[no_mangle] - pub extern "C" fn set_binary_mode() {} + pub unsafe extern "C" fn set_binary_mode() {} pub static mut MPEG_CLOCK_FREQ: u64 = 0; - fn parse_args(args: &[&str]) -> (CcxOptions, CcxTeletextConfig) { + fn parse_args(args: &[&str]) -> (Options, TeletextConfig) { let mut common_args = vec!["./ccextractor", "input_file"]; common_args.extend_from_slice(args); let args = Args::try_parse_from(common_args).expect("Failed to parse arguments"); - let mut options = CcxOptions { + let mut options = Options { ..Default::default() }; - let mut tlt_config = CcxTeletextConfig { + let mut tlt_config = TeletextConfig { ..Default::default() }; @@ -1576,8 +1597,8 @@ pub mod tests { let (options, _) = parse_args(&["--autoprogram", "--out", "srt", "--latin1"]); assert!(options.demux_cfg.ts_autoprogram); - assert_eq!(options.write_format, CcxOutputFormat::Srt); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.write_format, OutputFormat::Srt); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } #[test] @@ -1585,8 +1606,8 @@ pub mod tests { let (options, _) = parse_args(&["--autoprogram", "--out", "sami", "--latin1"]); assert!(options.demux_cfg.ts_autoprogram); - assert_eq!(options.write_format, CcxOutputFormat::Sami); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.write_format, OutputFormat::Sami); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } #[test] @@ -1601,8 +1622,8 @@ pub mod tests { ]); assert!(options.demux_cfg.ts_autoprogram); - assert_eq!(options.write_format, CcxOutputFormat::Transcript); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.write_format, OutputFormat::Transcript); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); assert!(options.ucla); assert!(options.transcript_settings.xds); } @@ -1610,17 +1631,17 @@ pub mod tests { #[test] fn broken_4() { let (options, _) = parse_args(&["--out", "ttxt", "--latin1"]); - assert_eq!(options.write_format, CcxOutputFormat::Transcript); + assert_eq!(options.write_format, OutputFormat::Transcript); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } #[test] fn broken_5() { let (options, _) = parse_args(&["--out", "srt", "--latin1"]); - assert_eq!(options.write_format, CcxOutputFormat::Srt); + assert_eq!(options.write_format, OutputFormat::Srt); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } #[test] @@ -1631,7 +1652,7 @@ pub mod tests { assert!(options.enc_cfg.no_bom); assert!(options.no_rollup); - assert_eq!(options.write_format, CcxOutputFormat::Transcript); + assert_eq!(options.write_format, OutputFormat::Transcript); } #[test] @@ -1646,9 +1667,20 @@ pub mod tests { assert!(options.is_708_enabled); assert!(options.no_rollup); - assert_eq!(options.write_format, CcxOutputFormat::Transcript); - assert_eq!(options.enc_cfg.services_charsets[1], "UTF-8"); - assert_eq!(options.enc_cfg.services_charsets[2], "EUC-KR"); + assert_eq!(options.write_format, OutputFormat::Transcript); + + match options.enc_cfg.services_charsets { + DtvccServiceCharset::None => { + assert!(false); + } + DtvccServiceCharset::Same(_) => { + assert!(false); + } + DtvccServiceCharset::Unique(charsets) => { + assert_eq!(charsets[1], "UTF-8"); + assert_eq!(charsets[2], "EUC-KR"); + } + } } #[test] @@ -1657,7 +1689,10 @@ pub mod tests { assert!(options.is_708_enabled); assert!(options.no_rollup); - assert_eq!(options.enc_cfg.all_services_charset, "EUC-KR"); + assert_eq!( + options.enc_cfg.services_charsets, + DtvccServiceCharset::Same("EUC-KR".to_string()), + ); } #[test] @@ -1674,9 +1709,9 @@ pub mod tests { assert!(options.demux_cfg.ts_autoprogram); assert_eq!(options.demux_cfg.ts_cappids[0], 5603); - assert_eq!(options.demux_cfg.nb_ts_cappid, 1); - assert_eq!(options.write_format, CcxOutputFormat::Srt); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.demux_cfg.ts_cappids.len(), 1); + assert_eq!(options.write_format, OutputFormat::Srt); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } #[test] @@ -1684,7 +1719,7 @@ pub mod tests { let (options, _) = parse_args(&["--stdout", "--quiet", "--no-fontcolor"]); assert!(options.cc_to_stdout); - assert_eq!(options.messages_target, 0); + assert_eq!(options.messages_target, OutputTarget::Quiet); assert_eq!(options.nofontcolor, true); } @@ -1693,8 +1728,8 @@ pub mod tests { let (options, _) = parse_args(&["--autoprogram", "--out", "ttxt", "--latin1"]); assert!(options.demux_cfg.ts_autoprogram); - assert_eq!(options.write_format, CcxOutputFormat::Transcript); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.write_format, OutputFormat::Transcript); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } #[test] @@ -1705,12 +1740,15 @@ pub mod tests { "--out", "srt", "--latin1", + "--dvblang", + "eng", ]); assert!(options.wtvconvertfix); assert!(options.demux_cfg.ts_autoprogram); - assert_eq!(options.write_format, CcxOutputFormat::Srt); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.write_format, OutputFormat::Srt); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); + assert_eq!(options.dvblang.unwrap(), Language::Eng); } #[test] @@ -1728,9 +1766,9 @@ pub mod tests { assert!(options.ucla); assert!(options.demux_cfg.ts_autoprogram); assert!(options.is_608_enabled); - assert_eq!(options.write_format, CcxOutputFormat::Transcript); + assert_eq!(options.write_format, OutputFormat::Transcript); assert_eq!(options.extract, 2); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } #[test] @@ -1740,8 +1778,8 @@ pub mod tests { assert!(options.demux_cfg.ts_autoprogram); assert!(options.enc_cfg.sentence_cap); - assert_eq!(options.write_format, CcxOutputFormat::Rcwt); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.write_format, OutputFormat::Rcwt); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } #[test] @@ -1758,17 +1796,17 @@ pub mod tests { assert!(options.ucla); assert!(options.hauppauge_mode); assert!(options.demux_cfg.ts_autoprogram); - assert_eq!(options.write_format, CcxOutputFormat::Transcript); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.write_format, OutputFormat::Transcript); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } #[test] fn mp4_1() { let (options, _) = parse_args(&["--input", "mp4", "--out", "srt", "--latin1"]); - assert_eq!(options.demux_cfg.auto_stream, CcxStreamMode::Mp4); + assert_eq!(options.demux_cfg.auto_stream, StreamMode::Mp4); - assert_eq!(options.write_format, CcxOutputFormat::Srt); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.write_format, OutputFormat::Srt); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } #[test] @@ -1777,8 +1815,8 @@ pub mod tests { assert!(options.demux_cfg.ts_autoprogram); assert!(!options.enc_cfg.no_bom); - assert_eq!(options.write_format, CcxOutputFormat::Srt); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.write_format, OutputFormat::Srt); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } #[test] @@ -1793,8 +1831,8 @@ pub mod tests { assert!(options.demux_cfg.ts_autoprogram); assert!(options.mp4vidtrack); - assert_eq!(options.write_format, CcxOutputFormat::Transcript); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.write_format, OutputFormat::Transcript); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } #[test] @@ -1811,33 +1849,33 @@ pub mod tests { assert!(options.demux_cfg.ts_autoprogram); assert!(options.ucla); assert!(options.transcript_settings.xds); - assert_eq!(options.write_format, CcxOutputFormat::Transcript); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.write_format, OutputFormat::Transcript); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } #[test] fn options_1() { let (options, _) = parse_args(&["--input", "ts"]); - assert_eq!(options.demux_cfg.auto_stream, CcxStreamMode::Transport); + assert_eq!(options.demux_cfg.auto_stream, StreamMode::Transport); } #[test] fn options_2() { let (options, _) = parse_args(&["--out", "dvdraw"]); - assert_eq!(options.write_format, CcxOutputFormat::Dvdraw); + assert_eq!(options.write_format, OutputFormat::DvdRaw); } #[test] fn options_3() { let (options, _) = parse_args(&["--goptime"]); - assert_eq!(options.use_gop_as_pts, 1); + assert_eq!(options.use_gop_as_pts, Some(true)); } #[test] fn options_4() { let (options, _) = parse_args(&["--no-goptime"]); - assert_eq!(options.use_gop_as_pts, -1); + assert_eq!(options.use_gop_as_pts, Some(false)); } #[test] @@ -1858,13 +1896,13 @@ pub mod tests { #[test] fn options_7() { let (options, _) = parse_args(&["--myth"]); - assert_eq!(options.auto_myth, 1); + assert_eq!(options.auto_myth, Some(true)); } #[test] fn options_8() { let (options, _) = parse_args(&["--program-number", "1"]); - assert_eq!(options.demux_cfg.ts_forced_program, 1); + assert_eq!(options.demux_cfg.ts_forced_program, Some(1)); } #[test] @@ -1878,15 +1916,18 @@ pub mod tests { ]); assert!(options.noautotimeref); - assert_eq!(options.demux_cfg.ts_datastreamtype, 2); - assert_eq!(options.demux_cfg.ts_forced_streamtype, 2); + assert_eq!(options.demux_cfg.ts_datastreamtype, StreamType::VideoMpeg2); + assert_eq!( + options.demux_cfg.ts_forced_streamtype, + StreamType::VideoMpeg2 + ); } #[test] fn options_10() { let (options, _) = parse_args(&["--unicode", "--no-typesetting"]); assert!(options.notypesetting); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Unicode); + assert_eq!(options.enc_cfg.encoding, Encoding::Ucs2); } #[test] @@ -1894,7 +1935,7 @@ pub mod tests { let (options, _) = parse_args(&["--utf8", "--trim"]); assert!(options.enc_cfg.trim_subs); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Utf8); + assert_eq!(options.enc_cfg.encoding, Encoding::Utf8); } #[test] @@ -1902,7 +1943,7 @@ pub mod tests { let (options, _) = parse_args(&["--capfile", "Cargo.toml"]); assert!(options.enc_cfg.sentence_cap); - assert_eq!(options.sentence_cap_file.unwrap(), "Cargo.toml"); + assert_eq!(options.sentence_cap_file.to_str(), Some("Cargo.toml")); } #[test] @@ -1913,23 +1954,33 @@ pub mod tests { assert_eq!(UTC_REFVALUE, 5); } - assert_eq!(options.write_format, CcxOutputFormat::Transcript); + assert_eq!(options.write_format, OutputFormat::Transcript); } #[test] fn options_14() { let (options, _) = parse_args(&["--datets", "--out", "txt"]); - assert_eq!(options.date, CcxOutputDateFormat::Date); - assert_eq!(options.write_format, CcxOutputFormat::Transcript); + assert_eq!( + options.date_format, + TimestampFormat::Date { + millis_separator: ',' + } + ); + assert_eq!(options.write_format, OutputFormat::Transcript); } #[test] fn options_15() { let (options, _) = parse_args(&["--sects", "--out", "txt"]); - assert_eq!(options.date, CcxOutputDateFormat::Seconds); - assert_eq!(options.write_format, CcxOutputFormat::Transcript); + assert_eq!( + options.date_format, + TimestampFormat::Seconds { + millis_separator: ',' + }, + ); + assert_eq!(options.write_format, OutputFormat::Transcript); } #[test] @@ -1937,7 +1988,7 @@ pub mod tests { let (options, _) = parse_args(&["--lf", "--out", "txt"]); assert!(options.enc_cfg.line_terminator_lf); - assert_eq!(options.write_format, CcxOutputFormat::Transcript); + assert_eq!(options.write_format, OutputFormat::Transcript); } #[test] @@ -1996,70 +2047,70 @@ pub mod tests { fn options_24() { let (options, _) = parse_args(&["--delay", "200"]); - assert_eq!(options.subs_delay, 200); + assert_eq!(options.subs_delay, Timestamp::from_millis(200)); } #[test] fn options_25() { let (options, _) = parse_args(&["--startat", "4", "--endat", "7"]); - assert_eq!(options.extraction_start.ss, 4); + assert_eq!(options.extraction_start.seconds(), 4); } #[test] fn options_26() { let (options, _) = parse_args(&["--no-codec", "dvbsub"]); - assert_eq!(options.demux_cfg.nocodec, CcxCodeType::Dvb); + assert_eq!(options.demux_cfg.nocodec, SelectCodec::Some(Codec::Dvb)); } #[test] fn options_27() { let (options, _) = parse_args(&["--debug"]); - assert_eq!(options.debug_mask, CcxDebugMessageTypes::Verbose); + assert_eq!(options.debug_mask.mask(), DebugMessageFlag::VERBOSE); } #[test] fn options_28() { let (options, _) = parse_args(&["--608"]); - assert_eq!(options.debug_mask, CcxDebugMessageTypes::Decoder608); + assert_eq!(options.debug_mask.mask(), DebugMessageFlag::DECODER_608); } #[test] fn options_29() { let (options, _) = parse_args(&["--708"]); - assert_eq!(options.debug_mask, CcxDebugMessageTypes::Parse); + assert_eq!(options.debug_mask.mask(), DebugMessageFlag::DECODER_708); } #[test] fn options_30() { let (options, _) = parse_args(&["--goppts"]); - assert_eq!(options.debug_mask, CcxDebugMessageTypes::Time); + assert_eq!(options.debug_mask.mask(), DebugMessageFlag::TIME); } #[test] fn options_31() { let (options, _) = parse_args(&["--xdsdebug"]); - assert_eq!(options.debug_mask, CcxDebugMessageTypes::DecoderXds); + assert_eq!(options.debug_mask.mask(), DebugMessageFlag::DECODER_XDS); } #[test] fn options_32() { let (options, _) = parse_args(&["--vides"]); - assert_eq!(options.debug_mask, CcxDebugMessageTypes::Vides); + assert_eq!(options.debug_mask.mask(), DebugMessageFlag::VIDEO_STREAM); } #[test] fn options_33() { let (options, _) = parse_args(&["--cbraw"]); - assert_eq!(options.debug_mask, CcxDebugMessageTypes::Cbraw); + assert_eq!(options.debug_mask.mask(), DebugMessageFlag::CB_RAW); } #[test] @@ -2080,21 +2131,21 @@ pub mod tests { fn options_36() { let (options, _) = parse_args(&["--parsedebug"]); - assert_eq!(options.debug_mask, CcxDebugMessageTypes::Parse); + assert_eq!(options.debug_mask.mask(), DebugMessageFlag::PARSE); } #[test] fn options_37() { let (options, _) = parse_args(&["--parsePAT"]); - assert_eq!(options.debug_mask, CcxDebugMessageTypes::Pat); + assert_eq!(options.debug_mask.mask(), DebugMessageFlag::PAT); } #[test] fn options_38() { let (options, _) = parse_args(&["--parsePMT"]); - assert_eq!(options.debug_mask, CcxDebugMessageTypes::Pmt); + assert_eq!(options.debug_mask.mask(), DebugMessageFlag::PMT); } #[test] @@ -2130,15 +2181,15 @@ pub mod tests { let (options, _) = parse_args(&["--xmltv", "1", "--out", "null"]); assert_eq!(options.xmltv, 1); - assert_eq!(options.write_format, CcxOutputFormat::Null); + assert_eq!(options.write_format, OutputFormat::Null); } #[test] fn options_44() { let (options, _) = parse_args(&["--codec", "dvbsub", "--out", "spupng"]); - assert_eq!(options.demux_cfg.codec, CcxCodeType::Dvb); - assert_eq!(options.write_format, CcxOutputFormat::Spupng); + assert_eq!(options.demux_cfg.codec, SelectCodec::Some(Codec::Dvb)); + assert_eq!(options.write_format, OutputFormat::SpuPng); } #[test] @@ -2150,7 +2201,7 @@ pub mod tests { "CCextractor Start credit Testing", ]); - assert_eq!(options.enc_cfg.startcreditsnotbefore.ss, 1); + assert_eq!(options.enc_cfg.startcreditsnotbefore.seconds(), 1); assert_eq!( options.enc_cfg.start_credits_text, "CCextractor Start credit Testing" @@ -2166,7 +2217,7 @@ pub mod tests { "CCextractor Start credit Testing", ]); - assert_eq!(options.enc_cfg.startcreditsnotafter.ss, 2); + assert_eq!(options.enc_cfg.startcreditsnotafter.seconds(), 2); assert_eq!( options.enc_cfg.start_credits_text, "CCextractor Start credit Testing" @@ -2182,7 +2233,7 @@ pub mod tests { "CCextractor Start credit Testing", ]); - assert_eq!(options.enc_cfg.startcreditsforatleast.ss, 1); + assert_eq!(options.enc_cfg.startcreditsforatleast.seconds(), 1); assert_eq!( options.enc_cfg.start_credits_text, "CCextractor Start credit Testing" @@ -2198,7 +2249,7 @@ pub mod tests { "CCextractor Start credit Testing", ]); - assert_eq!(options.enc_cfg.startcreditsforatmost.ss, 2); + assert_eq!(options.enc_cfg.startcreditsforatmost.seconds(), 2); assert_eq!( options.enc_cfg.start_credits_text, "CCextractor Start credit Testing" @@ -2214,7 +2265,7 @@ pub mod tests { "CCextractor Start credit Testing", ]); - assert_eq!(options.enc_cfg.endcreditsforatleast.ss, 3); + assert_eq!(options.enc_cfg.endcreditsforatleast.seconds(), 3); assert_eq!( options.enc_cfg.end_credits_text, "CCextractor Start credit Testing" @@ -2230,7 +2281,7 @@ pub mod tests { "CCextractor Start credit Testing", ]); - assert_eq!(options.enc_cfg.endcreditsforatmost.ss, 2); + assert_eq!(options.enc_cfg.endcreditsforatmost.seconds(), 2); assert_eq!( options.enc_cfg.end_credits_text, "CCextractor Start credit Testing" @@ -2242,7 +2293,7 @@ pub mod tests { let (options, tlt_config) = parse_args(&["--tverbose"]); assert!(tlt_config.verbose); - assert_eq!(options.debug_mask, CcxDebugMessageTypes::Teletext); + assert_eq!(options.debug_mask.mask(), DebugMessageFlag::TELETEXT); } #[test] @@ -2258,9 +2309,9 @@ pub mod tests { assert!(options.demux_cfg.ts_autoprogram); assert_eq!(options.demux_cfg.ts_cappids[0], 2310); - assert_eq!(options.demux_cfg.nb_ts_cappid, 1); - assert_eq!(options.write_format, CcxOutputFormat::Srt); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.demux_cfg.ts_cappids.len(), 1); + assert_eq!(options.write_format, OutputFormat::Srt); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } #[test] @@ -2276,9 +2327,9 @@ pub mod tests { ]); assert!(options.demux_cfg.ts_autoprogram); - assert_eq!(options.demux_cfg.codec, CcxCodeType::Teletext); - assert_eq!(tlt_config.page, 398); - assert_eq!(options.write_format, CcxOutputFormat::Srt); - assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert_eq!(options.demux_cfg.codec, SelectCodec::Some(Codec::Teletext)); + assert_eq!(tlt_config.user_page, 398); + assert_eq!(options.write_format, OutputFormat::Srt); + assert_eq!(options.enc_cfg.encoding, Encoding::Latin1); } } diff --git a/src/rust/src/utils.rs b/src/rust/src/utils.rs index a9ba389b7..63364d80e 100644 --- a/src/rust/src/utils.rs +++ b/src/rust/src/utils.rs @@ -11,12 +11,23 @@ pub fn is_false>(val: T) -> bool { val.into() == 0 } +/// Convert a C string to a Rust string +/// # Safety +/// The pointer passed in has to be a valid C string +pub unsafe fn c_char_to_string(c: *const ::std::os::raw::c_char) -> Option { + if c.is_null() { + return None; + } + let c_str = ffi::CStr::from_ptr(c); + Some(c_str.to_string_lossy().into_owned()) +} + /// function to convert Rust literals to C strings to be passed into functions /// # Safety /// The pointer returned has to be deallocated using from_raw() at some point pub unsafe fn string_to_c_char(a: &str) -> *mut ::std::os::raw::c_char { if a.is_empty() { - return string_null(); + return null_pointer(); } let s = ffi::CString::new(a).unwrap(); @@ -25,7 +36,7 @@ pub unsafe fn string_to_c_char(a: &str) -> *mut ::std::os::raw::c_char { /// # Safety /// The pointer returned has to be deallocated using from_raw() at some point -pub fn string_null() -> *mut c_char { +pub fn null_pointer() -> *mut T { std::ptr::null_mut() } @@ -43,7 +54,7 @@ pub fn string_to_c_chars(strs: Vec) -> *mut *mut c_char { let mut c_char_vec: Vec<*const c_char> = vec![]; for s in &cstr_vec { if s.as_bytes().is_empty() { - c_char_vec.push(string_null()); + c_char_vec.push(null_pointer()); continue; } c_char_vec.push(s.as_ptr());