ffmpeg重采样指的是可以将一个固定频率的音频转换为任意格式的音频,比如改变音频的采样率或者声道,这种操作简称为重采样。但是在重采样的过程中也会有一些数据丢失的过程,主要原因是在采样会会进行向上对齐,所以会出现转换后大小不一致的情况

在重采样的过程中,主要通过两个容器来互相交换数据来达到数据转换的效果,其中创建容器的方法为:

//返回值小于0代表创建失败
int av_samples_alloc_array_and_samples(uint8_t ***audio_data, int *linesize, int nb_channels,int nb_samples, enum AVSampleFormat sample_fmt, int align);

在进行重采样操作时,另开一个线程来完成此工作

void Audiothread::run()
{
    ResampleAudioSpec in,out;
    //44100-s16le-2
    in.FileName = "D:/ffmpeg/ffm07_31_21_57_24.pcm";
    out.FileName = "D:/ffmpeg/4800_f32le_1.pcm";
    //初始化重采样上下文
    int ret = 0;

    //输入参数
    in.SamPleFmt = AV_SAMPLE_FMT_S16;//f32le
    in.SampleRate = 44100;
    in.ChLayout = AV_CH_LAYOUT_STEREO;//2声道

    //输出参数
    out.SamPleFmt = AV_SAMPLE_FMT_FLT;
    out.SampleRate = 48000;
    out.ChLayout = AV_CH_LAYOUT_MONO;//1声道

    ffmpegs::resampleAudio(in,out);
}

#include "ffmpegs.h"
#include <QDebug>
#include <QFile>
extern "C" {
//
#include <libswresample/swresample.h>
// 工具相关API(比如错误处理)
#include <libavutil/avutil.h>
}

#define ERROR_BUF(ret) \
    char errbuf[1024]; \
    av_strerror(ret,errbuf,sizeof(errbuf));
ffmpegs::ffmpegs()
{

}

void ffmpegs::resampleAudio(ResampleAudioSpec& in, ResampleAudioSpec& out)
{
        resampleAudio(in.FileName,in.SampleRate,in.SamPleFmt,in.ChLayout,
                      out.FileName,out.SampleRate,out.SamPleFmt,out.ChLayout);
}

void ffmpegs::resampleAudio(const char *inFilename,
                            int inSamplesRate,
                            AVSampleFormat inSamplesFmt,
                            int inChLayout,

                            const char *outFilename,
                            int outSampleRate,
                            AVSampleFormat outSampleFmt1,
                            int outChLayout)
{
    //44100-s16le-2
    QFile inFile(inFilename);
    QFile outFile(outFilename);

    //创建输入缓冲区
    //指向缓冲区的指针
    uint8_t **inData = nullptr;
    //缓冲区大小
    int inLinesize = 0;
    //声道数
    int inChs = av_get_channel_layout_nb_channels(inChLayout);
    //一个样本大小
    int inBytesPerSample = inChs * av_get_bytes_per_sample(inSamplesFmt);
    //缓冲区的样本数量
    int inSamples = 1024;
    //读取文件的数据大小
    int len = 0;

    //创建输出缓冲区
    //指向缓冲区的指针
    uint8_t **outData = nullptr;
    //缓冲区大小
    int outLinesize = 0;
    //声道数
    int outChs = av_get_channel_layout_nb_channels(inChLayout);
    //一个样本大小
    int outBytesPerSample = outChs * av_get_bytes_per_sample(outSampleFmt1);
    //缓冲区的样本数量
    int outSample = av_rescale_rnd(outSampleRate,inSamples,inSamplesRate,AV_ROUND_UP);
    //outSample = outSampleRate * inSamples / inSamplesRate
    //例如b[i] = av_rescale_rnd(a[i], 2, 1, AV_ROUND_NEAR_INF); // 缩放因子为2:1,所以b[i] = 2 * a[i]
    /*
     inSamplesRate    inSampless
     ------------ =  ---------
     outSampleRate   outSamples
     */

    //返回结果
    int ret = 0;
    int isSamples = 0;
    //创建重采样上下文
    SwrContext *ctx = swr_alloc_set_opts(nullptr,
                                         //输出参数
                                         outChLayout,outSampleFmt1,outSampleRate,
                                         //输入参数
                                         inChLayout,inSamplesFmt,inSamplesRate,
                                         0,nullptr);
    if(!ctx)
    {
        qDebug() << "swr_alloc_set_opts----error";
        return;
    }

    //初始化重采样上下文
    ret = swr_init(ctx);
    if(ret < 0)
    {
        ERROR_BUF(ret);
        qDebug() << "swr_init" << errbuf;

        goto end;
        return;
    }


    //创建输出缓冲区
    //ret = 返回几个字节
    ret = av_samples_alloc_array_and_samples(&outData,
                                             &outLinesize,
                                             outChs,
                                             outSample,
                                             outSampleFmt1,
                                             1);
    if(ret < 0)
    {
        ERROR_BUF(ret);
        qDebug() << "av_samples_alloc_array_and_samples error: " << errbuf;

        goto end;
    }

    //创建输入缓冲区
    //ret = 返回几个字节
    ret = av_samples_alloc_array_and_samples(&inData,
                                             &inLinesize,
                                             inChs,
                                             inSamples,
                                             inSamplesFmt,
                                             1);
    if(ret < 0)
    {
        ERROR_BUF(ret);
        qDebug() << "av_samples_alloc_array_and_samples error: " << errbuf;

        goto end;
    }



    //打开文件
    if(!inFile.open(QFile::ReadOnly))
    {
        qDebug() << "打开文件失败:" << inFilename;
        goto end;
    }

    if(!outFile.open(QFile::WriteOnly))
    {
        qDebug() << "打开文件失败:" << outFilename;
        goto end;
    }

    //读取文件数据
    while((len = inFile.read((char*) inData[0],inLinesize)) > 0)
    {
        //读取的样本数量
        isSamples = len / inBytesPerSample;

        //重采样(返回值转换后的样本数量)
        ret = swr_convert(ctx,
                          outData,outSample,
                          (const uint8_t **)inData,isSamples);

        if(ret < 0)
        {
            ERROR_BUF(ret);
            qDebug() << "swr_convert error" << errbuf;
            goto end;
        }

        int size = av_samples_get_buffer_size(nullptr,outChs,ret,outSampleFmt1,1);
        //size == ret * outBytesPerSample


        //将转换后的数据写入输出文件中
        //outFile.write((char*) outData[0],ret * outBytesPerSample);
        outFile.write((char*) outData[0],size);
    }

    //检查一下输出缓冲区是否还有残留的样本(已经重采样过的,转换过的)
    while((ret = swr_convert(ctx,outData,outSample,nullptr,0)) > 0)
    {
        //将转换后的数据写入输出文件中
        outFile.write((char*) outData[0],ret * outBytesPerSample);

    }
end:
    //释放资源
    //关闭文件
    inFile.close();
    outFile.close();

    //释放重采样上下文
    swr_free(&ctx);
    //释放输出缓冲区
    av_free(&outData[0]);
    //释放输入缓冲区
    av_free(&inData[0]);
    return;


    //释放资源
    //释放重采样上下文
    swr_free(&ctx);
}

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部