有的编码器是不支持某些音频样式的,举个例子,fdk-aac编码器只支持16位,但是在Mac上采集的PCM数据是32位的,这个时候就需要进行音频重采样,简而言之,音频重采样可以让我们改变一个PCM数据的采样率,采样格式,声道数
IOS下利用FFmpeg进行音频重采样
步骤:输入文件->输入缓冲区->重采样函数->输出缓冲区->输出文件
下面示例由采样率44100,声道数1,采样格式s16le转为采样率48000,声道数1,采样格式f32le
1.创建上下文
int inSampleRate = 44100;
int outSampleRate = 48000;
int inChLayout = AV_CH_LAYOUT_MONO;
int outChLayout = AV_CH_LAYOUT_MONO;
NSString *outFilename = @"resample.pcm";
//创建重采样上下文
SwrContext *ctx = swr_alloc_set_opts(NULL, outChLayout, AV_SAMPLE_FMT_FLT, outSampleRate, inChLayout, AV_SAMPLE_FMT_S16, inSampleRate, 0, NULL);
if (!ctx) {
NSLog(@"创建上下文失败");
return;
}
//初始化重采样上下文
int ret = swr_init(ctx);
if (ret < 0) {
NSLog(@"初始化重采样上下文失败");
swr_free(&ctx);
return;
}
2.创建输入缓冲区和输出缓冲区
uint8_t **inData = NULL;
//缓冲区大小
int inLineSize = 0;
//声道数
int inChs = av_get_channel_layout_nb_channels(inChLayout);
//缓冲区的样本数量
int inBytesPerSample = inChs * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
int inSample = 1024;
ret = av_samples_alloc_array_and_samples(&inData, &inLineSize, inChs, inSample, AV_SAMPLE_FMT_S16, 1);
if (ret < 0) {
NSLog(@"创建输入缓冲区错误");
swr_free(&ctx);
return;
}
创建输出缓冲区
uint8_t **outData = NULL;
//缓冲区大小
int outLineSize = 0;
//声道数
int outChs = av_get_channel_layout_nb_channels(outChLayout);
int outBytesPerSample = outChs * av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT);
//缓冲区的样本数量
int outSample = (int)av_rescale_rnd(outSampleRate,inSample,inSampleRate,AV_ROUND_UP);
//开辟缓冲区
ret = av_samples_alloc_array_and_samples(&outData, &outLineSize, outChs, outSample, AV_SAMPLE_FMT_FLT, 1);
if (ret < 0) {
NSLog(@"创建输出缓冲区错误");
swr_free(&ctx);
av_freep(&inData);
return;
}
3.创建输出文件
//创建输出的文件
NSString *outFilePath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:outFilename];
NSFileManager *writeManager = [NSFileManager defaultManager];
if ([writeManager fileExistsAtPath:outFilePath]) {
[writeManager removeItemAtPath:outFilePath error:nil];
}
[writeManager createFileAtPath:outFilePath contents:nil attributes:nil];
NSFileHandle *writeHandle = [NSFileHandle fileHandleForWritingAtPath:outFilePath];
4.打开文件并读取数据到输入缓冲区,通过swr_convert函数重采样到输出缓冲区,写入输出文件
//读取文件
NSFileHandle *readHandle = [NSFileHandle fileHandleForReadingAtPath:_path];
NSData *readData = NULL;
[readHandle seekToFileOffset:0];
readData = [readHandle readDataOfLength:inLineSize];
inData[0] = (uint8_t *)[readData bytes];
int len = (int)[readData length];
while (len > 0) {
nSample = len / inBytesPerSample;
ret = swr_convert(ctx, outData, outSample, (const uint8_t **)inData, inSample);
if (ret < 0) {
NSLog(@"swr_convert error");
break;
}
[writeHandle writeData:[NSData dataWithBytes:outData[0] length:ret *outBytesPerSample]];
[writeHandle seekToEndOfFile];
readData = [readHandle readDataOfLength:inLineSize];
inData[0] = (uint8_t *)[readData bytes];
len = (int)[readData length];
}
//检查一下输出缓冲区是否还有残留的样本
while ((ret = swr_convert(ctx, outData, outSample, NULL, 0)) > 0) {
[writeHandle writeData:[NSData dataWithBytes:(char *)outData[0] length:ret * outBytesPerSample]];
}
5.释放资源,关闭文件
[readHandle closeFile];
[writeHandle closeFile];
swr_free(&ctx);
av_freep(&inData);
av_freep(&outData);