@ -1,4 +1,6 @@
/*
* Copyright ( c ) 2013 - 2017 Andreas Unterweger
*
* This file is part of Libav .
*
* Libav is free software ; you can redistribute it and / or
@ -8,7 +10,7 @@
*
* Libav is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
@ -18,10 +20,11 @@
/**
* @ file
* s imple audio converter
* S imple audio converter
*
* @ example transcode_aac . c
* Convert an input audio file to AAC in an MP4 container using Libav .
* Formats other than MP4 are supported based on the output file extension .
* @ author Andreas Unterweger ( dustsigns @ gmail . com )
*/
@ -39,9 +42,9 @@
# include "libavresample/avresample.h"
/** The output bit rate in k bit/s */
/* The output bit rate in bit/s */
# define OUTPUT_BIT_RATE 96000
/** The number of output channels */
/* The number of output channels */
# define OUTPUT_CHANNELS 2
/**
@ -56,7 +59,13 @@ static char *get_error_text(const int error)
return error_buffer ;
}
/** Open an input file and the required decoder. */
/**
* Open an input file and the required decoder .
* @ param filename File to be opened
* @ param [ out ] input_format_context Format context of opened file
* @ param [ out ] input_codec_context Codec context of opened file
* @ return Error code ( 0 if successful )
*/
static int open_input_file ( const char * filename ,
AVFormatContext * * input_format_context ,
AVCodecContext * * input_codec_context )
@ -65,7 +74,7 @@ static int open_input_file(const char *filename,
AVCodec * input_codec ;
int error ;
/** Open the input file to read from it. */
/* Open the input file to read from it. */
if ( ( error = avformat_open_input ( input_format_context , filename , NULL ,
NULL ) ) < 0 ) {
fprintf ( stderr , " Could not open input file '%s' (error '%s') \n " ,
@ -74,7 +83,7 @@ static int open_input_file(const char *filename,
return error ;
}
/** Get information on the input file (number of streams etc.). */
/* Get information on the input file (number of streams etc.). */
if ( ( error = avformat_find_stream_info ( * input_format_context , NULL ) ) < 0 ) {
fprintf ( stderr , " Could not open find stream info (error '%s') \n " ,
get_error_text ( error ) ) ;
@ -82,7 +91,7 @@ static int open_input_file(const char *filename,
return error ;
}
/** Make sure that there is only one stream in the input file. */
/* Make sure that there is only one stream in the input file. */
if ( ( * input_format_context ) - > nb_streams ! = 1 ) {
fprintf ( stderr , " Expected one audio input stream, but found %d \n " ,
( * input_format_context ) - > nb_streams ) ;
@ -90,14 +99,14 @@ static int open_input_file(const char *filename,
return AVERROR_EXIT ;
}
/** Find a decoder for the audio stream. */
/* Find a decoder for the audio stream. */
if ( ! ( input_codec = avcodec_find_decoder ( ( * input_format_context ) - > streams [ 0 ] - > codecpar - > codec_id ) ) ) {
fprintf ( stderr , " Could not find input codec \n " ) ;
avformat_close_input ( input_format_context ) ;
return AVERROR_EXIT ;
}
/** allocate a new decoding context */
/* Allocate a new decoding context. */
avctx = avcodec_alloc_context3 ( input_codec ) ;
if ( ! avctx ) {
fprintf ( stderr , " Could not allocate a decoding context \n " ) ;
@ -105,7 +114,7 @@ static int open_input_file(const char *filename,
return AVERROR ( ENOMEM ) ;
}
/** i nitialize the stream parameters with demuxer information */
/* I nitialize the stream parameters with demuxer information. */
error = avcodec_parameters_to_context ( avctx , ( * input_format_context ) - > streams [ 0 ] - > codecpar ) ;
if ( error < 0 ) {
avformat_close_input ( input_format_context ) ;
@ -113,7 +122,7 @@ static int open_input_file(const char *filename,
return error ;
}
/** Open the decoder for the audio stream to use it later. */
/* Open the decoder for the audio stream to use it later. */
if ( ( error = avcodec_open2 ( avctx , input_codec , NULL ) ) < 0 ) {
fprintf ( stderr , " Could not open input codec (error '%s') \n " ,
get_error_text ( error ) ) ;
@ -122,7 +131,7 @@ static int open_input_file(const char *filename,
return error ;
}
/** Save the decoder context for easier access later. */
/* Save the decoder context for easier access later. */
* input_codec_context = avctx ;
return 0 ;
@ -132,6 +141,11 @@ static int open_input_file(const char *filename,
* Open an output file and the required encoder .
* Also set some basic encoder parameters .
* Some of these parameters are based on the input file ' s parameters .
* @ param filename File to be opened
* @ param input_codec_context Codec context of input file
* @ param [ out ] output_format_context Format context of output file
* @ param [ out ] output_codec_context Codec context of output file
* @ return Error code ( 0 if successful )
*/
static int open_output_file ( const char * filename ,
AVCodecContext * input_codec_context ,
@ -144,7 +158,7 @@ static int open_output_file(const char *filename,
AVCodec * output_codec = NULL ;
int error ;
/** Open the output file to write to it. */
/* Open the output file to write to it. */
if ( ( error = avio_open ( & output_io_context , filename ,
AVIO_FLAG_WRITE ) ) < 0 ) {
fprintf ( stderr , " Could not open output file '%s' (error '%s') \n " ,
@ -152,16 +166,16 @@ static int open_output_file(const char *filename,
return error ;
}
/** Create a new format context for the output container format. */
/* Create a new format context for the output container format. */
if ( ! ( * output_format_context = avformat_alloc_context ( ) ) ) {
fprintf ( stderr , " Could not allocate output format context \n " ) ;
return AVERROR ( ENOMEM ) ;
}
/** Associate the output file (pointer) with the container format context. */
/* Associate the output file (pointer) with the container format context. */
( * output_format_context ) - > pb = output_io_context ;
/** Guess the desired container format based on the file extension. */
/* Guess the desired container format based on the file extension. */
if ( ! ( ( * output_format_context ) - > oformat = av_guess_format ( NULL , filename ,
NULL ) ) ) {
fprintf ( stderr , " Could not find output file format \n " ) ;
@ -171,13 +185,13 @@ static int open_output_file(const char *filename,
av_strlcpy ( ( * output_format_context ) - > filename , filename ,
sizeof ( ( * output_format_context ) - > filename ) ) ;
/** Find the encoder to be used by its name. */
/* Find the encoder to be used by its name. */
if ( ! ( output_codec = avcodec_find_encoder ( AV_CODEC_ID_AAC ) ) ) {
fprintf ( stderr , " Could not find an AAC encoder. \n " ) ;
goto cleanup ;
}
/** Create a new audio stream in the output file container. */
/* Create a new audio stream in the output file container. */
if ( ! ( stream = avformat_new_stream ( * output_format_context , NULL ) ) ) {
fprintf ( stderr , " Could not create new stream \n " ) ;
error = AVERROR ( ENOMEM ) ;
@ -191,31 +205,27 @@ static int open_output_file(const char *filename,
goto cleanup ;
}
/**
* Set the basic encoder parameters .
* The input file ' s sample rate is used to avoid a sample rate conversion .
*/
/* Set the basic encoder parameters.
* The input file ' s sample rate is used to avoid a sample rate conversion . */
avctx - > channels = OUTPUT_CHANNELS ;
avctx - > channel_layout = av_get_default_channel_layout ( OUTPUT_CHANNELS ) ;
avctx - > sample_rate = input_codec_context - > sample_rate ;
avctx - > sample_fmt = output_codec - > sample_fmts [ 0 ] ;
avctx - > bit_rate = OUTPUT_BIT_RATE ;
/** Allow the use of the experimental AAC encoder */
/* Allow the use of the experimental AAC encoder. */
avctx - > strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL ;
/** Set the sample rate for the container. */
/* Set the sample rate for the container. */
stream - > time_base . den = input_codec_context - > sample_rate ;
stream - > time_base . num = 1 ;
/**
* Some container formats ( like MP4 ) require global headers to be present
* Mark the encoder so that it behaves accordingly .
*/
/* Some container formats (like MP4) require global headers to be present.
* Mark the encoder so that it behaves accordingly . */
if ( ( * output_format_context ) - > oformat - > flags & AVFMT_GLOBALHEADER )
avctx - > flags | = AV_CODEC_FLAG_GLOBAL_HEADER ;
/** Open the encoder for the audio stream to use it later. */
/* Open the encoder for the audio stream to use it later. */
if ( ( error = avcodec_open2 ( avctx , output_codec , NULL ) ) < 0 ) {
fprintf ( stderr , " Could not open output codec (error '%s') \n " ,
get_error_text ( error ) ) ;
@ -228,7 +238,7 @@ static int open_output_file(const char *filename,
goto cleanup ;
}
/** Save the encoder context for easier access later. */
/* Save the encoder context for easier access later. */
* output_codec_context = avctx ;
return 0 ;
@ -241,16 +251,23 @@ cleanup:
return error < 0 ? error : AVERROR_EXIT ;
}
/** Initialize one data packet for reading or writing. */
/**
* Initialize one data packet for reading or writing .
* @ param packet Packet to be initialized
*/
static void init_packet ( AVPacket * packet )
{
av_init_packet ( packet ) ;
/** Set the packet data and size so that it is recognized as being empty. */
/* Set the packet data and size so that it is recognized as being empty. */
packet - > data = NULL ;
packet - > size = 0 ;
}
/** Initialize one audio frame for reading from the input file */
/**
* Initialize one audio frame for reading from the input file .
* @ param [ out ] frame Frame to be initialized
* @ return Error code ( 0 if successful )
*/
static int init_input_frame ( AVFrame * * frame )
{
if ( ! ( * frame = av_frame_alloc ( ) ) ) {
@ -264,27 +281,28 @@ static int init_input_frame(AVFrame **frame)
* Initialize the audio resampler based on the input and output codec settings .
* If the input and output sample formats differ , a conversion is required
* libavresample takes care of this , but requires initialization .
* @ param input_codec_context Codec context of the input file
* @ param output_codec_context Codec context of the output file
* @ param [ out ] resample_context Resample context for the required conversion
* @ return Error code ( 0 if successful )
*/
static int init_resampler ( AVCodecContext * input_codec_context ,
AVCodecContext * output_codec_context ,
AVAudioResampleContext * * resample_context )
{
/**
* Only initialize the resampler if it is necessary , i . e . ,
* if and only if the sample formats differ .
*/
/* Only initialize the resampler if it is necessary, i.e.,
* if and only if the sample formats differ . */
if ( input_codec_context - > sample_fmt ! = output_codec_context - > sample_fmt | |
input_codec_context - > channels ! = output_codec_context - > channels ) {
int error ;
/** Create a resampler context for the conversion. */
/* Create a resampler context for the conversion. */
if ( ! ( * resample_context = avresample_alloc_context ( ) ) ) {
fprintf ( stderr , " Could not allocate resample context \n " ) ;
return AVERROR ( ENOMEM ) ;
}
/**
* Set the conversion parameters .
/* Set the conversion parameters.
* Default channel layouts based on the number of channels
* are assumed for simplicity ( they are sometimes not detected
* properly by the demuxer and / or decoder ) .
@ -302,7 +320,7 @@ static int init_resampler(AVCodecContext *input_codec_context,
av_opt_set_int ( * resample_context , " out_sample_fmt " ,
output_codec_context - > sample_fmt , 0 ) ;
/** Open the resampler with the specified parameters. */
/* Open the resampler with the specified parameters. */
if ( ( error = avresample_open ( * resample_context ) ) < 0 ) {
fprintf ( stderr , " Could not open resample context \n " ) ;
avresample_free ( resample_context ) ;
@ -312,10 +330,15 @@ static int init_resampler(AVCodecContext *input_codec_context,
return 0 ;
}
/** Initialize a FIFO buffer for the audio samples to be encoded. */
/**
* Initialize a FIFO buffer for the audio samples to be encoded .
* @ param [ out ] fifo Sample buffer
* @ param output_codec_context Codec context of the output file
* @ return Error code ( 0 if successful )
*/
static int init_fifo ( AVAudioFifo * * fifo , AVCodecContext * output_codec_context )
{
/** Create the FIFO buffer based on the specified output sample format. */
/* Create the FIFO buffer based on the specified output sample format. */
if ( ! ( * fifo = av_audio_fifo_alloc ( output_codec_context - > sample_fmt ,
output_codec_context - > channels , 1 ) ) ) {
fprintf ( stderr , " Could not allocate FIFO \n " ) ;
@ -324,7 +347,11 @@ static int init_fifo(AVAudioFifo **fifo, AVCodecContext *output_codec_context)
return 0 ;
}
/** Write the header of the output file container. */
/**
* Write the header of the output file container .
* @ param output_format_context Format context of the output file
* @ return Error code ( 0 if successful )
*/
static int write_output_file_header ( AVFormatContext * output_format_context )
{
int error ;
@ -336,20 +363,32 @@ static int write_output_file_header(AVFormatContext *output_format_context)
return 0 ;
}
/** Decode one audio frame from the input file. */
/**
* Decode one audio frame from the input file .
* @ param frame Audio frame to be decoded
* @ param input_format_context Format context of the input file
* @ param input_codec_context Codec context of the input file
* @ param [ out ] data_present Indicates whether data has been decoded
* @ param [ out ] finished Indicates whether the end of file has
* been reached and all data has been
* decoded . If this flag is false , there
* is more data to be decoded , i . e . , this
* function has to be called again .
* @ return Error code ( 0 if successful )
*/
static int decode_audio_frame ( AVFrame * frame ,
AVFormatContext * input_format_context ,
AVCodecContext * input_codec_context ,
int * data_present , int * finished )
{
/** Packet used for temporary storage. */
/* Packet used for temporary storage. */
AVPacket input_packet ;
int error ;
init_packet ( & input_packet ) ;
/** Read one audio frame from the input file into a temporary packet. */
/* Read one audio frame from the input file into a temporary packet. */
if ( ( error = av_read_frame ( input_format_context , & input_packet ) ) < 0 ) {
/** If we are the the end of the file, flush the decoder below. */
/* If we are the the end of the file, flush the decoder below. */
if ( error = = AVERROR_EOF )
* finished = 1 ;
else {
@ -359,12 +398,10 @@ static int decode_audio_frame(AVFrame *frame,
}
}
/**
* Decode the audio frame stored in the temporary packet .
/* Decode the audio frame stored in the temporary packet.
* The input audio stream decoder is used to do this .
* If we are at the end of the file , pass an empty packet to the decoder
* to flush it .
*/
* to flush it . */
if ( ( error = avcodec_decode_audio4 ( input_codec_context , frame ,
data_present , & input_packet ) ) < 0 ) {
fprintf ( stderr , " Could not decode frame (error '%s') \n " ,
@ -373,10 +410,8 @@ static int decode_audio_frame(AVFrame *frame,
return error ;
}
/**
* If the decoder has not been flushed completely , we are not finished ,
* so that this function has to be called again .
*/
/* If the decoder has not been flushed completely, we are not finished,
* so that this function has to be called again . */
if ( * finished & & * data_present )
* finished = 0 ;
av_packet_unref ( & input_packet ) ;
@ -387,6 +422,13 @@ static int decode_audio_frame(AVFrame *frame,
* Initialize a temporary storage for the specified number of audio samples .
* The conversion requires temporary storage due to the different format .
* The number of audio samples to be allocated is specified in frame_size .
* @ param [ out ] converted_input_samples Array of converted samples . The
* dimensions are reference , channel
* ( for multi - channel audio ) , sample .
* @ param output_codec_context Codec context of the output file
* @ param frame_size Number of samples to be converted in
* each round
* @ return Error code ( 0 if successful )
*/
static int init_converted_samples ( uint8_t * * * converted_input_samples ,
AVCodecContext * output_codec_context ,
@ -394,8 +436,7 @@ static int init_converted_samples(uint8_t ***converted_input_samples,
{
int error ;
/**
* Allocate as many pointers as there are audio channels .
/* Allocate as many pointers as there are audio channels.
* Each pointer will later point to the audio samples of the corresponding
* channels ( although it may be NULL for interleaved formats ) .
*/
@ -405,10 +446,8 @@ static int init_converted_samples(uint8_t ***converted_input_samples,
return AVERROR ( ENOMEM ) ;
}
/**
* Allocate memory for the samples of all channels in one consecutive
* block for convenience .
*/
/* Allocate memory for the samples of all channels in one consecutive
* block for convenience . */
if ( ( error = av_samples_alloc ( * converted_input_samples , NULL ,
output_codec_context - > channels ,
frame_size ,
@ -425,8 +464,15 @@ static int init_converted_samples(uint8_t ***converted_input_samples,
/**
* Convert the input audio samples into the output sample format .
* The conversion happens on a per - frame basis , the size of which is specified
* by frame_size .
* The conversion happens on a per - frame basis , the size of which is
* specified by frame_size .
* @ param input_data Samples to be decoded . The dimensions are
* channel ( for multi - channel audio ) , sample .
* @ param [ out ] converted_data Converted samples . The dimensions are channel
* ( for multi - channel audio ) , sample .
* @ param frame_size Number of samples to be converted
* @ param resample_context Resample context for the conversion
* @ return Error code ( 0 if successful )
*/
static int convert_samples ( uint8_t * * input_data ,
uint8_t * * converted_data , const int frame_size ,
@ -434,7 +480,7 @@ static int convert_samples(uint8_t **input_data,
{
int error ;
/** Convert the samples using the resampler. */
/* Convert the samples using the resampler. */
if ( ( error = avresample_convert ( resample_context , converted_data , 0 ,
frame_size , input_data , 0 , frame_size ) ) < 0 ) {
fprintf ( stderr , " Could not convert input samples (error '%s') \n " ,
@ -442,11 +488,9 @@ static int convert_samples(uint8_t **input_data,
return error ;
}
/**
* Perform a sanity check so that the number of converted samples is
/* Perform a sanity check so that the number of converted samples is
* not greater than the number of samples to be converted .
* If the sample rates differ , this case has to be handled differently
*/
* If the sample rates differ , this case has to be handled differently . */
if ( avresample_available ( resample_context ) ) {
fprintf ( stderr , " Converted samples left over \n " ) ;
return AVERROR_EXIT ;
@ -455,23 +499,28 @@ static int convert_samples(uint8_t **input_data,
return 0 ;
}
/** Add converted input audio samples to the FIFO buffer for later processing. */
/**
* Add converted input audio samples to the FIFO buffer for later processing .
* @ param fifo Buffer to add the samples to
* @ param converted_input_samples Samples to be added . The dimensions are channel
* ( for multi - channel audio ) , sample .
* @ param frame_size Number of samples to be converted
* @ return Error code ( 0 if successful )
*/
static int add_samples_to_fifo ( AVAudioFifo * fifo ,
uint8_t * * converted_input_samples ,
const int frame_size )
{
int error ;
/**
* Make the FIFO as large as it needs to be to hold both ,
* the old and the new samples .
*/
/* Make the FIFO as large as it needs to be to hold both,
* the old and the new samples . */
if ( ( error = av_audio_fifo_realloc ( fifo , av_audio_fifo_size ( fifo ) + frame_size ) ) < 0 ) {
fprintf ( stderr , " Could not reallocate FIFO \n " ) ;
return error ;
}
/** Store the new samples in the FIFO buffer. */
/* Store the new samples in the FIFO buffer. */
if ( av_audio_fifo_write ( fifo , ( void * * ) converted_input_samples ,
frame_size ) < frame_size ) {
fprintf ( stderr , " Could not write data to FIFO \n " ) ;
@ -481,55 +530,63 @@ static int add_samples_to_fifo(AVAudioFifo *fifo,
}
/**
* Read one audio frame from the input file , decodes , converts and stores
* Read one audio frame from the input file , decode , convert and store
* it in the FIFO buffer .
* @ param fifo Buffer used for temporary storage
* @ param input_format_context Format context of the input file
* @ param input_codec_context Codec context of the input file
* @ param output_codec_context Codec context of the output file
* @ param resample_context Resample context for the conversion
* @ param [ out ] finished Indicates whether the end of file has
* been reached and all data has been
* decoded . If this flag is false ,
* there is more data to be decoded ,
* i . e . , this function has to be called
* again .
* @ return Error code ( 0 if successful )
*/
static int read_decode_convert_and_store ( AVAudioFifo * fifo ,
AVFormatContext * input_format_context ,
AVCodecContext * input_codec_context ,
AVCodecContext * output_codec_context ,
AVAudioResampleContext * resampler_context ,
AVAudioResampleContext * resample_context ,
int * finished )
{
/** Temporary storage of the input samples of the frame read from the file. */
/* Temporary storage of the input samples of the frame read from the file. */
AVFrame * input_frame = NULL ;
/** Temporary storage for the converted input samples. */
/* Temporary storage for the converted input samples. */
uint8_t * * converted_input_samples = NULL ;
int data_present ;
int ret = AVERROR_EXIT ;
/** Initialize temporary storage for one input frame. */
/* Initialize temporary storage for one input frame. */
if ( init_input_frame ( & input_frame ) )
goto cleanup ;
/** Decode one frame worth of audio samples. */
/* Decode one frame worth of audio samples. */
if ( decode_audio_frame ( input_frame , input_format_context ,
input_codec_context , & data_present , finished ) )
goto cleanup ;
/**
* If we are at the end of the file and there are no more samples
/* If we are at the end of the file and there are no more samples
* in the decoder which are delayed , we are actually finished .
* This must not be treated as an error .
*/
* This must not be treated as an error . */
if ( * finished & & ! data_present ) {
ret = 0 ;
goto cleanup ;
}
/** If there is decoded data, convert and store it */
/* If there is decoded data, convert and store it. */
if ( data_present ) {
/** Initialize the temporary storage for the converted input samples. */
/* Initialize the temporary storage for the converted input samples. */
if ( init_converted_samples ( & converted_input_samples , output_codec_context ,
input_frame - > nb_samples ) )
goto cleanup ;
/**
* Convert the input samples to the desired output sample format .
* This requires a temporary storage provided by converted_input_samples .
*/
/* Convert the input samples to the desired output sample format.
* This requires a temporary storage provided by converted_input_samples . */
if ( convert_samples ( input_frame - > extended_data , converted_input_samples ,
input_frame - > nb_samples , resampler _context ) )
input_frame - > nb_samples , resample_context ) )
goto cleanup ;
/** Add the converted input samples to the FIFO buffer for later processing. */
/* Add the converted input samples to the FIFO buffer for later processing. */
if ( add_samples_to_fifo ( fifo , converted_input_samples ,
input_frame - > nb_samples ) )
goto cleanup ;
@ -550,6 +607,10 @@ cleanup:
/**
* Initialize one input frame for writing to the output file .
* The frame will be exactly frame_size samples large .
* @ param [ out ] frame Frame to be initialized
* @ param output_codec_context Codec context of the output file
* @ param frame_size Size of the frame
* @ return Error code ( 0 if successful )
*/
static int init_output_frame ( AVFrame * * frame ,
AVCodecContext * output_codec_context ,
@ -557,28 +618,24 @@ static int init_output_frame(AVFrame **frame,
{
int error ;
/** Create a new frame to store the audio samples. */
/* Create a new frame to store the audio samples. */
if ( ! ( * frame = av_frame_alloc ( ) ) ) {
fprintf ( stderr , " Could not allocate output frame \n " ) ;
return AVERROR_EXIT ;
}
/**
* Set the frame ' s parameters , especially its size and format .
/* Set the frame's parameters, especially its size and format.
* av_frame_get_buffer needs this to allocate memory for the
* audio samples of the frame .
* Default channel layouts based on the number of channels
* are assumed for simplicity .
*/
* are assumed for simplicity . */
( * frame ) - > nb_samples = frame_size ;
( * frame ) - > channel_layout = output_codec_context - > channel_layout ;
( * frame ) - > format = output_codec_context - > sample_fmt ;
( * frame ) - > sample_rate = output_codec_context - > sample_rate ;
/**
* Allocate the samples of the created frame . This call will make
* sure that the audio frame can hold as many samples as specified .
*/
/* Allocate the samples of the created frame. This call will make
* sure that the audio frame can hold as many samples as specified . */
if ( ( error = av_frame_get_buffer ( * frame , 0 ) ) < 0 ) {
fprintf ( stderr , " Could not allocate output frame samples (error '%s') \n " ,
get_error_text ( error ) ) ;
@ -589,30 +646,36 @@ static int init_output_frame(AVFrame **frame,
return 0 ;
}
/** Global timestamp for the audio frames */
/* Global timestamp for the audio frames. */
static int64_t pts = 0 ;
/** Encode one frame worth of audio to the output file. */
/**
* Encode one frame worth of audio to the output file .
* @ param frame Samples to be encoded
* @ param output_format_context Format context of the output file
* @ param output_codec_context Codec context of the output file
* @ param [ out ] data_present Indicates whether data has been
* decoded
* @ return Error code ( 0 if successful )
*/
static int encode_audio_frame ( AVFrame * frame ,
AVFormatContext * output_format_context ,
AVCodecContext * output_codec_context ,
int * data_present )
{
/** Packet used for temporary storage. */
/* Packet used for temporary storage. */
AVPacket output_packet ;
int error ;
init_packet ( & output_packet ) ;
/** Set a timestamp based on the sample rate for the container. */
/* Set a timestamp based on the sample rate for the container. */
if ( frame ) {
frame - > pts = pts ;
pts + = frame - > nb_samples ;
}
/**
* Encode the audio frame and store it in the temporary packet .
* The output audio stream encoder is used to do this .
*/
/* Encode the audio frame and store it in the temporary packet.
* The output audio stream encoder is used to do this . */
if ( ( error = avcodec_encode_audio2 ( output_codec_context , & output_packet ,
frame , data_present ) ) < 0 ) {
fprintf ( stderr , " Could not encode frame (error '%s') \n " ,
@ -621,7 +684,7 @@ static int encode_audio_frame(AVFrame *frame,
return error ;
}
/** Write one audio frame from the temporary packet to the output file. */
/* Write one audio frame from the temporary packet to the output file. */
if ( * data_present ) {
if ( ( error = av_write_frame ( output_format_context , & output_packet ) ) < 0 ) {
fprintf ( stderr , " Could not write frame (error '%s') \n " ,
@ -639,37 +702,37 @@ static int encode_audio_frame(AVFrame *frame,
/**
* Load one audio frame from the FIFO buffer , encode and write it to the
* output file .
* @ param fifo Buffer used for temporary storage
* @ param output_format_context Format context of the output file
* @ param output_codec_context Codec context of the output file
* @ return Error code ( 0 if successful )
*/
static int load_encode_and_write ( AVAudioFifo * fifo ,
AVFormatContext * output_format_context ,
AVCodecContext * output_codec_context )
{
/** Temporary storage of the output samples of the frame written to the file. */
/* Temporary storage of the output samples of the frame written to the file. */
AVFrame * output_frame ;
/**
* Use the maximum number of possible samples per frame .
/* Use the maximum number of possible samples per frame.
* If there is less than the maximum possible frame size in the FIFO
* buffer use this number . Otherwise , use the maximum possible frame size
*/
* buffer use this number . Otherwise , use the maximum possible frame size . */
const int frame_size = FFMIN ( av_audio_fifo_size ( fifo ) ,
output_codec_context - > frame_size ) ;
int data_written ;
/** Initialize temporary storage for one output frame. */
/* Initialize temporary storage for one output frame. */
if ( init_output_frame ( & output_frame , output_codec_context , frame_size ) )
return AVERROR_EXIT ;
/**
* Read as many samples from the FIFO buffer as required to fill the frame .
* The samples are stored in the frame temporarily .
*/
/* Read as many samples from the FIFO buffer as required to fill the frame.
* The samples are stored in the frame temporarily . */
if ( av_audio_fifo_read ( fifo , ( void * * ) output_frame - > data , frame_size ) < frame_size ) {
fprintf ( stderr , " Could not read data from FIFO \n " ) ;
av_frame_free ( & output_frame ) ;
return AVERROR_EXIT ;
}
/** Encode one frame worth of audio samples. */
/* Encode one frame worth of audio samples. */
if ( encode_audio_frame ( output_frame , output_format_context ,
output_codec_context , & data_written ) ) {
av_frame_free ( & output_frame ) ;
@ -679,7 +742,11 @@ static int load_encode_and_write(AVAudioFifo *fifo,
return 0 ;
}
/** Write the trailer of the output file container. */
/**
* Write the trailer of the output file container .
* @ param output_format_context Format context of the output file
* @ return Error code ( 0 if successful )
*/
static int write_output_file_trailer ( AVFormatContext * output_format_context )
{
int error ;
@ -691,7 +758,6 @@ static int write_output_file_trailer(AVFormatContext *output_format_context)
return 0 ;
}
/** Convert an audio file to an AAC file in an MP4 container. */
int main ( int argc , char * * argv )
{
AVFormatContext * input_format_context = NULL , * output_format_context = NULL ;
@ -700,89 +766,75 @@ int main(int argc, char **argv)
AVAudioFifo * fifo = NULL ;
int ret = AVERROR_EXIT ;
if ( argc < 3 ) {
if ( argc ! = 3 ) {
fprintf ( stderr , " Usage: %s <input file> <output file> \n " , argv [ 0 ] ) ;
exit ( 1 ) ;
}
/** Register all codecs and formats so that they can be used. */
/* Register all codecs and formats so that they can be used. */
av_register_all ( ) ;
/** Open the input file for reading. */
/* Open the input file for reading. */
if ( open_input_file ( argv [ 1 ] , & input_format_context ,
& input_codec_context ) )
goto cleanup ;
/** Open the output file for writing. */
/* Open the output file for writing. */
if ( open_output_file ( argv [ 2 ] , input_codec_context ,
& output_format_context , & output_codec_context ) )
goto cleanup ;
/** Initialize the resampler to be able to convert audio sample formats. */
/* Initialize the resampler to be able to convert audio sample formats. */
if ( init_resampler ( input_codec_context , output_codec_context ,
& resample_context ) )
goto cleanup ;
/** Initialize the FIFO buffer to store audio samples to be encoded. */
/* Initialize the FIFO buffer to store audio samples to be encoded. */
if ( init_fifo ( & fifo , output_codec_context ) )
goto cleanup ;
/** Write the header of the output file container. */
/* Write the header of the output file container. */
if ( write_output_file_header ( output_format_context ) )
goto cleanup ;
/**
* Loop as long as we have input samples to read or output samples
* to write ; abort as soon as we have neither .
*/
/* Loop as long as we have input samples to read or output samples
* to write ; abort as soon as we have neither . */
while ( 1 ) {
/** Use the encoder's desired frame size for processing. */
/* Use the encoder's desired frame size for processing. */
const int output_frame_size = output_codec_context - > frame_size ;
int finished = 0 ;
/**
* Make sure that there is one frame worth of samples in the FIFO
/* Make sure that there is one frame worth of samples in the FIFO
* buffer so that the encoder can do its work .
* Since the decoder ' s and the encoder ' s frame size may differ , we
* need to FIFO buffer to store as many frames worth of input samples
* that they make up at least one frame worth of output samples .
*/
* that they make up at least one frame worth of output samples . */
while ( av_audio_fifo_size ( fifo ) < output_frame_size ) {
/**
* Decode one frame worth of audio samples , convert it to the
* output sample format and put it into the FIFO buffer .
*/
/* Decode one frame worth of audio samples, convert it to the
* output sample format and put it into the FIFO buffer . */
if ( read_decode_convert_and_store ( fifo , input_format_context ,
input_codec_context ,
output_codec_context ,
resample_context , & finished ) )
goto cleanup ;
/**
* If we are at the end of the input file , we continue
* encoding the remaining audio samples to the output file .
*/
/* If we are at the end of the input file, we continue
* encoding the remaining audio samples to the output file . */
if ( finished )
break ;
}
/**
* If we have enough samples for the encoder , we encode them .
/* If we have enough samples for the encoder, we encode them.
* At the end of the file , we pass the remaining samples to
* the encoder .
*/
* the encoder . */
while ( av_audio_fifo_size ( fifo ) > = output_frame_size | |
( finished & & av_audio_fifo_size ( fifo ) > 0 ) )
/**
* Take one frame worth of audio samples from the FIFO buffer ,
* encode it and write it to the output file .
*/
/* Take one frame worth of audio samples from the FIFO buffer,
* encode it and write it to the output file . */
if ( load_encode_and_write ( fifo , output_format_context ,
output_codec_context ) )
goto cleanup ;
/**
* If we are at the end of the input file and have encoded
* all remaining samples , we can exit this loop and finish .
*/
/* If we are at the end of the input file and have encoded
* all remaining samples , we can exit this loop and finish . */
if ( finished ) {
int data_written ;
/** Flush the encoder as it may have delayed frames. */
/* Flush the encoder as it may have delayed frames. */
do {
if ( encode_audio_frame ( NULL , output_format_context ,
output_codec_context , & data_written ) )
@ -792,7 +844,7 @@ int main(int argc, char **argv)
}
}
/** Write the trailer of the output file container. */
/* Write the trailer of the output file container. */
if ( write_output_file_trailer ( output_format_context ) )
goto cleanup ;
ret = 0 ;