利用ffmpeg中的libswresample模块对音频进行重采样

9 篇文章 0 订阅
6 篇文章 0 订阅

在工作中,需要对音频进行重采样,比如说采样率为44.1kHz变为48kHz,通过研究ffmpeg中提供的例子,自己写了一段测试代码,贴出来供大家参考(当然,重采样还有另外一种方法,就是用ffmpeg中的libavfilter模块,后面我也会把该方法的代码贴出来)。测试代码仅测试过AAC格式,其它格式需要稍加改动(主要在fwrite的那一段,AAC解码出来的format为FLTP类型,如果不是该格式,需要用其它正确的方式来写文件),然后后存储为PCM数据,通过goldWave工具可以正常播放。我使用的ffmpeg版本:2.8 release,下面是具体的代码:

#include <unistd.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfiltergraph.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include "libavutil/samplefmt.h"
#include "libswresample/swresample.h"

static int open_input_file(const char *filename,AVFormatContext **ifmt_ctx)
{
    int ret;
    unsigned int i;
    ret = avformat_open_input(ifmt_ctx, filename, NULL, NULL);
    if(ret < 0)
    {
        av_log(NULL,AV_LOG_ERROR,"Cannot open input file[%s]\n",filename);
        return -1;
    }

    ret = avformat_find_stream_info(*ifmt_ctx, NULL);
    if(ret < 0)
    {
        av_log(NULL,AV_LOG_ERROR,"Cannot find stream information \n");
        return -1;
    }

    for(i = 0; i < (*ifmt_ctx)->nb_streams; i++)
    {
        AVStream *stream;
        AVCodecContext *codec_ctx;
        stream = (*ifmt_ctx)->streams[i];
        codec_ctx = stream->codec;
        if(codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO || codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            ret = avcodec_open2(codec_ctx, avcodec_find_decoder(codec_ctx->codec_id), NULL);
            if(ret < 0 )
            {
                av_log(NULL,AV_LOG_ERROR,"Failed to open decoder for stream #%u\n",i);
                return ret;
            }
        }
    }
    return 0;
}

