每个端音频采集的底层和应用层的库是不一样的,所以使用ffmpeg中间层能够实现跨平台开发;
- Android端的底层库是AudioRecorder,应用层是MediaRecorder;
- iOS端的底层库是AudioUnit,应用层是AVFoundation;
- Windows端的常用的是Directshow OpenAL 还有Windows7之上的AudioCore;
使用ffmpeg有两种采集方式:
- 使用命令方式,命令详情查看ffmpeg相关指令的那篇
- 使用代码调用api的方式
- 在mac下的动态库需要对动态库进行签名
获取本地签名证书列表:/usr/bin/security find-identity -v -p codesigning
查看动态库是否签名: codesign -d -vv 动态库文件
签名命令:codesign -fs "iPhone Distribution: 你的签名证书." 动态库文件
xcode环境:13.2.1
签名了如果还是报错,关掉沙盒并且设置 Enable Hardened Runtime 为NO
在项目中设置user header search path的时候,要使用全路径方式,我使用$(PROJECT_NAME)方式,有的头文件在链接的时候会报错;
采集音频的步骤:
- 打开输入输出设备,涉及的包是avdevice avformat
- 注册设备
- 设置采集方式,根据平台选择,即设置输入
- 打开音频设备
- 获取数据包 包:avcodec
- 主要使用av_read_frame方法获取数据
- 将数据放入packet中
- 在读取的时候注意缓冲区无未准备好的情况
- 将数据输出到文件
- 创建文件--- fopen
- 将数据写入文件-- fwrite
- 关闭文件 -- fclose
- 打开设备 ·
void startRecorder(void) {
// 上下文
AVFormatContext *av_context = NULL;
AVDictionary *options = NULL;
// 1. 注册设备
avdevice_register_all();
// 2. 设置采集方式
//设置采集方式 mac os 下是AVfoundation Windows下是dshow linux 下是alsa
AVInputFormat *format = av_find_input_format("avfoundation");
// 3. 打开设备
//里面的识别格式为[[video device]:[audio device]] 这里写0 是获取第1个音频设备
char *name = ":0";
// url 是路径 可以是网络路径也可以是本地路径 本地路径mac下的格式是 video : audio 这里表示获取第一个音频设备
int result = avformat_open_input(&av_context, name, format, &options);
if (result != 0) {
char errors[1024];
// 根据返回值生成错误信息
av_make_error_string(errors, 1024, result);
printf("打开设备失败:%s\n", errors);
return;
}
printf("打开设备成功!\n");
get_audio_packet(av_context,&packet_callback);
// 关闭输入 上下文
avformat_close_input(&av_context);
}
- 读取数据和存储到文件
void get_audio_packet(AVFormatContext *context, void (*packet_callback)(AVPacket)) {
// w == 写 b == 二进制 + == 没有就创建文件
FILE *f = fopen("/Users/cunw/Desktop/learning/音视频学习/音视频文件/code_recorder.pcm", "wb+");
AVPacket *packet = av_packet_alloc();
int result = -1;
// 循环读取设备信息
// result == -35 是Resource temporarily unavailable 因为获取太频繁 设备未准备好,还正在处理数据
// 因为输入设备准备好需要时间 睡一秒后再读取
sleep(1.0);
while ((result = av_read_frame(·context, packet)) == 0 || result == -35) {
if (packet->size > 0) {
packet_callback(*packet);
fwrite(packet->data, packet->size, 1, f);
// 每读取一次 就清空数据包 不然数据包会一直增大
av_packet_unref(packet);
}
}
if (result != 0) {
char errors[1024];
av_make_error_string(errors, 1024, result);
printf("get packet occured error is \"%s\" \n", errors);
}
// 将缓冲区剩余的数据 强制写入文件
fflush(f);
fclose(f);
// 释放packet空间
av_packet_free(&packet);
}
// 回调函数
void packet_callback(AVPacket packet) {
printf("packet size is %d\n",packet.size);
}
-
播放
ffplay 播放pcm数据: ffplay -ar(采样率) 44100 -ac(通道数) 2 -f(采样大小)f32le 文件名
网友评论