2. 【音频采集实战】

每个端音频采集的底层和应用层的库是不一样的,所以使用ffmpeg中间层能够实现跨平台开发;

  • Android端的底层库是AudioRecorder,应用层是MediaRecorder;
  • iOS端的底层库是AudioUnit,应用层是AVFoundation;
  • Windows端的常用的是Directshow OpenAL 还有Windows7之上的AudioCore;

使用ffmpeg有两种采集方式:

  1. 使用命令方式,命令详情查看ffmpeg相关指令的那篇
  2. 使用代码调用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)方式,有的头文件在链接的时候会报错;

采集音频的步骤:

  1. 打开输入输出设备,涉及的包是avdevice avformat
    1. 注册设备
    2. 设置采集方式,根据平台选择,即设置输入
    3. 打开音频设备
  2. 获取数据包 包:avcodec
    1. 主要使用av_read_frame方法获取数据
    2. 将数据放入packet中
    3. 在读取的时候注意缓冲区无未准备好的情况
  3. 将数据输出到文件
    1. 创建文件--- fopen
    2. 将数据写入文件-- fwrite
    3. 关闭文件 -- 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 文件名

上一篇::音频基础知识
下一篇:音频编码原理

你可能感兴趣的:(2. 【音频采集实战】)