int init_resampler(AVCodecContext *input_codec_context,AVCodecContext  **output_codec_context,SwrContext **resample_context)
{
    int ret;
    *output_codec_context = avformat_alloc_context();
    if(ret < 0)
    {
        av_log(NULL,AV_LOG_QUIET,"Could not allocate output format context\n");
        return ret;
    }

    //init output codec context
    (*output_codec_context)->sample_fmt = AV_SAMPLE_FMT_FLTP;
    (*output_codec_context)->bit_rate = 128000;
    (*output_codec_context)->sample_rate= 64000;
    (*output_codec_context)->channels= 2;
    (*output_codec_context)->channel_layout= av_get_default_channel_layout((*output_codec_context)->channels);

    //init resample context
    *resample_context = swr_alloc_set_opts(NULL,
                        (*output_codec_context)->channel_layout,
                        (*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)
    {
        av_log(NULL,AV_LOG_QUIET,"Could not allocate resample context\n");
        return -1;
    }

    ret = swr_init(*resample_context);
    if(ret < 0)
    {
        av_log(NULL,AV_LOG_QUIET,"Could not open resample context\n");
        swr_free(resample_context);
        return ret;
    }

    return 0;
}

static int init_converted_samples(uint8_t ***converted_samples, AVCodecContext *output_codec_context, int frame_size)
{
    int error;
    *converted_samples = calloc(output_codec_context->channels, sizeof(**converted_samples));
    if(!(*converted_samples))
    {
        av_log(NULL,AV_LOG_QUIET,"Could not allocate coverter input sample opinters \n");
        return AVERROR(ENOMEM);
    }

    error = av_samples_alloc(*converted_samples, NULL,
                            output_codec_context->channels,
                            frame_size,
                            output_codec_context->sample_fmt, 0);
    if(error < 0)
    {
        av_log(NULL,AV_LOG_QUIET,"Could not allocate converted input samples\n");
        av_freep(&(*converted_samples)[0]);
        free(*converted_samples);
        return error;
    }
    return 0;
}

static int convert_samples(const uint8_t **input_data, uint8_t **converted_data, const int frame_size, SwrContext *resample_context)
{
    int ret;
    ret = swr_convert(resample_context, converted_data, frame_size, input_data, frame_size);
    return ret > 0 ? ret : 0;
}

int main(int argc, char **argv)
{
    av_register_all();
    avfilter_register_all();
    if(argc != 2)
    {
        av_log(NULL,AV_LOG_QUIET,"use it like: %s test.aac\n",argv[0]);
        return -1;
    }
    int ret, i, stream_index,got_frame;
    enum AVMediaType type;
    char *inputFileName = argv[1];
    AVCodecContext *input_codec_context = NULL;
    AVCodecContext *output_codec_context = NULL;
    SwrContext *resample_context = NULL;
    uint8_t **converted_samples = NULL;
    FILE *fp = fopen("/tmp/test.pcm","wb");
    if(fp == NULL)
    {
        av_log(NULL,AV_LOG_QUIET,"Open pcm file failed\n");
        return -1;
    }
    AVFormatContext *ifmt_ctx = NULL;
    AVPacket packet = {.data = NULL, .size = 0};
    AVFrame *frame = av_frame_alloc();
    if(frame == NULL)
    {
        av_log(NULL,AV_LOG_QUIET,"allocate frame failed\n");
        goto end;
    }
    ret = open_input_file(inputFileName ,&ifmt_ctx);
    if(ret < 0)
    {
        av_log(NULL,AV_LOG_QUIET,"Open input file failed\n");
        goto end;
    }
    av_dump_format(ifmt_ctx, 0, inputFileName, 0);

    input_codec_context = ifmt_ctx->streams[0]->codec;
    init_resampler(input_codec_context, &output_codec_context, &resample_context);

    while(1){
        ret = av_read_frame(ifmt_ctx, &packet);
        if(ret < 0){
            av_log(NULL,AV_LOG_INFO,"Reached end point\n");
            break;
        }
        stream_index = packet.stream_index;
        type = ifmt_ctx->streams[stream_index]->codec->codec_type;
        av_packet_rescale_ts(&packet,
                            ifmt_ctx->streams[stream_index]->time_base,
                            ifmt_ctx->streams[stream_index]->codec->time_base);

        ret = avcodec_decode_audio4(ifmt_ctx->streams[stream_index]->codec, frame, &got_frame, &packet);
        if( ret < 0 )
        {
            av_log(NULL,AV_LOG_QUIET,"Decoding failed\n");
            break;
        }

        if(got_frame)
        {
            init_converted_samples(&converted_samples, output_codec_context, frame->nb_samples);
            ret = convert_samples((const uint8_t**)frame->extended_data, converted_samples, frame->nb_samples, resample_context);
            if(ret < 0)
            {
                av_log(NULL,AV_LOG_QUIET,"Convert failed\n");
            }
            else{
                int i, ch;
                int data_size = av_get_bytes_per_sample(output_codec_context->sample_fmt);
                printf("frame->nb_samples=%d\n",frame->nb_samples);
                printf("data_size=%d\n",data_size);
                printf("channels=%d\n",output_codec_context->channels);
                //fwrite(converted_samples[0], 1, ret, fp);

                //for(i = 0; i < frame->nb_samples; i++)
                if(ret != 1024)
                {
                    printf("ret==== %d\n",ret);
                }
                for(i = 0; i < ret; i++)
                {
                    for(ch = 0 ;ch < output_codec_context->channels; ch++)
                        fwrite(converted_samples[ch] + data_size*i, 1, data_size , fp);
                }

                //判断是否有缓存
                int fifo_size = swr_get_out_samples(resample_context, 0);
                printf("fifo_size=%d\n",fifo_size);
                //if(fifo_size > frame->nb_samples)
                if(fifo_size > 0)
                {
                    ret = swr_convert(resample_context, converted_samples,frame->nb_samples,NULL,0);
                    printf("ret=%d\n",ret);
                    for(i = 0; i < ret; i++)
                    {
                        for(ch = 0 ;ch < output_codec_context->channels; ch++)
                            fwrite(converted_samples[ch] + data_size*i, 1, data_size , fp);
                    }
                }
            };

            if(converted_samples)
            {
                av_freep(&converted_samples[0]);
                free(converted_samples);
            }
        }
        av_packet_unref(&packet);
    }
end:
    av_packet_unref(&packet);
    //av_frame_free(&frame);
    for(i = 0; i < ifmt_ctx->nb_streams; i++)
    {
        avcodec_close(ifmt_ctx->streams[i]->codec);
    }
    avformat_close_input(&ifmt_ctx);

    fclose(fp);
    return 0;
}


编译方法:

gcc -o resample resample_mix.c -I/home/b1shen/workspace/itc_mcu_dep/install/include -L/home/b1shen/workspace/itc_mcu_dep/install/lib -lavdevice -lavformat -lavfilter -lpostproc -lavcodec -lswresample -lswscale -lavutil -lpthread -ldl -lxml2 -lz -ljsoncpp -lx264 -ldl -lm

具体的include及lib路径,根据你的实际目录来进行设置。

因为是测试段代码,有些内存并没有释放,大家在参数的过程中要稍加留意。


值的一提的是,由于解码出来的音频帧,在进行重采样后,实际的数据长度有所变化(可能变长,也可以变短),所以如何将resample后的音频数据扔给编码器编码,也是有方法的,否则编码会失败。


我看到有些网友有提到,需要使用ffmpeg中的AVAudioFifo,把resample后的数据存到fifo中,如果达到一帧,就把数据取出来,扔给编码器编码,否则等待后面的数据。稍后我会把这种方法测试一下,然后贴出代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值