1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! Lazy pull-based audio sample iteration.
//!
//! This module provides [`AudioIterator`] for streaming decoded audio
//! samples without collecting the entire track into memory. Audio is
//! decoded, resampled to mono f32, and yielded in chunks.
//!
//! # Example
//!
//! ```no_run
//! use unbundle::{MediaFile, UnbundleError};
//!
//! let mut unbundler = MediaFile::open("input.mp4")?;
//! let iter = unbundler.audio().sample_iter()?;
//! for result in iter {
//! let chunk = result?;
//! println!("Got {} samples at {:?}", chunk.samples.len(), chunk.timestamp);
//! }
//! # Ok::<(), UnbundleError>(())
//! ```
use std::time::Duration;
use ffmpeg_next::{
ChannelLayout, Error as FfmpegError, Packet,
codec::context::Context as CodecContext,
decoder::Audio as AudioDecoder,
format::{Sample, sample::Type as SampleType},
frame::Audio as AudioFrame,
software::resampling::Context as ResamplingContext,
};
use crate::{error::UnbundleError, unbundle::MediaFile};
/// A chunk of decoded audio samples.
#[derive(Debug, Clone)]
pub struct AudioChunk {
/// Mono f32 samples in this chunk.
pub samples: Vec<f32>,
/// Approximate timestamp of the first sample in this chunk.
pub timestamp: Duration,
/// Sample rate of the decoded audio.
pub sample_rate: u32,
}
/// A lazy iterator over decoded audio samples.
///
/// Yields [`AudioChunk`] values containing mono f32 samples. Each chunk
/// corresponds roughly to one decoded audio frame.
pub struct AudioIterator<'a> {
unbundler: &'a mut MediaFile,
decoder: AudioDecoder,
resampler: ResamplingContext,
audio_stream_index: usize,
sample_rate: u32,
samples_yielded: u64,
decoded_frame: AudioFrame,
resampled_frame: AudioFrame,
eof_sent: bool,
done: bool,
}
impl<'a> AudioIterator<'a> {
/// Create a new audio iterator for the given stream index.
pub(crate) fn new(
unbundler: &'a mut MediaFile,
audio_stream_index: usize,
) -> Result<Self, UnbundleError> {
log::debug!("Creating AudioIterator (stream={})", audio_stream_index);
let stream = unbundler
.input_context
.stream(audio_stream_index)
.ok_or(UnbundleError::NoAudioStream)?;
let codec_parameters = stream.parameters();
let decoder_context = CodecContext::from_parameters(codec_parameters)?;
let decoder = decoder_context.decoder().audio().map_err(|e| {
UnbundleError::AudioDecodeError(format!("Failed to create audio decoder: {e}"))
})?;
let sample_rate = decoder.rate();
let resampler = ResamplingContext::get(
decoder.format(),
decoder.channel_layout(),
sample_rate,
Sample::F32(SampleType::Packed),
ChannelLayout::MONO,
sample_rate,
)
.map_err(|e| UnbundleError::AudioDecodeError(format!("Failed to create resampler: {e}")))?;
Ok(Self {
unbundler,
decoder,
resampler,
audio_stream_index,
sample_rate,
samples_yielded: 0,
decoded_frame: AudioFrame::empty(),
resampled_frame: AudioFrame::empty(),
eof_sent: false,
done: false,
})
}
}
impl<'a> Iterator for AudioIterator<'a> {
type Item = Result<AudioChunk, UnbundleError>;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
loop {
// Try to receive a decoded frame.
if self.decoder.receive_frame(&mut self.decoded_frame).is_ok() {
match self
.resampler
.run(&self.decoded_frame, &mut self.resampled_frame)
{
Ok(_) => {
let data = self.resampled_frame.data(0);
let sample_count = self.resampled_frame.samples();
let float_samples: &[f32] = unsafe {
std::slice::from_raw_parts(data.as_ptr() as *const f32, sample_count)
};
let timestamp = Duration::from_secs_f64(
self.samples_yielded as f64 / self.sample_rate as f64,
);
self.samples_yielded += sample_count as u64;
return Some(Ok(AudioChunk {
samples: float_samples.to_vec(),
timestamp,
sample_rate: self.sample_rate,
}));
}
Err(e) => {
self.done = true;
return Some(Err(UnbundleError::AudioDecodeError(format!(
"Resample error: {e}"
))));
}
}
}
// Feed more packets.
if self.eof_sent {
self.done = true;
return None;
}
let mut packet = Packet::empty();
match packet.read(&mut self.unbundler.input_context) {
Ok(()) => {
if packet.stream() as usize == self.audio_stream_index {
if let Err(e) = self.decoder.send_packet(&packet) {
self.done = true;
return Some(Err(UnbundleError::from(e)));
}
}
}
Err(FfmpegError::Eof) => {
if let Err(e) = self.decoder.send_eof() {
self.done = true;
return Some(Err(UnbundleError::from(e)));
}
self.eof_sent = true;
}
Err(_) => {
// Non-fatal read error — try next packet.
}
}
}
}
}