ff_encode/audio/
builder.rs1use std::path::PathBuf;
7use std::time::Instant;
8
9use ff_format::AudioFrame;
10
11use super::encoder_inner::{AudioEncoderConfig, AudioEncoderInner};
12use crate::{AudioCodec, Container, EncodeError};
13
14pub struct AudioEncoderBuilder {
30 pub(crate) path: PathBuf,
31 pub(crate) container: Option<Container>,
32 pub(crate) audio_sample_rate: Option<u32>,
33 pub(crate) audio_channels: Option<u32>,
34 pub(crate) audio_codec: AudioCodec,
35 pub(crate) audio_bitrate: Option<u64>,
36}
37
38impl AudioEncoderBuilder {
39 pub(crate) fn new(path: PathBuf) -> Self {
40 Self {
41 path,
42 container: None,
43 audio_sample_rate: None,
44 audio_channels: None,
45 audio_codec: AudioCodec::default(),
46 audio_bitrate: None,
47 }
48 }
49
50 #[must_use]
52 pub fn audio(mut self, sample_rate: u32, channels: u32) -> Self {
53 self.audio_sample_rate = Some(sample_rate);
54 self.audio_channels = Some(channels);
55 self
56 }
57
58 #[must_use]
60 pub fn audio_codec(mut self, codec: AudioCodec) -> Self {
61 self.audio_codec = codec;
62 self
63 }
64
65 #[must_use]
67 pub fn audio_bitrate(mut self, bitrate: u64) -> Self {
68 self.audio_bitrate = Some(bitrate);
69 self
70 }
71
72 #[must_use]
74 pub fn container(mut self, container: Container) -> Self {
75 self.container = Some(container);
76 self
77 }
78
79 pub fn build(self) -> Result<AudioEncoder, EncodeError> {
86 AudioEncoder::from_builder(self)
87 }
88}
89
90pub struct AudioEncoder {
106 inner: Option<AudioEncoderInner>,
107 _config: AudioEncoderConfig,
108 _start_time: Instant,
109}
110
111impl AudioEncoder {
112 pub fn create<P: AsRef<std::path::Path>>(path: P) -> AudioEncoderBuilder {
117 AudioEncoderBuilder::new(path.as_ref().to_path_buf())
118 }
119
120 pub(crate) fn from_builder(builder: AudioEncoderBuilder) -> Result<Self, EncodeError> {
121 let config = AudioEncoderConfig {
122 path: builder.path.clone(),
123 sample_rate: builder
124 .audio_sample_rate
125 .ok_or_else(|| EncodeError::InvalidConfig {
126 reason: "Audio sample rate not configured".to_string(),
127 })?,
128 channels: builder
129 .audio_channels
130 .ok_or_else(|| EncodeError::InvalidConfig {
131 reason: "Audio channels not configured".to_string(),
132 })?,
133 codec: builder.audio_codec,
134 bitrate: builder.audio_bitrate,
135 _progress_callback: false,
136 };
137
138 let inner = Some(AudioEncoderInner::new(&config)?);
139
140 Ok(Self {
141 inner,
142 _config: config,
143 _start_time: Instant::now(),
144 })
145 }
146
147 #[must_use]
149 pub fn actual_codec(&self) -> &str {
150 self.inner
151 .as_ref()
152 .map_or("", |inner| inner.actual_codec.as_str())
153 }
154
155 pub fn push(&mut self, frame: &AudioFrame) -> Result<(), EncodeError> {
161 let inner = self
162 .inner
163 .as_mut()
164 .ok_or_else(|| EncodeError::InvalidConfig {
165 reason: "Audio encoder not initialized".to_string(),
166 })?;
167 unsafe { inner.push_frame(frame)? };
169 Ok(())
170 }
171
172 pub fn finish(mut self) -> Result<(), EncodeError> {
178 if let Some(mut inner) = self.inner.take() {
179 unsafe { inner.finish()? };
181 }
182 Ok(())
183 }
184}
185
186impl Drop for AudioEncoder {
187 fn drop(&mut self) {
188 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 #[test]
197 fn create_should_return_builder_without_error() {
198 let _builder: AudioEncoderBuilder = AudioEncoder::create("output.m4a");
199 }
200
201 #[test]
202 fn builder_audio_settings_should_be_stored() {
203 let builder = AudioEncoder::create("output.m4a")
204 .audio(48000, 2)
205 .audio_codec(AudioCodec::Aac)
206 .audio_bitrate(192_000);
207 assert_eq!(builder.audio_sample_rate, Some(48000));
208 assert_eq!(builder.audio_channels, Some(2));
209 assert_eq!(builder.audio_codec, AudioCodec::Aac);
210 assert_eq!(builder.audio_bitrate, Some(192_000));
211 }
212
213 #[test]
214 fn build_without_sample_rate_should_return_error() {
215 let result = AudioEncoder::create("output.m4a").build();
216 assert!(result.is_err());
217 }
218}