#![allow(clippy::unwrap_used)]
mod fixtures;
use ff_encode::{AudioExtractor, EncodeError};
use fixtures::{FileGuard, assert_valid_output_file, create_black_frame, test_output_path};
#[test]
fn audio_extractor_should_fail_when_input_missing() {
let result = AudioExtractor::new("nonexistent_input.mp4", "out.mp3").run();
assert!(
result.is_err(),
"expected error for nonexistent input, got Ok(())"
);
}
#[test]
fn audio_extractor_should_fail_when_input_has_no_audio_stream() {
use ff_encode::{BitrateMode, Preset, VideoCodec, VideoEncoder};
let video_only_path = test_output_path("extractor_video_only.mp4");
let output_path = test_output_path("extractor_no_audio_out.mp3");
let _guard_v = FileGuard::new(video_only_path.clone());
let _guard_o = FileGuard::new(output_path.clone());
let mut encoder = match VideoEncoder::create(&video_only_path)
.video(160, 120, 15.0)
.video_codec(VideoCodec::Mpeg4)
.bitrate_mode(BitrateMode::Cbr(200_000))
.preset(Preset::Ultrafast)
.build()
{
Ok(enc) => enc,
Err(e) => {
println!("Skipping: video encoder unavailable ({e})");
return;
}
};
for _ in 0..15 {
let frame = create_black_frame(160, 120);
if let Err(e) = encoder.push_video(&frame) {
println!("Skipping: push_video failed ({e})");
return;
}
}
if let Err(e) = encoder.finish() {
println!("Skipping: encoder.finish failed ({e})");
return;
}
let result = AudioExtractor::new(&video_only_path, &output_path).run();
assert!(
matches!(result, Err(EncodeError::MediaOperationFailed { .. })),
"expected MediaOperationFailed for input with no audio stream, got {result:?}"
);
}
#[test]
fn audio_extractor_should_fail_when_stream_index_out_of_range() {
use ff_encode::{BitrateMode, Preset, VideoCodec, VideoEncoder};
let video_only_path = test_output_path("extractor_stream_idx_oob.mp4");
let output_path = test_output_path("extractor_stream_idx_oob_out.mp3");
let _guard_v = FileGuard::new(video_only_path.clone());
let _guard_o = FileGuard::new(output_path.clone());
let mut encoder = match VideoEncoder::create(&video_only_path)
.video(160, 120, 15.0)
.video_codec(VideoCodec::Mpeg4)
.bitrate_mode(BitrateMode::Cbr(200_000))
.preset(Preset::Ultrafast)
.build()
{
Ok(enc) => enc,
Err(e) => {
println!("Skipping: video encoder unavailable ({e})");
return;
}
};
for _ in 0..15 {
let frame = create_black_frame(160, 120);
if let Err(e) = encoder.push_video(&frame) {
println!("Skipping: push_video failed ({e})");
return;
}
}
if let Err(e) = encoder.finish() {
println!("Skipping: encoder.finish failed ({e})");
return;
}
let result = AudioExtractor::new(&video_only_path, &output_path)
.stream_index(99)
.run();
assert!(
matches!(result, Err(EncodeError::MediaOperationFailed { .. })),
"expected MediaOperationFailed for out-of-range stream_index, got {result:?}"
);
}
#[test]
fn audio_extractor_should_produce_audio_file() {
use ff_encode::{AudioCodec, BitrateMode, Preset, VideoCodec, VideoEncoder};
use ff_format::{AudioFrame, SampleFormat};
let source_path = test_output_path("extractor_source.mp4");
let output_path = test_output_path("extractor_output.mp3");
let _guard_s = FileGuard::new(source_path.clone());
let _guard_o = FileGuard::new(output_path.clone());
let mut encoder = match VideoEncoder::create(&source_path)
.video(160, 120, 15.0)
.video_codec(VideoCodec::Mpeg4)
.bitrate_mode(BitrateMode::Cbr(200_000))
.preset(Preset::Ultrafast)
.audio(44100, 1)
.audio_codec(AudioCodec::Mp3)
.audio_bitrate(64_000)
.build()
{
Ok(enc) => enc,
Err(e) => {
println!("Skipping: encoder unavailable ({e})");
return;
}
};
let sample_rate = 44100_u32;
let samples_per_video_frame = (sample_rate as f64 / 15.0) as usize;
for _ in 0..15 {
let frame = create_black_frame(160, 120);
if let Err(e) = encoder.push_video(&frame) {
println!("Skipping: push_video failed ({e})");
return;
}
let audio =
match AudioFrame::empty(samples_per_video_frame, 1, sample_rate, SampleFormat::F32) {
Ok(f) => f,
Err(e) => {
println!("Skipping: AudioFrame::empty failed ({e})");
return;
}
};
if let Err(e) = encoder.push_audio(&audio) {
println!("Skipping: push_audio failed ({e})");
return;
}
}
if let Err(e) = encoder.finish() {
println!("Skipping: encoder.finish failed ({e})");
return;
}
let result = AudioExtractor::new(&source_path, &output_path).run();
match result {
Ok(()) => {}
Err(e) => {
println!("Skipping: AudioExtractor::run failed ({e})");
return;
}
}
assert_valid_output_file(&output_path);
}
#[test]
fn audio_extractor_stream_index_should_extract_selected_stream() {
use ff_encode::{AudioCodec, BitrateMode, Preset, VideoCodec, VideoEncoder};
use ff_format::{AudioFrame, SampleFormat};
let source_path = test_output_path("extractor_idx_source.mp4");
let output_path = test_output_path("extractor_idx_output.mp3");
let _guard_s = FileGuard::new(source_path.clone());
let _guard_o = FileGuard::new(output_path.clone());
let mut encoder = match VideoEncoder::create(&source_path)
.video(160, 120, 15.0)
.video_codec(VideoCodec::Mpeg4)
.bitrate_mode(BitrateMode::Cbr(200_000))
.preset(Preset::Ultrafast)
.audio(44100, 1)
.audio_codec(AudioCodec::Mp3)
.audio_bitrate(64_000)
.build()
{
Ok(enc) => enc,
Err(e) => {
println!("Skipping: encoder unavailable ({e})");
return;
}
};
let sample_rate = 44100_u32;
let samples_per_video_frame = (sample_rate as f64 / 15.0) as usize;
for _ in 0..15 {
let frame = create_black_frame(160, 120);
if let Err(e) = encoder.push_video(&frame) {
println!("Skipping: push_video failed ({e})");
return;
}
let audio =
match AudioFrame::empty(samples_per_video_frame, 1, sample_rate, SampleFormat::F32) {
Ok(f) => f,
Err(e) => {
println!("Skipping: AudioFrame::empty failed ({e})");
return;
}
};
if let Err(e) = encoder.push_audio(&audio) {
println!("Skipping: push_audio failed ({e})");
return;
}
}
if let Err(e) = encoder.finish() {
println!("Skipping: encoder.finish failed ({e})");
return;
}
let result = AudioExtractor::new(&source_path, &output_path)
.stream_index(1)
.run();
match result {
Ok(()) => {}
Err(EncodeError::MediaOperationFailed { reason }) if reason.contains("not an audio") => {
println!("Skipping: stream 1 is not audio in this container ({reason})");
return;
}
Err(e) => {
println!("Skipping: AudioExtractor::run failed ({e})");
return;
}
}
assert_valid_output_file(&output_path);
}