ffmpeg重采样

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

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

bash 复制代码
//返回值小于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);

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

bash 复制代码
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);
}
bash 复制代码
#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);
}
相关推荐
无巧不成书02181 天前
FFmpeg 保姆级安装教程!Windows/macOS/Linux全平台覆盖,
windows·macos·ffmpeg
kkoral3 天前
OpenCV 与 FFmpeg 的关系
opencv·ffmpeg
kkoral3 天前
如何在 Python 中使用 OpenCV 调用 FFmpeg 的特定功能?
python·opencv·ffmpeg
山栀shanzhi3 天前
【FFmpeg】音视频MP4封装格式转封装MOV
ffmpeg·音视频
山栀shanzhi3 天前
【FFmpeg】是什么是未压缩的裸流?
c++·ffmpeg
Maverick064 天前
02-SQL执行计划与优化器:Oracle是怎么决定“该怎么查“的
数据库·sql·oracle·ffmpeg
EasyDSS4 天前
RTMP高清推流直播/智能转码/无人机直播EasyDSS破局旅游慢直播痛点
ffmpeg·旅游·视频转码·fmp4·点播技术
EasyDSS4 天前
RTMP高清推流直播/视频转码EasyDSS在无人机RTMP直播场景中的应用技术解析
ffmpeg·音视频·无人机·视频转码·语音转写·点播技术
私人珍藏库5 天前
[Windows] FFmpeg 图形化:EasyFF v0.17
ffmpeg·媒体·工具·软件·win·多功能
EasyDSS5 天前
场景深耕:低延迟高并发EasyDSS无人机RTMP高清推流直播技术剖析
ffmpeg·webrtc·rtmp