use ffmpeg_next as ffmpeg; use ffmpeg::format::{input, Pixel}; use ffmpeg::media::Type; use ffmpeg::codec::{context::Context, decoder}; use std::path::Path; fn main() -> Result<(), Box> { ffmpeg::init()?; let input_path = "test.mp4"; if !Path::new(input_path).exists() { eprintln!("錯誤:找不到檔案 '{}'", input_path); eprintln!("請將一個視頻文件重命名為 'test.mp4' 並放在專案根目錄。"); return Ok(()); } let mut ictx = input(&input_path)?; println!("=== 檔案基本資訊 ==="); println!("格式名稱 (Format): {}", ictx.format().name()); println!("長描述: {}", ictx.format().description()); let duration_sec = ictx.duration() as f64 / ffmpeg::ffi::AV_TIME_BASE as f64; println!("總長度 (Duration): {:.2} 秒", duration_sec); println!("\n=== 串流資訊 ==="); for (index, stream) in ictx.streams().enumerate() { let codec_params = stream.parameters(); let codec_id = codec_params.id(); let media_type = codec_params.medium(); println!("[串流 #{}]", index); println!(" 類型: {:?}", media_type); println!(" 編碼器 ID: {:?}", codec_id); if let Some(codec_descriptor) = decoder::find(codec_id) { let mut context = Context::new_with_codec(codec_descriptor); if let Err(e) = context.set_parameters(codec_params) { eprintln!(" 警告:無法設置參數: {}", e); continue; } unsafe { let ptr = context.as_mut_ptr(); match media_type { Type::Video => { let width = (*ptr).width; let height = (*ptr).height; let pix_fmt_val = (*ptr).pix_fmt; let format = Pixel::from(pix_fmt_val); let frame_rate = stream.avg_frame_rate(); let fps = if frame_rate.numerator() != 0 { frame_rate.numerator() as f64 / frame_rate.denominator() as f64 } else { 0.0 }; println!(" 解析度: {}x{}", width, height); println!(" 像素格式: {:?}", format); println!(" 幀率: {:.2} fps", fps); if let Some(codec_name) = context.codec() { println!(" 編碼器名稱: {}", codec_name.name()); } }, Type::Audio => { let sample_rate = (*ptr).sample_rate; // 【關鍵修復】新版本使用 ch_layout.nb_channels 獲取聲道數 // ch_layout 是一個 AVChannelLayout 結構體 let channels = (*ptr).ch_layout.nb_channels; let sample_fmt_val = (*ptr).sample_fmt; let format = ffmpeg::format::Sample::from(sample_fmt_val); println!(" 採樣率: {} Hz", sample_rate); println!(" 聲道數: {}", channels); println!(" 音訊格式: {:?}", format); // 可選:打印聲道佈局描述 (例如 "stereo", "5.1") // 需要引入 ffi 來調用 av_channel_layout_describe /* let mut buf = vec![0u8; 1024]; let ret = ffmpeg::ffi::av_channel_layout_describe( &(*ptr).ch_layout, buf.as_mut_ptr() as *mut i8, buf.len() as i32 ); if ret >= 0 { if let Ok(layout_str) = std::ffi::CStr::from_bytes_until_nul(&buf) { println!(" 聲道佈局: {}", layout_str.to_string_lossy()); } } */ if let Some(codec_name) = context.codec() { println!(" 編碼器名稱: {}", codec_name.name()); } }, Type::Subtitle => { println!(" (字幕串流)"); if let Some(codec_name) = context.codec() { println!(" 編碼器名稱: {}", codec_name.name()); } }, _ => { println!(" (其他類型串流)"); if let Some(codec_name) = context.codec() { println!(" 編碼器名稱: {}", codec_name.name()); } } } } // end unsafe } else { println!(" (未找到對應的解碼器)"); } // 輸出 Metadata for (key, value) in stream.metadata().iter() { println!(" Metadata [{}]: {}", key, value); } } println!("\n=== 檔案 Metadata ==="); for (key, value) in ictx.metadata().iter() { println!("{}: {}", key, value); } Ok(()) }