swr_convert的第三个参数,不能传输出的frame_size,因为mp3转aac,1152-》1024,采样数会溢出,导致fifo并不是满的。数据会丢。所以换成了传input frame的nb_samples,这样,不论是1152-》1024还是1024-》1152,都可以保证数据不会丢失
#include
#include
/*
音频转码器mp3->aac
*/
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavutil/audio_fifo.h"
#include "libavformat/avformat.h"
#include "libavutil/avstring.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include
#include
#include
};
#define OUTPUT_CHANNELS 2
#define OUTPUT_BIT_RATE 96000
int64_t pts = 0;
int framecnt = 0;
const char *get_error_text(const int error)
{
static char error_buffer[255];
av_strerror(error, error_buffer, sizeof(error_buffer));
return error_buffer;
}
void init_packet(AVPacket *packet){
av_init_packet(packet);
packet->data = NULL;
packet->size = 0;
}
int open_input_file(const char *filename, AVFormatContext **input_format_context, AVCodecContext **input_codec_context){
AVCodec *input_codec;
int error;
/** 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",
filename, get_error_text(error));
*input_format_context = NULL;
return error;
}
/** 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));
avformat_close_input(input_format_context);
return error;
}
/** 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);
avformat_close_input(input_format_context);
return AVERROR_EXIT;
}
/** Find a decoder for the audio stream. */
if (!(input_codec = avcodec_find_decoder((*input_format_context)->streams[0]->codec->codec_id))) {
fprintf(stderr, "Could not find input codec\n");
avformat_close_input(input_format_context);
return AVERROR_EXIT;
}
/** Open the decoder for the audio stream to use it later. */
if ((error = avcodec_open2((*input_format_context)->streams[0]->codec,
input_codec, NULL)) < 0) {
fprintf(stderr, "Could not open input codec (error '%s')\n",
get_error_text(error));
avformat_close_input(input_format_context);
return error;
}
/** Save the decoder context for easier access later. */
*input_codec_context = (*input_format_context)->streams[0]->codec;
return 0;
}
int open_output_file(const char *filename,
AVCodecContext *input_codec_context,
AVFormatContext **output_format_context,
AVCodecContext **output_codec_context)
{
AVIOContext *output_io_context = NULL;
AVStream *stream = NULL;
AVCodec *output_codec = NULL;
int error;
/** 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",
filename, get_error_text(error));
return error;
}
/** 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. */
(*output_format_context)->pb = output_io_context;
/** 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");
goto cleanup;
}
av_strlcpy((*output_format_context)->filename, filename,
sizeof((*output_format_context)->filename));
/** 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. */
if (!(stream = avformat_new_stream(*output_format_context, output_codec))) {
fprintf(stderr, "Could not create new stream\n");
error = AVERROR(ENOMEM);
goto cleanup;
}
*output_codec_context = stream->codec;
/**
* Set the basic encoder parameters.
* The input file's sample rate is used to avoid a sample rate conversion.
*/
(*output_codec_context)->codec_id = (*output_format_context)->oformat->audio_codec;
(*output_codec_context)->codec_type = AVMEDIA_TYPE_AUDIO;
(*output_codec_context)->channels = OUTPUT_CHANNELS;
(*output_codec_context)->channel_layout = av_get_default_channel_layout(OUTPUT_CHANNELS);
//samples per second
(*output_codec_context)->sample_rate = input_codec_context->sample_rate;
(*output_codec_context)->sample_fmt = output_codec->sample_fmts[0];
(*output_codec_context)->bit_rate = OUTPUT_BIT_RATE;
/** Allow the use of the experimental AAC encoder */
(*output_codec_context)->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
/** 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.
*/
if ((*output_format_context)->oformat->flags & AVFMT_GLOBALHEADER)
(*output_codec_context)->flags |= CODEC_FLAG_GLOBAL_HEADER;
/** Open the encoder for the audio stream to use it later. */
if ((error = avcodec_open2(*output_codec_context, output_codec, NULL)) < 0) {
fprintf(stderr, "Could not open output codec (error '%s')\n",
get_error_text(error));
goto cleanup;
}
return 0;
cleanup:
avio_closep(&(*output_format_context)->pb);
avformat_free_context(*output_format_context);
*output_format_context = NULL;
return error < 0 ? error : AVERROR_EXIT;
}
int init_resampler(AVCodecContext *input_codec_context,
AVCodecContext *output_codec_context,
SwrContext **resample_context){
int error;
*resample_context = swr_alloc_set_opts(NULL,
av_get_default_channel_layout(output_codec_context->channels), output_codec_context->sample_fmt, output_codec_context->sample_rate,
av_get_default_channel_layout(input_codec_context->channels), input_codec_context->sample_fmt, input_codec_context->sample_rate,
0, NULL);
if (!*resample_context) {
fprintf(stderr, "Could not allocate resample context\n");
return AVERROR(ENOMEM);
}
if ((error = swr_init(*resample_context)) < 0) {
fprintf(stderr, "Could not open resample context\n");
swr_free(resample_context);
return error;
}
return 0;
}
int encode_audio_frame(AVFrame *frame,int nbsamples,
AVFormatContext *output_format_context,
AVCodecContext *output_codec_context){
int got_packet;
AVPacket enc_pkt;
init_packet(&enc_pkt);
int ret = avcodec_encode_audio2(output_codec_context, &enc_pkt,
frame, &got_packet);
printf("ret:%d\t got_packet:%d\n", ret, got_packet);
if (ret < 0 || !got_packet){
av_free_packet(&enc_pkt);
av_frame_free(&frame);
return 1;
}
/** Set a timestamp based on the sample rate for the container. */
if (enc_pkt.pts == AV_NOPTS_VALUE){
pts += nbsamples;
enc_pkt.pts = pts;
}
ret = av_interleaved_write_frame(output_format_context, &enc_pkt);
av_free_packet(&enc_pkt);
av_frame_free(&frame);
if (ret < 0){
return 1;
}else{
framecnt++;
}
return 0;
}
int main(int argc, char* argv[]){
int ret = AVERROR_EXIT;
const char* input_file = "skycity1.mp3";
const char* ouput_file = "skycity1_out.aac";
av_register_all();
AVFormatContext *input_format_context = NULL, *output_format_context = NULL;
AVCodecContext *input_codec_context = NULL, *output_codec_context = NULL;
SwrContext *resample_context = NULL;
AVAudioFifo* audiofifo = NULL;
/** Open the input file for reading. */
if (open_input_file(input_file, &input_format_context, &input_codec_context))
goto cleanup;
/** Open the output file for writing. */
if (open_output_file(ouput_file, input_codec_context,
&output_format_context, &output_codec_context))
goto cleanup;
/** Initialize the resampler to be able to convert audio sample formats. */
if (init_resampler(input_codec_context, output_codec_context, &resample_context))
goto cleanup;
//Write Header
avformat_write_header(output_format_context, NULL);
if (avformat_write_header(output_format_context, NULL) < 0) {
printf("Could not write output file header (error '%s')\n");
goto cleanup;
}
int out_framesize = output_codec_context->frame_size;
AVSampleFormat out_sample_fmt = output_codec_context->sample_fmt;
int out_channels = av_get_channel_layout_nb_channels(output_codec_context->channel_layout);
audiofifo = av_audio_fifo_alloc(out_sample_fmt, out_channels, 1);
int readFinished = 0;
while (1){
while (av_audio_fifo_size(audiofifo) < out_framesize){
AVPacket input_packet;
init_packet(&input_packet);
if (!av_read_frame(input_format_context, &input_packet)){
AVFrame *input_frame = av_frame_alloc();
uint8_t ** audio_data_buffer = NULL;
int got_frame = 0;
avcodec_decode_audio4(input_codec_context, input_frame, &got_frame, &input_packet);
if (got_frame){
av_samples_alloc_array_and_samples(&audio_data_buffer, NULL, out_channels, input_frame->nb_samples, out_sample_fmt, 1);
//这里的out nb_samples 必须传输入采样数
int convert_nb_samples = swr_convert(resample_context, audio_data_buffer, input_frame->nb_samples,
(const uint8_t**)input_frame->data, input_frame->nb_samples);
av_audio_fifo_realloc(audiofifo, av_audio_fifo_size(audiofifo) + input_frame->nb_samples);
av_audio_fifo_write(audiofifo, (void **)audio_data_buffer, input_frame->nb_samples);
}
av_free_packet(&input_packet);
av_frame_free(&input_frame);
if (audio_data_buffer) {
av_free(audio_data_buffer[0]);
av_free(audio_data_buffer);
}
}else{
readFinished = 1;
av_free_packet(&input_packet);
break;
}
}
while (av_audio_fifo_size(audiofifo) >= out_framesize || (readFinished&&av_audio_fifo_size(audiofifo)>0)){
int frame_size = FFMIN(av_audio_fifo_size(audiofifo), out_framesize);
AVFrame* output_frame = NULL;
output_frame = av_frame_alloc();
output_frame->nb_samples = frame_size;
output_frame->channel_layout = output_codec_context->channel_layout;
output_frame->format = output_codec_context->sample_fmt;
output_frame->sample_rate = output_codec_context->sample_rate;
av_frame_get_buffer(output_frame, 0);
av_audio_fifo_read(audiofifo, (void **)output_frame->data, frame_size);
encode_audio_frame(output_frame, frame_size, output_format_context, output_codec_context);
}
if (readFinished){
if (output_codec_context->codec->capabilities &CODEC_CAP_DELAY){
while (!encode_audio_frame(NULL, out_framesize, output_format_context, output_codec_context)){ ; }
}
break;
}
}
if (av_write_trailer(output_format_context) < 0) {
printf("Could not write output file trailer (error '%s')\n");
goto cleanup;
}
printf("framecnt:%d\n", framecnt);
ret = 0;
cleanup:
if (audiofifo)
av_audio_fifo_free(audiofifo);
swr_free(&resample_context);
if (output_codec_context)
avcodec_close(output_codec_context);
if (output_format_context) {
avio_closep(&output_format_context->pb);
avformat_free_context(output_format_context);
}
if (input_codec_context)
avcodec_close(input_codec_context);
if (input_format_context)
avformat_close_input(&input_format_context);
return ret;
}