diff --git a/src/rust/src/demuxer/mod.rs b/src/rust/src/demuxer/mod.rs index 6d64559ae..0bac2df97 100644 --- a/src/rust/src/demuxer/mod.rs +++ b/src/rust/src/demuxer/mod.rs @@ -41,5 +41,7 @@ pub mod demuxer_data; pub mod dvdraw; #[cfg(feature = "enable_mp4_ffmpeg")] pub mod mp4; +#[cfg(any(feature = "enable_mp4_ffmpeg", test))] +mod mprint; pub mod scc; pub mod stream_functions; diff --git a/src/rust/src/demuxer/mp4.rs b/src/rust/src/demuxer/mp4.rs index 55167faad..cf0cd4548 100644 --- a/src/rust/src/demuxer/mp4.rs +++ b/src/rust/src/demuxer/mp4.rs @@ -13,6 +13,8 @@ //! //! - `dvdsub` (DVD bitmap subtitles) via OCR through vobsub_decoder +use super::mprint::mprint_arguments; + #[cfg(feature = "enable_mp4_ffmpeg")] use rsmpeg::avformat::AVFormatContextInput; #[cfg(feature = "enable_mp4_ffmpeg")] @@ -82,6 +84,12 @@ extern "C" { fn encode_sub(enc_ctx: *mut encoder_ctx, sub: *mut cc_subtitle) -> c_int; } +#[cfg(feature = "enable_mp4_ffmpeg")] +unsafe fn mprint_text(message: *const c_char) { + let (format, message) = mprint_arguments(message); + mprint(format, message); +} + /// Track types we can extract captions from #[derive(Debug, Clone, Copy, PartialEq)] enum TrackType { @@ -143,20 +151,20 @@ pub unsafe fn processmp4_rust(ctx: *mut lib_ccx_ctx, path: &CStr, sub: *mut cc_s let path_display = String::from_utf8_lossy(path_str); let open_msg = format!("Opening '{}' with FFmpeg: \0", path_display); - mprint(open_msg.as_ptr() as *const c_char); + mprint_text(open_msg.as_ptr() as *const c_char); // Open the file with FFmpeg let fmt_ctx = match AVFormatContextInput::open(path, None, &mut None) { Ok(ctx) => ctx, Err(e) => { let err_msg = format!("Failed to open input file with FFmpeg: {}\n\0", e); - mprint(err_msg.as_ptr() as *const c_char); + mprint_text(err_msg.as_ptr() as *const c_char); return -2; } }; let ok_msg = b"ok\n\0"; - mprint(ok_msg.as_ptr() as *const c_char); + mprint_text(ok_msg.as_ptr() as *const c_char); // Set up encoder/decoder let dec_ctx = update_decoder_list(ctx); @@ -225,7 +233,7 @@ pub unsafe fn processmp4_rust(ctx: *mut lib_ccx_ctx, path: &CStr, sub: *mut cc_s "Track {}, type={} timescale={}\n\0", i, type_name, timescale ); - mprint(msg.as_ptr() as *const c_char); + mprint_text(msg.as_ptr() as *const c_char); tracks.push(TrackInfo { stream_index: i, @@ -261,11 +269,11 @@ pub unsafe fn processmp4_rust(ctx: *mut lib_ccx_ctx, path: &CStr, sub: *mut cc_s hevc_count, cc_count ); - mprint(summary.as_ptr() as *const c_char); + mprint_text(summary.as_ptr() as *const c_char); if tracks.is_empty() { let msg = b"No processable tracks found\n\0"; - mprint(msg.as_ptr() as *const c_char); + mprint_text(msg.as_ptr() as *const c_char); return 0; } @@ -490,25 +498,25 @@ pub unsafe fn processmp4_rust(ctx: *mut lib_ccx_ctx, path: &CStr, sub: *mut cc_s ccx_mp4_report_progress(ctx, 100, 100); let close_msg = b"\nClosing media: ok\n\0"; - mprint(close_msg.as_ptr() as *const c_char); + mprint_text(close_msg.as_ptr() as *const c_char); if avc_count > 0 { let msg = format!("Found {} AVC track(s). \0", avc_count); - mprint(msg.as_ptr() as *const c_char); + mprint_text(msg.as_ptr() as *const c_char); } else { let msg = b"Found no AVC track(s). \0"; - mprint(msg.as_ptr() as *const c_char); + mprint_text(msg.as_ptr() as *const c_char); } if hevc_count > 0 { let msg = format!("Found {} HEVC track(s). \0", hevc_count); - mprint(msg.as_ptr() as *const c_char); + mprint_text(msg.as_ptr() as *const c_char); } if cc_count > 0 { let msg = format!("Found {} CC track(s).\n\0", cc_count); - mprint(msg.as_ptr() as *const c_char); + mprint_text(msg.as_ptr() as *const c_char); } else { let msg = b"Found no dedicated CC track(s).\n\0"; - mprint(msg.as_ptr() as *const c_char); + mprint_text(msg.as_ptr() as *const c_char); } (*ctx).freport.mp4_cc_track_cnt = cc_count as u32; @@ -639,24 +647,24 @@ pub unsafe fn dumpchapters_rust(_ctx: *mut lib_ccx_ctx, path: &CStr) -> c_int { let path_display = String::from_utf8_lossy(path_str); let open_msg = format!("Opening '{}': \0", path_display); - mprint(open_msg.as_ptr() as *const c_char); + mprint_text(open_msg.as_ptr() as *const c_char); let fmt_ctx = match AVFormatContextInput::open(path, None, &mut None) { Ok(ctx) => ctx, Err(_) => { let msg = b"failed to open\n\0"; - mprint(msg.as_ptr() as *const c_char); + mprint_text(msg.as_ptr() as *const c_char); return 5; } }; let ok_msg = b"ok\n\0"; - mprint(ok_msg.as_ptr() as *const c_char); + mprint_text(ok_msg.as_ptr() as *const c_char); let nb_chapters = (*fmt_ctx.as_ptr()).nb_chapters as usize; if nb_chapters == 0 { let msg = b"No chapters information found!\n\0"; - mprint(msg.as_ptr() as *const c_char); + mprint_text(msg.as_ptr() as *const c_char); return 0; } @@ -667,7 +675,7 @@ pub unsafe fn dumpchapters_rust(_ctx: *mut lib_ccx_ctx, path: &CStr) -> c_int { }; let writing_msg = format!("Writing chapters into {}\n\0", out_name); - mprint(writing_msg.as_ptr() as *const c_char); + mprint_text(writing_msg.as_ptr() as *const c_char); use std::io::Write; for i in 0..nb_chapters { diff --git a/src/rust/src/demuxer/mprint.rs b/src/rust/src/demuxer/mprint.rs new file mode 100644 index 000000000..554d803fb --- /dev/null +++ b/src/rust/src/demuxer/mprint.rs @@ -0,0 +1,25 @@ +use std::os::raw::c_char; + +const TEXT_FORMAT: &[u8] = b"%s\0"; + +pub(super) fn mprint_arguments(message: *const c_char) -> (*const c_char, *const c_char) { + (TEXT_FORMAT.as_ptr() as *const c_char, message) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::ffi::{CStr, CString}; + + #[test] + fn text_uses_a_literal_format() { + let message = CString::new("Opening '%s%n.mp4' with FFmpeg").unwrap(); + let (format, argument) = mprint_arguments(message.as_ptr()); + + assert_eq!(unsafe { CStr::from_ptr(format) }.to_bytes(), b"%s"); + assert_eq!( + unsafe { CStr::from_ptr(argument) }.to_bytes(), + message.as_bytes() + ); + } +}