diff --git a/tests/transcode_aac.rs b/tests/transcode_aac.rs index 419f4a9..74ea817 100644 --- a/tests/transcode_aac.rs +++ b/tests/transcode_aac.rs @@ -1,7 +1,6 @@ -// Due to usage of new channel layout api +//! RIIR: https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/transcode_aac.c use anyhow::{bail, Context as AnyhowContext, Result}; use cstr::cstr; -use once_cell::sync::Lazy as SyncLazy; use rsmpeg::{ avcodec::{AVCodec, AVCodecContext}, avformat::{AVFormatContextInput, AVFormatContextOutput}, @@ -10,18 +9,30 @@ use rsmpeg::{ ffi, swresample::SwrContext, }; -use std::{ffi::CStr, sync::Mutex}; +use std::{ + ffi::CStr, + sync::atomic::{AtomicI64, Ordering}, +}; + +/// The output bit rate in bit/s +const OUTPUT_BIT_RATE: i64 = 96000; +/// The number of output channels +const OUTPUT_CHANNELS: i32 = 2; fn open_input_file(input_file: &CStr) -> Result<(AVFormatContextInput, AVCodecContext, usize)> { - let input_format_context = - AVFormatContextInput::open(input_file, None, &mut None).context("Failed to get encoder")?; + let input_format_context = AVFormatContextInput::open(input_file, None, &mut None) + .context("Could not open input file")?; let (audio_index, decoder) = input_format_context .find_best_stream(ffi::AVMediaType_AVMEDIA_TYPE_AUDIO)? .context("Failed to find audio stream")?; + let stream = &input_format_context.streams()[audio_index]; let mut decode_context = AVCodecContext::new(&decoder); - decode_context.apply_codecpar(&input_format_context.streams()[audio_index].codecpar())?; - decode_context.open(None)?; + decode_context.apply_codecpar(&stream.codecpar())?; + decode_context + .open(None) + .context("Could not open input codec")?; + decode_context.set_pkt_timebase(stream.time_base); Ok((input_format_context, decode_context, audio_index)) } @@ -39,19 +50,13 @@ fn open_output_file( let mut encode_context = AVCodecContext::new(&encode_codec); - const OUTPUT_CHANNELS: i32 = 2; - const OUTPUT_BIT_RATE: i64 = 96000; // Set the basic encoder parameters. // The input file's sample rate is used to avoid a sample rate conversion. - encode_context.set_channels(OUTPUT_CHANNELS); encode_context.set_ch_layout(AVChannelLayout::from_nb_channels(OUTPUT_CHANNELS).into_inner()); encode_context.set_sample_rate(decode_context.sample_rate); encode_context.set_sample_fmt(encode_codec.sample_fmts().unwrap()[0]); encode_context.set_bit_rate(OUTPUT_BIT_RATE); - // Allow the use of the experimental AAC encoder. - encode_context.set_strict_std_compliance(ffi::FF_COMPLIANCE_EXPERIMENTAL); - // Open the encoder for the audio stream to use it later. encode_context.open(None)?; @@ -60,7 +65,7 @@ fn open_output_file( let mut stream = output_format_context.new_stream(); stream.set_codecpar(encode_context.extract_codecpar()); // Set the sample rate for the container. - stream.set_time_base(ra(decode_context.sample_rate, 1)); + stream.set_time_base(ra(1, decode_context.sample_rate)); } Ok((output_format_context, encode_context)) @@ -78,38 +83,41 @@ fn init_resampler( decode_context.sample_fmt, decode_context.sample_rate, ) - .context("Swrcontext parameters incorrect.")?; - resample_context.init()?; + .context("Could not allocate resample context")?; + resample_context + .init() + .context("Could not open resample context")?; Ok(resample_context) } fn add_samples_to_fifo( fifo: &mut AVAudioFifo, samples_buffer: &AVSamples, - num_samples: i32, + frame_size: i32, ) -> Result<()> { - fifo.realloc(fifo.size() + num_samples); - if unsafe { fifo.write(samples_buffer.audio_data.as_ptr(), num_samples) }? < num_samples { - bail!("samples doesn't all written."); - } + fifo.realloc(fifo.size() + frame_size); + unsafe { fifo.write(samples_buffer.audio_data.as_ptr(), frame_size) } + .context("Could not write data to FIFO")?; Ok(()) } -fn create_output_frame( +fn init_output_frame( nb_samples: i32, ch_layout: ffi::AVChannelLayout, sample_fmt: i32, sample_rate: i32, -) -> AVFrame { +) -> Result { let mut frame = AVFrame::new(); frame.set_nb_samples(nb_samples); frame.set_ch_layout(ch_layout); frame.set_format(sample_fmt); frame.set_sample_rate(sample_rate); - frame.alloc_buffer().unwrap(); - frame + .get_buffer(0) + .context("Could not allocate output frame samples")?; + + Ok(frame) } /// Return boolean: if data is written. @@ -118,12 +126,10 @@ fn encode_audio_frame( output_format_context: &mut AVFormatContextOutput, encode_context: &mut AVCodecContext, ) -> Result<()> { - static PTS: SyncLazy> = SyncLazy::new(|| Mutex::new(0)); + static PTS: AtomicI64 = AtomicI64::new(0); if let Some(frame) = frame.as_mut() { - let mut pts = PTS.lock().unwrap(); - frame.set_pts(*pts); - *pts += frame.nb_samples as i64; + frame.set_pts(PTS.fetch_add(frame.nb_samples as i64, Ordering::Relaxed)); } encode_context.send_frame(frame.as_ref())?; @@ -133,10 +139,12 @@ fn encode_audio_frame( Err(RsmpegError::EncoderDrainError) | Err(RsmpegError::EncoderFlushedError) => { break; } - Err(e) => bail!(e), + Err(e) => Err(e).context("Could not encode frame")?, }; - output_format_context.write_frame(&mut packet)?; + output_format_context + .write_frame(&mut packet) + .context("Could not write frame")?; } Ok(()) } @@ -146,15 +154,15 @@ fn load_encode_and_write( output_format_context: &mut AVFormatContextOutput, encode_context: &mut AVCodecContext, ) -> Result<()> { - let nb_samples = fifo.size().min(encode_context.frame_size); - let mut frame = create_output_frame( - nb_samples, + let frame_size = fifo.size().min(encode_context.frame_size); + let mut frame = init_output_frame( + frame_size, encode_context.ch_layout().clone().into_inner(), encode_context.sample_fmt, encode_context.sample_rate, - ); - if unsafe { fifo.read(frame.data_mut().as_mut_ptr(), nb_samples)? } < nb_samples { - bail!("samples doesn't all read"); + )?; + if unsafe { fifo.read(frame.data_mut().as_mut_ptr(), frame_size)? } < frame_size { + bail!("Could not read data from FIFO"); } encode_audio_frame(Some(frame), output_format_context, encode_context)?; Ok(()) @@ -170,27 +178,36 @@ fn transcode_aac(input_file: &CStr, output_file: &CStr) -> Result<()> { open_output_file(output_file, &decode_context)?; // Initialize the resampler to be able to convert audio sample formats. - let resample_context = init_resampler(&mut decode_context, &mut encode_context)?; + let mut resample_context = init_resampler(&mut decode_context, &mut encode_context)?; // Initialize the FIFO buffer to store audio samples to be encoded. - let mut fifo = AVAudioFifo::new(encode_context.sample_fmt, encode_context.channels, 1); + let mut fifo = AVAudioFifo::new( + encode_context.sample_fmt, + encode_context.ch_layout.nb_channels, + 1, + ); // Write the header of the output file container. - output_format_context.write_header(&mut None)?; - - let output_nb_sample = encode_context.frame_size; + output_format_context + .write_header(&mut None) + .context("Could not write output file header")?; // Loop as long as we have input samples to read or output samples to write; // abort as soon as we have neither. loop { + let output_frame_size = encode_context.frame_size; + loop { // We get enough audio samples. - if fifo.size() >= output_nb_sample { + if fifo.size() >= output_frame_size { break; } // Break when no more input packets. - let packet = match input_format_context.read_packet()? { + let packet = match input_format_context + .read_packet() + .context("Could not read frame")? + { Some(x) => x, None => break, }; @@ -200,7 +217,9 @@ fn transcode_aac(input_file: &CStr, output_file: &CStr) -> Result<()> { continue; } - decode_context.send_packet(Some(&packet))?; + decode_context + .send_packet(Some(&packet)) + .context("Could not send packet for decoding")?; loop { let frame = match decode_context.receive_frame() { @@ -208,11 +227,11 @@ fn transcode_aac(input_file: &CStr, output_file: &CStr) -> Result<()> { Err(RsmpegError::DecoderDrainError) | Err(RsmpegError::DecoderFlushedError) => { break; } - Err(e) => bail!(e), + Err(e) => Err(e).context("Could not decode frame")?, }; let mut output_samples = AVSamples::new( - encode_context.channels, + encode_context.ch_layout.nb_channels, frame.nb_samples, encode_context.sample_fmt, 0, @@ -220,11 +239,14 @@ fn transcode_aac(input_file: &CStr, output_file: &CStr) -> Result<()> { .context("Create samples buffer failed.")?; unsafe { - resample_context.convert( - &mut output_samples, - frame.extended_data as *const _, - frame.nb_samples, - )?; + resample_context + .convert( + output_samples.audio_data.as_mut_ptr(), + output_samples.nb_samples, + frame.extended_data as *const _, + frame.nb_samples, + ) + .context("Could not convert input samples")?; } add_samples_to_fifo(&mut fifo, &output_samples, frame.nb_samples)?; @@ -232,12 +254,12 @@ fn transcode_aac(input_file: &CStr, output_file: &CStr) -> Result<()> { } // If we still cannot get enough samples, break. - if fifo.size() < output_nb_sample { + if fifo.size() < output_frame_size { break; } // Write frame as much as possible. - while fifo.size() >= output_nb_sample { + while fifo.size() >= output_frame_size { load_encode_and_write(&mut fifo, &mut output_format_context, &mut encode_context)?; } } @@ -245,7 +267,8 @@ fn transcode_aac(input_file: &CStr, output_file: &CStr) -> Result<()> { // Flush encode context encode_audio_frame(None, &mut output_format_context, &mut encode_context)?; - output_format_context.write_trailer().unwrap(); + output_format_context.write_trailer()?; + Ok(()) }