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);
}
相关推荐
企业数字化笔记12 小时前
视觉处理为什么会慢?解码、预处理、模型推理、后处理和编码的性能拆解
ffmpeg·gpu算力
Maynor9961 天前
6张商品图→17秒带货短视频:开源12个Claude Skills,配音、字幕花字、BGM本地一条命令出片
ffmpeg·ai编程·claude·电商·agent skills
AIGC小尼1 天前
8G 显存零基础本地部署 AI 漫剧全流程|ComfyUI+Wan2.2+FFmpeg 离线成片完整方案(含代码 / 指令 / 排坑)
人工智能·ffmpeg·php·comfyui·ai漫剧
开开心心就好1 天前
以图搜图找重复图片,本地工具离线就能用
javascript·智能手机·ffmpeg·c#·ocr·word·音视频
可乐鸡翅yeah_2 天前
AES‑128 加密 M3U8,IV 初始化向量新手容易踩坑
前端·网络·数据库·ffmpeg·音视频·m3u8在线
企业数字化笔记4 天前
视频视觉任务怎样选择媒体处理技术?FFmpeg、OpenCV、GStreamer 的职责与适用场景
ffmpeg·音视频·媒体
可乐鸡翅yeah_4 天前
网页端 M3U8 播放器性能监控,怎么看播放卡顿指标
javascript·ffmpeg·音视频·safari·m3u8
开开心心就好4 天前
超市定时播音软件,免费版支持循环播放
智能手机·ffmpeg·ocr·word·vim·音视频·visual studio
企业数字化笔记4 天前
视频成片怎么稳定导出?编码选择、GPU/CPU降级与媒体交付验收
java·python·ffmpeg
企业数字化笔记4 天前
视觉智能体怎么选技术?目标检测、跟踪、分割、OpenCV和FFmpeg的职责划分
opencv·目标检测·ffmpeg