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);
}