Initial commit: Video analyzer with ffmpeg-next
This commit is contained in:
135
src/main.rs
Normal file
135
src/main.rs
Normal file
@@ -0,0 +1,135 @@
|
||||
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<dyn std::error::Error>> {
|
||||
ffmpeg::init()?;
|
||||
|
||||
let input_path = "test.mp4";
|
||||
|
||||
if !Path::new(input_path).exists() {
|
||||
eprintln!("錯誤:找不到檔案 '{}'", input_path);
|
||||
eprintln!("請將一個視頻文件重命名為 'test.mp4' 並放在專案根目錄。");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let 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(())
|
||||
}
|
||||
135
src/main.rs.bak
Normal file
135
src/main.rs.bak
Normal file
@@ -0,0 +1,135 @@
|
||||
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<dyn std::error::Error>> {
|
||||
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(())
|
||||
}
|
||||
Reference in New Issue
Block a user