什么是音频重采样?
将音频的三要素 采样大小(位深)、声道数、采样率更换为另一套值
为什么要进行重采样?
- 从设备采集的音频数据和编码器要求的数据不一致;
- 扬声器要求的音频数据与播放的音频数据不一致;
- 更方便运算(例如 回音消除总是将多声道转为单声道)。
如何知道是否需要重采样?
- 要理解音频设备参数
- 查看ffmpeg的源码(ffmpeg内置了主流设备的参数)
重采样的步骤:
- 创建重采样上下文
- 设置参数
- 初始化重采样
- 进行重采样(对每一帧)
重要API
- swr_alloc_set_opts 分配上下文
- swr_init 初始化上下文
- swr_convert 采样率转换
- swr_free 释放上下文
-
av_samples_alloc_array_and_samples 创建缓冲区
-
av_freep 释放内存
我们对采集的音频数据 采样率为44100hz 通道数1 采样大小AV_SAMPLE_FMT_FLT(float类型 32位)
转化为 采样率48000hz, 通道数2,采样大小AV_SAMPLE_FMT_S16(16位)
上代码
//
// rec_audio.c
// yuanGitFFmpeg
//
// Created by yuanxuzhen on 2021/3/29.
//
#include "rec_audio.h"
//
// test_c.c
// MyFFmpegDemo
//
// Created by yuanxuzhen on 2021/3/26.
//
static int rec_status = 0;
void rec_audio(){
int ret = 0;
char error[1024] = {0, };
AVFormatContext* format_context = NULL ;
char *devicename=":0";
AVDictionary *options = NULL;
AVPacket pkt;
int count = 0;
char* url = "/Users/yuanxuzhen/study/mac/yuanGitFFmpeg/yuanGitFFmpeg/out/audio.pcm";
av_log_set_level(AV_LOG_DEBUG);
avdevice_register_all();
AVInputFormat * iformat = av_find_input_format("avfoundation");
ret = avformat_open_input(&format_context, devicename, iformat, &options);
av_dump_format(format_context, 0, devicename, 0);
if(ret < 0){
av_strerror(ret, error, 1024);
printf(stderr, "Failed to open audio devices, [%d] %s\n", ret, error);
return;
};
//创建文件
FILE *outFile = fopen(url, "wb");
rec_status = 1;
/*我们对采集的音频数据 采样率为44100hz 通道数1 采样大小AV_SAMPLE_FMT_FLT(float类型 32位)
转化为 采样率48000hz, 通道数2,采样大小AV_SAMPLE_FMT_S16(16位)*/
SwrContext* swr_ctx = swr_alloc_set_opts(NULL, // 已经存在的采样上下文,没有C传NULL
AV_CH_LAYOUT_MONO,//输出通道个数
AV_SAMPLE_FMT_S16,//输出的位深
48000,//输出的采样率
AV_CH_LAYOUT_MONO,//输入的通道个数
AV_SAMPLE_FMT_FLT,//输入的位深
44100,//输入的采样率
0,// logging level offset
NULL// parent logging context, can be NULL
);
uint8_t **src_data = NULL;
int src_linesize = 0;
uint8_t **dst_data = NULL;
int dst_linesize = 0;
//创建输入缓存区
av_samples_alloc_array_and_samples(&src_data, //输入缓冲区地址
&src_linesize, //缓冲区的大小
1,//通道个数
512,//单通道采样个数
AV_SAMPLE_FMT_FLT,//采样格式
0);
//创建输出缓存区
av_samples_alloc_array_and_samples(&dst_data,//输出缓冲区地址
&dst_linesize,//缓冲区的大小
1,//通道个数
512,//单通道采样个数
AV_SAMPLE_FMT_S16,//采样格式
0);
//获取音频数据并保存
while ((ret = av_read_frame(format_context, &pkt)) == 0 || ret == -35) {
if(!rec_status){
break;
}
if(ret == -35){
usleep(100);
continue;
}
//进行内存拷贝,按字节拷贝的uint8_t **src_data srcdata相对于是个数组
memcpy((void*)src_data[0], (void*)pkt.data, pkt.size);
//重采样
swr_convert(swr_ctx, //重采样的上下文
dst_data, //输出结果缓冲区
512, //输出每个通道的采样数
(const uint8_t **)src_data, //输入缓冲区
512); //输入单个通道的采样数
fwrite(dst_data[0], 1, dst_linesize, outFile);
fflush(outFile);
printf("creent pkt size %d count %d\n", pkt.size, count);
av_packet_unref(&pkt);
}
//释放输入输出缓冲区
if(src_data){
av_freep(&src_data[0]);
}
av_freep(src_data);
if(dst_data){
av_freep(&dst_data[0]);
}
av_freep(dst_data);
//释放重采样的上下文
swr_free(&swr_ctx);
fclose(outFile);
avformat_close_input(&format_context);
printf("creent count %d\n", count);
format_context = NULL;
return;
}
void set_status(int status){
rec_status = status;
}
ffplay 播放命令ffplay -ar 48000 -ac 1 -f s16le audio.pcm
源代码地址
https://gitee.com/creat151/ffmpeg.git