mp3转aac,用AVAudioFifo缓冲pcm数据

swr_convert的第三个参数,不能传输出的frame_size,因为mp3转aac,1152-》1024,采样数会溢出,导致fifo并不是满的。数据会丢。所以换成了传input frame的nb_samples,这样,不论是1152-》1024还是1024-》1152,都可以保证数据不会丢失

#include <stdio.h>
#include <windows.h>
/*
音频转码器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 <libavutil/opt.h>
#include <libavutil/channel_layout.h>
#include <libavutil/samplefmt.h>


};


#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;

}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值