音视频(五):音频重采样

862 阅读1分钟

有的编码器是不支持某些音频样式的,举个例子,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);