transcode_audio/
transcode-audio.rs

1extern crate ffmpeg_next as ffmpeg;
2
3use std::env;
4use std::path::Path;
5
6use ffmpeg::{codec, filter, format, frame, media};
7use ffmpeg::{rescale, Rescale};
8
9fn filter(
10    spec: &str,
11    decoder: &codec::decoder::Audio,
12    encoder: &codec::encoder::Audio,
13) -> Result<filter::Graph, ffmpeg::Error> {
14    let mut filter = filter::Graph::new();
15
16    let args = format!(
17        "time_base={}:sample_rate={}:sample_fmt={}:channel_layout=0x{:x}",
18        decoder.time_base(),
19        decoder.rate(),
20        decoder.format().name(),
21        decoder.channel_layout().bits()
22    );
23
24    filter.add(&filter::find("abuffer").unwrap(), "in", &args)?;
25    filter.add(&filter::find("abuffersink").unwrap(), "out", "")?;
26
27    {
28        let mut out = filter.get("out").unwrap();
29
30        out.set_sample_format(encoder.format());
31        out.set_channel_layout(encoder.channel_layout());
32        out.set_sample_rate(encoder.rate());
33    }
34
35    filter.output("in", 0)?.input("out", 0)?.parse(spec)?;
36    filter.validate()?;
37
38    println!("{}", filter.dump());
39
40    if let Some(codec) = encoder.codec() {
41        if !codec
42            .capabilities()
43            .contains(ffmpeg::codec::capabilities::Capabilities::VARIABLE_FRAME_SIZE)
44        {
45            filter
46                .get("out")
47                .unwrap()
48                .sink()
49                .set_frame_size(encoder.frame_size());
50        }
51    }
52
53    Ok(filter)
54}
55
56struct Transcoder {
57    stream: usize,
58    filter: filter::Graph,
59    decoder: codec::decoder::Audio,
60    encoder: codec::encoder::Audio,
61    in_time_base: ffmpeg::Rational,
62    out_time_base: ffmpeg::Rational,
63}
64
65fn transcoder<P: AsRef<Path> + ?Sized>(
66    ictx: &mut format::context::Input,
67    octx: &mut format::context::Output,
68    path: &P,
69    filter_spec: &str,
70) -> Result<Transcoder, ffmpeg::Error> {
71    let input = ictx
72        .streams()
73        .best(media::Type::Audio)
74        .expect("could not find best audio stream");
75    let context = ffmpeg::codec::context::Context::from_parameters(input.parameters())?;
76    let mut decoder = context.decoder().audio()?;
77    let codec = ffmpeg::encoder::find(octx.format().codec(path, media::Type::Audio))
78        .expect("failed to find encoder")
79        .audio()?;
80    let global = octx
81        .format()
82        .flags()
83        .contains(ffmpeg::format::flag::Flags::GLOBAL_HEADER);
84
85    decoder.set_parameters(input.parameters())?;
86
87    let mut output = octx.add_stream(codec)?;
88    let context = ffmpeg::codec::context::Context::from_parameters(output.parameters())?;
89    let mut encoder = context.encoder().audio()?;
90
91    let channel_layout = codec
92        .channel_layouts()
93        .map(|cls| cls.best(decoder.channel_layout().channels()))
94        .unwrap_or(ffmpeg::channel_layout::ChannelLayout::STEREO);
95
96    if global {
97        encoder.set_flags(ffmpeg::codec::flag::Flags::GLOBAL_HEADER);
98    }
99
100    encoder.set_rate(decoder.rate() as i32);
101    encoder.set_channel_layout(channel_layout);
102    #[cfg(not(feature = "ffmpeg_7_0"))]
103    {
104        encoder.set_channels(channel_layout.channels());
105    }
106    encoder.set_format(
107        codec
108            .formats()
109            .expect("unknown supported formats")
110            .next()
111            .unwrap(),
112    );
113    encoder.set_bit_rate(decoder.bit_rate());
114    encoder.set_max_bit_rate(decoder.max_bit_rate());
115
116    encoder.set_time_base((1, decoder.rate() as i32));
117    output.set_time_base((1, decoder.rate() as i32));
118
119    let encoder = encoder.open_as(codec)?;
120    output.set_parameters(&encoder);
121
122    let filter = filter(filter_spec, &decoder, &encoder)?;
123
124    let in_time_base = decoder.time_base();
125    let out_time_base = output.time_base();
126
127    Ok(Transcoder {
128        stream: input.index(),
129        filter,
130        decoder,
131        encoder,
132        in_time_base,
133        out_time_base,
134    })
135}
136
137impl Transcoder {
138    fn send_frame_to_encoder(&mut self, frame: &ffmpeg::Frame) {
139        self.encoder.send_frame(frame).unwrap();
140    }
141
142    fn send_eof_to_encoder(&mut self) {
143        self.encoder.send_eof().unwrap();
144    }
145
146    fn receive_and_process_encoded_packets(&mut self, octx: &mut format::context::Output) {
147        let mut encoded = ffmpeg::Packet::empty();
148        while self.encoder.receive_packet(&mut encoded).is_ok() {
149            encoded.set_stream(0);
150            encoded.rescale_ts(self.in_time_base, self.out_time_base);
151            encoded.write_interleaved(octx).unwrap();
152        }
153    }
154
155    fn add_frame_to_filter(&mut self, frame: &ffmpeg::Frame) {
156        self.filter.get("in").unwrap().source().add(frame).unwrap();
157    }
158
159    fn flush_filter(&mut self) {
160        self.filter.get("in").unwrap().source().flush().unwrap();
161    }
162
163    fn get_and_process_filtered_frames(&mut self, octx: &mut format::context::Output) {
164        let mut filtered = frame::Audio::empty();
165        while self
166            .filter
167            .get("out")
168            .unwrap()
169            .sink()
170            .frame(&mut filtered)
171            .is_ok()
172        {
173            self.send_frame_to_encoder(&filtered);
174            self.receive_and_process_encoded_packets(octx);
175        }
176    }
177
178    fn send_packet_to_decoder(&mut self, packet: &ffmpeg::Packet) {
179        self.decoder.send_packet(packet).unwrap();
180    }
181
182    fn send_eof_to_decoder(&mut self) {
183        self.decoder.send_eof().unwrap();
184    }
185
186    fn receive_and_process_decoded_frames(&mut self, octx: &mut format::context::Output) {
187        let mut decoded = frame::Audio::empty();
188        while self.decoder.receive_frame(&mut decoded).is_ok() {
189            let timestamp = decoded.timestamp();
190            decoded.set_pts(timestamp);
191            self.add_frame_to_filter(&decoded);
192            self.get_and_process_filtered_frames(octx);
193        }
194    }
195}
196
197// Transcode the `best` audio stream of the input file into a the output file while applying a
198// given filter. If no filter was specified the stream gets copied (`anull` filter).
199//
200// Example 1: Transcode *.mp3 file to *.wmv while speeding it up
201// transcode-audio in.mp3 out.wmv "atempo=1.2"
202//
203// Example 2: Overlay an audio file
204// transcode-audio in.mp3 out.mp3 "amovie=overlay.mp3 [ov]; [in][ov] amerge [out]"
205//
206// Example 3: Seek to a specified position (in seconds)
207// transcode-audio in.mp3 out.mp3 anull 30
208fn main() {
209    ffmpeg::init().unwrap();
210
211    let input = env::args().nth(1).expect("missing input");
212    let output = env::args().nth(2).expect("missing output");
213    let filter = env::args().nth(3).unwrap_or_else(|| "anull".to_owned());
214    let seek = env::args().nth(4).and_then(|s| s.parse::<i64>().ok());
215
216    let mut ictx = format::input(&input).unwrap();
217    let mut octx = format::output(&output).unwrap();
218    let mut transcoder = transcoder(&mut ictx, &mut octx, &output, &filter).unwrap();
219
220    if let Some(position) = seek {
221        // If the position was given in seconds, rescale it to ffmpegs base timebase.
222        let position = position.rescale((1, 1), rescale::TIME_BASE);
223        // If this seek was embedded in the transcoding loop, a call of `flush()`
224        // for every opened buffer after the successful seek would be advisable.
225        ictx.seek(position, ..position).unwrap();
226    }
227
228    octx.set_metadata(ictx.metadata().to_owned());
229    octx.write_header().unwrap();
230
231    for (stream, mut packet) in ictx.packets() {
232        if stream.index() == transcoder.stream {
233            packet.rescale_ts(stream.time_base(), transcoder.in_time_base);
234            transcoder.send_packet_to_decoder(&packet);
235            transcoder.receive_and_process_decoded_frames(&mut octx);
236        }
237    }
238
239    transcoder.send_eof_to_decoder();
240    transcoder.receive_and_process_decoded_frames(&mut octx);
241
242    transcoder.flush_filter();
243    transcoder.get_and_process_filtered_frames(&mut octx);
244
245    transcoder.send_eof_to_encoder();
246    transcoder.receive_and_process_encoded_packets(&mut octx);
247
248    octx.write_trailer().unwrap();
249}