diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 9f921024f..89f23fd84 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -23,7 +23,13 @@ jobs: - name: Format code run: | find src/ -type f -not -path "src/thirdparty/*" -not -path "src/lib_ccx/zvbi/*" -name '*.c' -not -path "src/GUI/icon_data.c" | xargs clang-format -i - git diff-index --quiet HEAD -- || (git diff && exit 1) + git config --global user.email "you@example.com" + git config --global user.name "Your Name" + git checkout -B formatted-branch + git add . + git commit -m "Automated code formatting by GitHub Actions" + git push origin formatted-branch + format_rust: runs-on: ubuntu-latest strategy: @@ -51,7 +57,7 @@ jobs: - name: dependencies run: sudo apt update && sudo apt install libtesseract-dev libavformat-dev libavdevice-dev libswscale-dev yasm - name: rustfmt - run: cargo fmt --all -- --check + run: cargo fmt --all - name: clippy run: | - cargo clippy -- -D warnings + cargo clippy -- -D warnings \ No newline at end of file diff --git a/src/lib_ccx/CMakeLists.txt b/src/lib_ccx/CMakeLists.txt index 4f329bcaa..3909c01d5 100644 --- a/src/lib_ccx/CMakeLists.txt +++ b/src/lib_ccx/CMakeLists.txt @@ -62,7 +62,7 @@ endif (WITH_SHARING) aux_source_directory ("${PROJECT_SOURCE_DIR}/lib_ccx/" SOURCEFILE) -add_library (ccx ${SOURCEFILE} ccx_dtvcc.h ccx_dtvcc.c ccx_encoders_mcc.c ccx_encoders_mcc.h) +add_library (ccx ${SOURCEFILE} ccx_dtvcc.h ccx_dtvcc.c ccx_encoders_mcc.c ccx_encoders_mcc.h ccx_log.c ccx_log.h) target_link_libraries (ccx ${EXTRA_LIBS}) target_include_directories (ccx PUBLIC ${EXTRA_INCLUDES}) diff --git a/src/lib_ccx/ccx_decoders_common.c b/src/lib_ccx/ccx_decoders_common.c index 7961506f6..5e290173c 100644 --- a/src/lib_ccx/ccx_decoders_common.c +++ b/src/lib_ccx/ccx_decoders_common.c @@ -14,10 +14,13 @@ made to reuse, not duplicate, as many functions as possible */ #include "ccx_decoders_vbi.h" #include "ccx_encoders_mcc.h" #include "ccx_dtvcc.h" +#include "ccx_log.h" #ifndef DISABLE_RUST extern int ccxr_process_cc_data(struct lib_cc_decode *dec_ctx, unsigned char *cc_data, int cc_count); extern void ccxr_flush_decoder(struct dtvcc_ctx *dtvcc, struct dtvcc_service_decoder *decoder); +extern int ccxr_dtvcc_init(struct lib_cc_decode *ctx); +extern void ccxr_dtvcc_free(struct lib_cc_decode *ctx); #endif uint64_t utc_refvalue = UINT64_MAX; /* _UI64_MAX/UINT64_MAX means don't use UNIX, 0 = use current system time as reference, +1 use a specific reference */ @@ -263,6 +266,12 @@ struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *sett ctx->dtvcc = dtvcc_init(setting->settings_dtvcc); ctx->dtvcc->is_active = setting->settings_dtvcc->enabled; + ctx->dtvcc_rust = NULL; // Initialize dtvcc_rust to NULL + + // Initialize the Rust Dtvcc instance + if (ccxr_dtvcc_init(ctx) != 0) { + ccx_log("Failed to initialize Rust Dtvcc instance\n"); + } if (setting->codec == CCX_CODEC_ATSC_CC) { @@ -540,6 +549,10 @@ struct lib_cc_decode *copy_decoder_context(struct lib_cc_decode *ctx) ctx_copy->dtvcc = malloc(sizeof(struct dtvcc_ctx)); memcpy(ctx_copy->dtvcc, ctx->dtvcc, sizeof(struct dtvcc_ctx)); } + + // dtvcc_rust will be initialized later by ccxr_dtvcc_init + ctx_copy->dtvcc_rust = NULL; + if (ctx->xds_ctx) { ctx_copy->xds_ctx = malloc(sizeof(struct ccx_decoders_xds_context)); @@ -593,6 +606,10 @@ void free_decoder_context(struct lib_cc_decode *ctx) freep(&ctx->timing); freep(&ctx->avc_ctx); freep(&ctx->private_data); + + // Free the Rust Dtvcc instance + ccxr_dtvcc_free(ctx); + freep(&ctx->dtvcc); freep(&ctx->xds_ctx); freep(&ctx->vbi_decoder); diff --git a/src/lib_ccx/ccx_decoders_common.h b/src/lib_ccx/ccx_decoders_common.h index 0db796b73..a08dc4d21 100644 --- a/src/lib_ccx/ccx_decoders_common.h +++ b/src/lib_ccx/ccx_decoders_common.h @@ -32,4 +32,10 @@ struct cc_subtitle* copy_subtitle(struct cc_subtitle *sub); void free_encoder_context(struct encoder_ctx *ctx); void free_decoder_context(struct lib_cc_decode *ctx); void free_subtitle(struct cc_subtitle* sub); + +extern int ccxr_process_cc_data(struct lib_cc_decode *dec_ctx, unsigned char *cc_data, int cc_count); +extern void ccxr_flush_decoder(struct dtvcc_ctx *dtvcc, struct dtvcc_service_decoder *decoder); +extern int ccxr_dtvcc_init(struct lib_cc_decode *ctx); +extern void ccxr_dtvcc_free(struct lib_cc_decode *ctx); + #endif diff --git a/src/lib_ccx/ccx_decoders_structs.h b/src/lib_ccx/ccx_decoders_structs.h index 9d4e0619b..de77edbdd 100644 --- a/src/lib_ccx/ccx_decoders_structs.h +++ b/src/lib_ccx/ccx_decoders_structs.h @@ -209,6 +209,7 @@ struct lib_cc_decode int false_pict_header; dtvcc_ctx *dtvcc; + void *dtvcc_rust; // Rust Dtvcc instance int current_field; // Analyse/use the picture information int maxtref; // Use to remember the temporal reference number diff --git a/src/lib_ccx/ccx_log.c b/src/lib_ccx/ccx_log.c new file mode 100644 index 000000000..7dbf70ed2 --- /dev/null +++ b/src/lib_ccx/ccx_log.c @@ -0,0 +1,43 @@ +#include "lib_ccx.h" +#include "utility.h" + +/** + * ccx_log - A wrapper function for mprint to be used by the Rust code + * + * This function is a simple wrapper around mprint to provide a consistent + * logging interface for the Rust code. + * + * @param fmt The format string + * @param ... Variable arguments for the format string + */ +#ifdef __APPLE__ +// On macOS, we need to ensure the function is exported with the correct name +__attribute__((visibility("default"))) +void _ccx_log(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + mprint(fmt, args); + va_end(args); +} + +// Also provide the original name for compatibility +__attribute__((visibility("default"))) +void ccx_log(const char *fmt, ...) +{ + // For compatibility, we'll just call mprint directly + va_list args; + va_start(args, fmt); + mprint(fmt, args); + va_end(args); +} +#else +__attribute__((visibility("default"))) +void ccx_log(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + mprint(fmt, args); + va_end(args); +} +#endif \ No newline at end of file diff --git a/src/lib_ccx/ccx_log.h b/src/lib_ccx/ccx_log.h new file mode 100644 index 000000000..495bddd33 --- /dev/null +++ b/src/lib_ccx/ccx_log.h @@ -0,0 +1,33 @@ +#ifndef CCX_LOG_H +#define CCX_LOG_H + +#include + +/** + * ccx_log - A wrapper function for mprint to be used by the Rust code + * + * This function is a simple wrapper around mprint to provide a consistent + * logging interface for the Rust code. + * + * @param fmt The format string + * @param ... Variable arguments for the format string + */ +#ifdef __APPLE__ +__attribute__((visibility("default"))) +#endif +void ccx_log(const char *fmt, ...); + +#ifdef __APPLE__ +/** + * _ccx_log - Internal function for macOS compatibility + * + * This function is provided for compatibility with macOS/arm64 architecture. + * + * @param fmt The format string + * @param ... Variable arguments for the format string + */ +__attribute__((visibility("default"))) +void _ccx_log(const char *fmt, ...); +#endif + +#endif /* CCX_LOG_H */ \ No newline at end of file diff --git a/src/lib_ccx/mp4.c b/src/lib_ccx/mp4.c index 05df43fe0..ef96200fd 100644 --- a/src/lib_ccx/mp4.c +++ b/src/lib_ccx/mp4.c @@ -11,11 +11,26 @@ #include "ccx_mp4.h" #include "activity.h" #include "ccx_dtvcc.h" +#include "ccx_demuxer.h" +#include "png.h" +#include "ccx_decoders_common.h" +#include "ccx_log.h" + +#ifndef DISABLE_RUST +extern int ccxr_dtvcc_init(struct lib_cc_decode *ctx); +extern int ccxr_process_cc_data(struct lib_cc_decode *dec_ctx, unsigned char *cc_data, int cc_count); +#endif #define MEDIA_TYPE(type, subtype) (((u64)(type) << 32) + (subtype)) #define GF_ISOM_SUBTYPE_C708 GF_4CC('c', '7', '0', '8') +// Convert a 4-byte type to 4 separate bytes for printing +#define PRINT_TYPE(t) (t) & 0xFF, ((t) >> 8) & 0xFF, ((t) >> 16) & 0xFF, ((t) >> 24) & 0xFF +#define ATOMPRINT mprint("Atom: [%c%c%c%c]", PRINT_TYPE(head.type)) + +#define ATOM_CONTAINS(a, b) (a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3]) + static short bswap16(short v) { return ((v >> 8) & 0x00FF) | ((v << 8) & 0xFF00); @@ -418,7 +433,29 @@ static int process_clcp(struct lib_ccx_ctx *ctx, struct encoder_ctx *enc_ctx, } // WARN: otherwise cea-708 will not work dec_ctx->dtvcc->encoder = (void *)enc_ctx; + + // Use Rust implementation if available +#ifndef DISABLE_RUST + // Initialize Rust Dtvcc if not already done + if (dec_ctx->dtvcc_rust == NULL) { + if (ccxr_dtvcc_init(dec_ctx) != 0) { + ccx_log("Failed to initialize Rust Dtvcc instance, falling back to C implementation\n"); + dtvcc_process_data(dec_ctx->dtvcc, (unsigned char *)temp); + continue; + } + } + + // Process data using Rust implementation + unsigned char cc_block[3]; + cc_block[0] = (cc_valid << 2) | cc_type; + cc_block[1] = cc_data[1]; + cc_block[2] = cc_data[2]; + + // For Rust, we send the whole cc_block instead of temp + ccxr_process_cc_data(dec_ctx, cc_block, 1); +#else dtvcc_process_data(dec_ctx->dtvcc, (unsigned char *)temp); +#endif cb_708++; } if (ctx->write_format == CCX_OF_MCC) diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index b94861db2..b90bbc7f1 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] -crate-type = ["staticlib"] +crate-type = ["staticlib", "cdylib"] [dependencies] log = "0.4.26" @@ -43,3 +43,6 @@ hardsubx_ocr = ["rsmpeg", "tesseract-sys", "leptonica-sys"] [profile.release-with-debug] inherits = "release" debug = true + +[target.'cfg(target_os = "macos")'.dependencies] +libc = "0.2" diff --git a/src/rust/src/decoder/mod.rs b/src/rust/src/decoder/mod.rs index 86a0ebcbe..31ff32434 100644 --- a/src/rust/src/decoder/mod.rs +++ b/src/rust/src/decoder/mod.rs @@ -251,12 +251,21 @@ mod test { false, )) .ok(); + + // Create and initialize needed components + let mut report = get_zero_allocated_obj::(); + let mut encoder = get_zero_allocated_obj::(); + let mut timing = get_zero_allocated_obj::(); + + // Create and initialize dtvcc_ctx with proper pointers let mut dtvcc_ctx = get_zero_allocated_obj::(); + dtvcc_ctx.report = Box::into_raw(report); + dtvcc_ctx.encoder = Box::into_raw(encoder) as *mut ::std::os::raw::c_void; + dtvcc_ctx.timing = Box::into_raw(timing); + let mut decoder = Dtvcc::new(&mut dtvcc_ctx); // Case 1: cc_type = 2 - let mut dtvcc_report = ccx_decoder_dtvcc_report::default(); - decoder.report = &mut dtvcc_report; decoder.is_header_parsed = true; decoder.is_active = true; decoder.report_enabled = true; @@ -290,6 +299,13 @@ mod test { assert_eq!(decoder.packet, vec![0xC2, 0x23, 0x45, 0x67, 0x01, 0x02]); assert_eq!(decoder.packet_length, 6); assert!(decoder.is_header_parsed); + + // Clean up raw pointers (convert back to Box and let it drop) + unsafe { + let _ = Box::from_raw(dtvcc_ctx.report); + let _ = Box::from_raw(dtvcc_ctx.encoder as *mut encoder_ctx); + let _ = Box::from_raw(dtvcc_ctx.timing); + } } #[test] @@ -300,12 +316,21 @@ mod test { false, )) .ok(); + + // Create and initialize needed components + let mut report = get_zero_allocated_obj::(); + let mut encoder = get_zero_allocated_obj::(); + let mut timing = get_zero_allocated_obj::(); + + // Create and initialize dtvcc_ctx with proper pointers let mut dtvcc_ctx = get_zero_allocated_obj::(); + dtvcc_ctx.report = Box::into_raw(report); + dtvcc_ctx.encoder = Box::into_raw(encoder) as *mut ::std::os::raw::c_void; + dtvcc_ctx.timing = Box::into_raw(timing); + let mut decoder = Dtvcc::new(&mut dtvcc_ctx); // Case 1: Without providing last_sequence - let mut dtvcc_report = ccx_decoder_dtvcc_report::default(); - decoder.report = &mut dtvcc_report; decoder.packet = vec![0xC2, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]; decoder.packet_length = 8; decoder.process_current_packet(4); @@ -314,8 +339,6 @@ mod test { assert_eq!(decoder.packet_length, 0); // due to `clear_packet()` fn call // Case 2: With providing last_sequence - let mut dtvcc_report = ccx_decoder_dtvcc_report::default(); - decoder.report = &mut dtvcc_report; decoder.packet = vec![0xC7, 0xC2, 0x12, 0x67, 0x29, 0xAB, 0xCD, 0xEF]; decoder.packet_length = 8; decoder.last_sequence = 6; @@ -325,8 +348,6 @@ mod test { assert_eq!(decoder.packet_length, 0); // due to `clear_packet()` fn call // Test case 3: Packet with extended header and multiple service blocks - let mut dtvcc_report = ccx_decoder_dtvcc_report::default(); - decoder.report = &mut dtvcc_report; decoder.packet = vec![ 0xC0, 0xE7, 0x08, 0x02, 0x01, 0x02, 0x07, 0x03, 0x03, 0x04, 0x05, ]; @@ -336,5 +357,12 @@ mod test { assert_eq!(decoder.report.services[8], 1); assert_eq!(decoder.packet_length, 0); // due to `clear_packet()` fn call + + // Clean up raw pointers (convert back to Box and let it drop) + unsafe { + let _ = Box::from_raw(dtvcc_ctx.report); + let _ = Box::from_raw(dtvcc_ctx.encoder as *mut encoder_ctx); + let _ = Box::from_raw(dtvcc_ctx.timing); + } } } diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs index d6b575d89..26aad9bfa 100644 --- a/src/rust/src/lib.rs +++ b/src/rust/src/lib.rs @@ -13,6 +13,10 @@ pub mod bindings { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } +// Constants from ccx_decoders_structs.h +pub const MAXBFRAMES: usize = 50; +pub const SORTBUF: usize = 2 * MAXBFRAMES + 1; + pub mod args; pub mod common; pub mod decoder; @@ -39,9 +43,95 @@ use log::{warn, LevelFilter}; use std::{ ffi::CStr, io::Write, - os::raw::{c_char, c_double, c_int, c_long, c_uint}, + os::raw::{c_char, c_double, c_int, c_long, c_uint, c_void}, }; +// Add a new field to store the Rust Dtvcc instance +#[repr(C)] +pub struct lib_cc_decode { + pub dtvcc_rust: *mut Dtvcc<'static>, + pub dtvcc: *mut dtvcc_ctx, + pub write_format: ccx_output_format, + pub cc_stats: [c_int; 4], + pub timing: *mut ccx_common_timing_ctx, + pub current_field: c_int, + pub extraction_start: ccx_boundary_time, + pub extraction_end: ccx_boundary_time, + pub processed_enough: c_int, + pub saw_caption_block: c_int, + pub context_cc608_field_1: *mut c_void, + pub context_cc608_field_2: *mut c_void, + pub no_rollup: c_int, + pub noscte20: c_int, + pub fix_padding: c_int, + pub subs_delay: i64, + pub extract: c_int, + pub fullbin: c_int, + pub dec_sub: cc_subtitle, + pub in_bufferdatatype: ccx_bufferdata_type, + pub hauppauge_mode: c_uint, + pub frames_since_last_gop: c_int, + pub saw_gop_header: c_int, + pub max_gop_length: c_int, + pub last_gop_length: c_int, + pub total_pulldownfields: c_uint, + pub total_pulldownframes: c_uint, + pub program_number: c_int, + pub list: list_head, + pub codec: ccx_code_type, + pub has_ccdata_buffered: c_int, + pub is_alloc: c_int, + pub avc_ctx: *mut avc_ctx, + pub private_data: *mut c_void, + pub current_hor_size: c_uint, + pub current_vert_size: c_uint, + pub current_aspect_ratio: c_uint, + pub current_frame_rate: c_uint, + pub no_bitstream_error: c_int, + pub saw_seqgoppic: c_int, + pub in_pic_data: c_int, + pub current_progressive_sequence: c_uint, + pub current_pulldownfields: c_uint, + pub temporal_reference: c_int, + pub picture_coding_type: ccx_frame_type, + pub num_key_frames: c_uint, + pub picture_structure: c_uint, + pub repeat_first_field: c_uint, + pub progressive_frame: c_uint, + pub pulldownfields: c_uint, + pub top_field_first: c_uint, + pub stat_numuserheaders: c_int, + pub stat_dvdccheaders: c_int, + pub stat_scte20ccheaders: c_int, + pub stat_replay5000headers: c_int, + pub stat_replay4000headers: c_int, + pub stat_dishheaders: c_int, + pub stat_hdtv: c_int, + pub stat_divicom: c_int, + pub false_pict_header: c_int, + pub maxtref: c_int, + pub cc_data_count: [c_int; SORTBUF], + pub cc_fts: [i64; SORTBUF], + pub cc_data_pkts: [[u8; 10*31*3+1]; SORTBUF], + pub anchor_seq_number: c_int, + pub xds_ctx: *mut ccx_decoders_xds_context, + pub vbi_decoder: *mut ccx_decoder_vbi_ctx, + pub writedata: Option c_int>, + pub ocr_quantmode: c_int, + pub prev: *mut lib_cc_decode, +} + +// Define the ccx_boundary_time struct +#[repr(C)] +#[derive(Default)] +pub struct ccx_boundary_time { + pub hh: c_int, + pub mm: c_int, + pub ss: c_int, + pub time_in_ms: i64, + pub set: c_int, +} + #[cfg(test)] static mut cb_708: c_int = 0; #[cfg(test)] @@ -86,13 +176,54 @@ pub extern "C" fn ccxr_init_logger() { .init(); } +/// Initialize a new Dtvcc instance and store it in the lib_cc_decode struct +#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")] +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub extern "C" fn ccxr_dtvcc_init(dec_ctx: *mut lib_cc_decode) -> c_int { + if dec_ctx.is_null() { + return -1; + } + + let dec_ctx = unsafe { &mut *dec_ctx }; + if dec_ctx.dtvcc.is_null() { + return -1; + } + + let dtvcc_ctx = unsafe { &mut *dec_ctx.dtvcc }; + let dtvcc = Box::new(Dtvcc::new(dtvcc_ctx)); + dec_ctx.dtvcc_rust = Box::into_raw(dtvcc) as *mut Dtvcc<'static>; + + 0 +} + +/// Free the Dtvcc instance stored in the lib_cc_decode struct +#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")] +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub extern "C" fn ccxr_dtvcc_free(dec_ctx: *mut lib_cc_decode) { + if dec_ctx.is_null() { + return; + } + + let dec_ctx = unsafe { &mut *dec_ctx }; + if !dec_ctx.dtvcc_rust.is_null() { + unsafe { + let _ = Box::from_raw(dec_ctx.dtvcc_rust); + } + dec_ctx.dtvcc_rust = std::ptr::null_mut(); + } +} + /// Process cc_data /// /// # Safety /// dec_ctx should not be a null pointer /// data should point to cc_data of length cc_count +#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")] #[no_mangle] -extern "C" fn ccxr_process_cc_data( +#[allow(improper_ctypes_definitions)] +pub extern "C" fn ccxr_process_cc_data( dec_ctx: *mut lib_cc_decode, data: *const ::std::os::raw::c_uchar, cc_count: c_int, @@ -102,13 +233,35 @@ extern "C" fn ccxr_process_cc_data( .map(|x| unsafe { *data.add(x as usize) }) .collect(); let dec_ctx = unsafe { &mut *dec_ctx }; - let dtvcc_ctx = unsafe { &mut *dec_ctx.dtvcc }; - let mut dtvcc = Dtvcc::new(dtvcc_ctx); + + // Use the stored Dtvcc instance instead of creating a new one + if dec_ctx.dtvcc_rust.is_null() { + // If dtvcc_rust is null, initialize it + if ccxr_dtvcc_init(dec_ctx) != 0 { + // If initialization fails, fall back to the old method + let dtvcc_ctx = unsafe { &mut *dec_ctx.dtvcc }; + let mut dtvcc = Dtvcc::new(dtvcc_ctx); + + for cc_block in cc_data.chunks_exact_mut(3) { + if !validate_cc_pair(cc_block) { + continue; + } + let success = do_cb(dec_ctx, &mut dtvcc, cc_block); + if success { + ret = 0; + } + } + return ret; + } + } + + let dtvcc = unsafe { &mut *dec_ctx.dtvcc_rust }; + for cc_block in cc_data.chunks_exact_mut(3) { if !validate_cc_pair(cc_block) { continue; } - let success = do_cb(dec_ctx, &mut dtvcc, cc_block); + let success = do_cb(dec_ctx, dtvcc, cc_block); if success { ret = 0; } @@ -294,6 +447,85 @@ pub unsafe extern "C" fn ccxr_parse_parameters(argc: c_int, argv: *mut *mut c_ch ExitCause::Ok.exit_code() } +// Implement Default for lib_cc_decode +impl Default for lib_cc_decode { + fn default() -> Self { + // Create a default instance with all fields zeroed/nulled + Self { + dtvcc_rust: std::ptr::null_mut(), + dtvcc: std::ptr::null_mut(), + write_format: ccx_output_format::CCX_OF_SRT, // Default to SRT + cc_stats: [0; 4], + timing: std::ptr::null_mut(), + current_field: 0, + extraction_start: ccx_boundary_time::default(), + extraction_end: ccx_boundary_time::default(), + processed_enough: 0, + saw_caption_block: 0, + context_cc608_field_1: std::ptr::null_mut(), + context_cc608_field_2: std::ptr::null_mut(), + no_rollup: 0, + noscte20: 0, + fix_padding: 0, + subs_delay: 0, + extract: 0, + fullbin: 0, + dec_sub: unsafe { std::mem::zeroed() }, + in_bufferdatatype: unsafe { std::mem::zeroed() }, + hauppauge_mode: 0, + frames_since_last_gop: 0, + saw_gop_header: 0, + max_gop_length: 0, + last_gop_length: 0, + total_pulldownfields: 0, + total_pulldownframes: 0, + program_number: 0, + list: unsafe { std::mem::zeroed() }, + codec: unsafe { std::mem::zeroed() }, + has_ccdata_buffered: 0, + is_alloc: 0, + avc_ctx: std::ptr::null_mut(), + private_data: std::ptr::null_mut(), + current_hor_size: 0, + current_vert_size: 0, + current_aspect_ratio: 0, + current_frame_rate: 0, + no_bitstream_error: 0, + saw_seqgoppic: 0, + in_pic_data: 0, + current_progressive_sequence: 0, + current_pulldownfields: 0, + temporal_reference: 0, + picture_coding_type: unsafe { std::mem::zeroed() }, + num_key_frames: 0, + picture_structure: 0, + repeat_first_field: 0, + progressive_frame: 0, + pulldownfields: 0, + top_field_first: 0, + stat_numuserheaders: 0, + stat_dvdccheaders: 0, + stat_scte20ccheaders: 0, + stat_replay5000headers: 0, + stat_replay4000headers: 0, + stat_dishheaders: 0, + stat_hdtv: 0, + stat_divicom: 0, + false_pict_header: 0, + maxtref: 0, + cc_data_count: [0; SORTBUF], + cc_fts: [0; SORTBUF], + cc_data_pkts: [[0; 10*31*3+1]; SORTBUF], + anchor_seq_number: 0, + xds_ctx: std::ptr::null_mut(), + vbi_decoder: std::ptr::null_mut(), + writedata: None, + ocr_quantmode: 0, + prev: std::ptr::null_mut(), + } + } +} + #[cfg(test)] mod test { use super::*;