文|网易云信资深PC端开发工程师

Windows Vista 之后的系统中,音频系统相比之前的系统有很大的变化,产生了一套新的底层 API 即 Core Audio APIs 。

该低层 API 为高层 API【如 Media Foundation(将要取代DirectShow 等高层 API)等 】 提供服务。该系统API具有低延迟、高可靠性、安全性等特点。

本文主要从实时音视频场景中,简单介绍该API的使用。

Core Audio APIs 的组成:MMDevice、EndpointVolume、WASAPI等。对于实时音视频系统,主要用到的是MMDeviceEndpointVolume这两套API。其在系统中的位置如下图:

我对实时音视频中音频设备的使用简单的分为:

1. 设备列表管理

2. 设备初始化

3. 设备功能管理

4. 数据交互

5. 音量管理

6. 设备终端监听

接下来为大家介绍相关功能的实现。

相关功能的实现

1、设备列表管理 

音频设备的管理,由MMDevice API来实现。

首先我们要创建一个IMMDeviceEnumerator对象来开始相关功能的调用。

IMMDeviceEnumerator* ptrEnumerator;CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL,                    __uuidof(IMMDeviceEnumerator),                    reinterpret_cast<void**>(&ptrEnumerator));

并通过IMMDeviceEnumerator可以实现:获取系统默认设备GetDefaultAudioEndpoint、获取设备集合IMMDeviceCollection、获取指定设备GetDevice、注册设备监听IMMNotificationClient(监听设备插拔及状态变更)。

通过这些方法,我们能得到系统默认设备、遍历设备列表、打开指定设备并监听设备变更。这样就实现了实时音视频中的设备管理相关的功能。

2、设备初始化

音频设备的启动是整个音频模块的可靠性的重要节点。根据设备类型和设备数据捕获方式,我们可分为3类设备:麦克风采集、扬声器播放、扬声器采集。

首先我们需要一个IMMDevice对象,可以在设备管理的相关功能中获取。

IMMDevice* pDevice;//GetDefaultptrEnumerator->GetDefaultAudioEndpoint((EDataFlow)dir,                                              (ERole)role/* eCommunications */,                                              &pDevice);//Get by pathptrEnumerator->GetDevice(device_path, &pDevice);//GetIndexpCollection->Item(index, &pDevice)

再通过IMMDevice得到IAudioClient,设备的格式设置及初始化通过IAudioClient对象实现。一般都以共享模式打开,其中麦克风采集及扬声器播使用事件驱动方式处理数据,而扬声器采集以回环的方式驱动处理数据。

简单示例如下:

//mic capturerptrClient->Initialize(AUDCLNT_SHAREMODE_SHARED,AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,0,0,  (WAVEFORMATEX*)&Wfx,NULL);//playout renderptrClient->Initialize(AUDCLNT_SHAREMODE_SHARED,AUDCLNT_STREAMFLAGS_EVENTCALLBACK,0,0,  (WAVEFORMATEX*)&Wfx,NULL);//playout capturerptrClient->Initialize(AUDCLNT_SHAREMODE_SHARED,AUDCLNT_STREAMFLAGS_LOOPBACK,0,0,  (WAVEFORMATEX*)&Wfx,NULL);

其中Wfx是设备格式参数,一般为了保证设备的可用性,使用默认格式(通过IAudioClient::GetMixFormat获取),如果需要使用自定义格式,可以通过IAudioClient::IsFormatSupported方法去遍历尝试设备支持格式。

3、设备功能管理

针对麦克风设备,我们通常需要对其进行数据处理。部分硬件设备和系统支持自带的降噪、增益、消回音等功能。但是一般windows系统下设备比较繁杂不可控,大都使用软件算法处理。

如果我们需要检测设备是否使用了自带的处理功能及相关参数,需要使用Topology模块的功能。

IDeviceTopology* pTopo;pDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_INPROC_SERVER, 0,&pTopo);

通过IDeviceTopology,我们能够遍历IConnector对象,获得IAudioAutoGainControl、IAudioVolumeLevel等能力对象,并处理相关能力。

注意:IConnector可能是循环嵌套,在遍历IConnector的IPart时需要判别成员对象IPart的类型。

4、数据交互

在设备初始化的时候,我们就根据不同的设备选择了不同的模式进行了启动。不同的设备在各自的模式下,数据驱动也各有不同:

麦克风采集:

扬声器播放:

扬声器采集:

在和设备进行数据交互时,我们需要根据数据获取模式,获取对应的服务对象来获取设备数据。其中采集部分使用IAudioCaptureClient服务用于获取设备数据,播放使用IAudioRenderClient服务获取设备数据传入指针。

示例如下:

//capturerIAudioCaptureClient* ptrCaptureClient;//audioin or audiooutptrClient->GetService(__uuidof(IAudioCaptureClient), (void**)&ptrCaptureClient);{//work thread     //Wait EventptrCaptureClient->GetBuffer(&pData,            // packet which is ready to be read by used&framesAvailable,  // #frames in the captured packet (can be zero)&flags,            // support flags (check)&recPos,    // device position of first audio frame in data packet&recTime);  // value of performance counter at the time of recording//pData processingptrCaptureClient->ReleaseBuffer(framesAvailable);}//renderIAudioRenderClient* ptrRenderClient;//audiooutptrClient->GetService(__uuidof(IAudioRenderClient), (void**)&ptrRenderClient);{//work thread               BYTE* pData;//form buffer                UINT32 bufferLength = 0;               ptrClient->GetBufferSize(&bufferLength);               UINT32 playBlockSize = nSamplesPerSec / 100;               //Wait Event               UINT32 padding = 0;               ptrClient->GetCurrentPadding(&padding);               if (bufferLength - padding > playBlockSize)               {                          ptrRenderClient->GetBuffer(playBlockSize, &pData);//request and getdataptrCaptureClient->ReleaseBuffer(playBlockSize, 0);}}

在实际的数据交互中,需要另开单独线程处理GetBuffer及ReleaseBuffer。其中麦克风采集及扬声器播放时,都是通过设备事件驱动,可以在设备初始化完成后设置响应的事件句柄(IAudioClient::SetEventHandle)。

在整个音视频系统中,设备数据线程还需要统计数据处理时长、采集播放缓存大小等,用户监听检查设备状态及aec延迟计算。

5、音量管理

一般音量管理只在设备选定后处理当前设备的音量,所以一般使用IAudioEndpointVolume,该对象通过设备对象IMMDevice获取:

IAudioEndpointVolume* pVolume;pDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL,                           reinterpret_cast<void**>(&pVolume));

得到IAudioEndpointVolume对象后,我们能处理当前设备的音量控制:

pVolume->GetMasterVolumeLevelScalar(&fLevel);pVolume->SetMasterVolumeLevelScalar(fLevel, NULL);

静音控制:

BOOL mute;pVolume->GetMute(&mute);pVolume->SetMute(mute, NULL);

以及注册IAudioEndpointVolumeCallback监听音量状态:

IAudioEndpointVolumeCallback* cbSessionVolume;//need to dopVolume->RegisterControlChangeNotify(cbSessionVolume);

6、设备终端监听

在运行过程中除了设备的插拔等操作,还可能有一些属性变更等,一般用IAudioSessionEvents监听:

IAudioSessionControl* ptrSessionControl;ptrClient->GetService(__uuidof(IAudioSessionControl), (void**)&ptrSessionControl);IAudioSessionEvents* notify;ptrSessionControl->RegisterAudioSessionNotification(notify);

该回调监听,能监听该设备的连接工作状态,名称变更等。

一些注意事项

1、线程优先级

在实际的工程开发过程中,我们需要对音频线程的工作线程进行处理。通常通过调用系统模块Avrt.dll,动态调用其下的函数,将调用线程与指定任务(Pro Audio)相关联。上代码:

函数绑定:

avrt_module_ = LoadLibrary(TEXT("Avrt.dll"));if (avrt_module_){_PAvRevertMmThreadCharacteristics = (PAvRevertMmThreadCharacteristics)GetProcAddress(avrt_module_, "AvRevertMmThreadCharacteristics");_PAvSetMmThreadCharacteristicsA = (PAvSetMmThreadCharacteristicsA)GetProcAddress(avrt_module_, "AvSetMmThreadCharacteristicsA");_PAvSetMmThreadPriority = (PAvSetMmThreadPriority)GetProcAddress(avrt_module_, "AvSetMmThreadPriority");}

在实际的数据处理线程关联:

hMmTask_ = _PAvSetMmThreadCharacteristicsA("Pro Audio", &taskIndex);if (hMmTask_){_PAvSetMmThreadPriority(hMmTask_, AVRT_PRIORITY_CRITICAL);}

通过任务绑定,能有效的提升音频数据处理线程的可靠性。

2、工作线程

设备的相关初始化和释放操作,需要在统一的线程处理,部分系统com对象在释放时需要在创建线程释放,不然可能导致释放崩溃。而一些音量选择、监听等的处理可以在用户线程处理,但需要做好多线程安全。

3、设备格式选择

在设备的采样率、声道等格式选择时,如果需要使用自定义的格式,可能出现格式匹配失败或者选择匹配的格式后设备初始化失败的场景。通常此类场景下直接使用默认格式启动。

4、数据处理异常

在数据处理线程处理音频数据时,通常会出现事件响应超时、设备对象异常等情况。通常的处理方法是,先退出数据线程并结束设备,然后检查当前设备是否正常功能,然后重新启动当前设备或选用默认设备。

点击阅读原文,了解【网易云信】。

推荐阅读

《网易实战分享|Docker文件系统实战》

《开源|如何开发一个高性能的redis cluster proxy?》

Windows下Core Audio APIs的使用简介相关推荐

  1. Windows下Core Audio APIS 音频应用开发(二)

    对于一个音频应用程序,最基本也是最重要的两个点就是:音频数据的采集:音频数据的播放.下面我们来看下如何用Core Audio APIS进行音频数据的采集. 最权威的学习资料无疑是微软的MSDN官方资料 ...

  2. Windows下Core Audio APIS 音频应用开发(五)

    之前做开发是一直有个疑问(博主qq,1204802552,欢迎交流) 按照Core Audio上面的文档,音频设备本身会有个设备周期,这个周期决定了音频设备所能缓存的最大数据量:而另外一方面,我们在初 ...

  3. Windows Core Audio APIs(一)介绍

    文章目录 Windows Core Audio APIs(一)介绍 Core Audio 架构介绍 Core Audio 架构图 音频高级API Core Audio APIs 共享模式和独占模式 音 ...

  4. Windows下Core_Audio_APIs的使用简介

    Windows Vista 之后系统,音频系统相比之前的系统有很大的变化,产生了一套新的底层 API 即 Core Audio APIs .该低层 API 为高层 API( 如 Media Found ...

  5. Core Audio API

    http://msdn.microsoft.com/en-us/library/dd370784(v=vs.85).aspx文章翻译 译文转载自http://www.cppblog.com/shenh ...

  6. Windows下RabbitMQ安装及注意事项

    Windows下RabbitMQ安装及注意事项 简介 背景 1.      RabbitMQ是一个由erlang开发的AMQP(Advanved Message Queue)的开源实现. Rabbit ...

  7. 音视频开发(4)---Windows下Red5安装与使用入门

    Windows下Red5安装与使用入门 简介 Red5是一个采用Java开发开源的Flash流媒体服务器.它支持:把音频(MP3)和视频(FLV)转换成播放流: 录制客户端播放流(只支持FLV):共享 ...

  8. Windows Core Audio 音频开发技术指南

    在音视频通信处理流程中,音频方面最基本的无外乎就是音频的采集和播放.windows 平台下,有很多音频采集播放的方法.作为一个 windows 端音频应用程序开发人员,经常会被各种可用的API淹没,比 ...

  9. Windows系统下使用protobuf:protobuf的简介、安装、使用方法之详细攻略

    Windows系统下使用protobuf:protobuf的简介.安装.使用方法之详细攻略 目录 protobuf的简介 protobuf的安装 protobuf的使用方法 protobuf的简介 P ...

最新文章

  1. 解决 Cannot open D:\Program Files\Anaconda3\Scripts\pip-script.py 问题
  2. 智慧水务建设纪实:数字可视化大屏+智能AI,凭啥这么强?
  3. python语言中split-python中的split()函数和os.path.split()函数使用详解
  4. JDK9.0.4安装配置
  5. va_list!!!
  6. 台州银行登录显示服务器异常,手把手教你设置台州银行网上银行【处理办法】...
  7. ViewStub 简介
  8. 软件开发中JAVA编程语言的应用
  9. 图形评审技术(GERT)与计划评审技术(PERT)
  10. 做直流逆变中用到的全桥逆变电路测试mos管好坏的方法
  11. 垃圾小白羊leetcode刷题记录2
  12. 1447D Catching Cheaters
  13. 洛谷 3455 (莫比乌斯反演优化)
  14. 山东女子学院数据科学与计算机学院官网,数据科学与计算机学院举办2006级校友返校活动...
  15. 通过小型机液晶面板查看FSP/ASMI IP地址
  16. Java正则表达式(详解)
  17. 基于盲盒商城的飞机大战游戏
  18. 计算机技术对社会的消极影响,【信息技术引发的社会问题】_请举一个例子说说信息技术对社会带来哪些消极的影响,你能分析一下......
  19. C++版 PPyolo+部署记录
  20. 第1章 计算机系统结构的基本概念 思维导图

热门文章

  1. PHP面向对象设计模式
  2. POJ-1414 Life Line (暴力搜索)
  3. 续流二极管原理及应用
  4. mooc北京大学python_实用Python程序设计
  5. python写小说阅读器_用python实现自己的小说阅读器!
  6. 编写AXI4协议读写BRAM并仿真验证
  7. 翻译 | ORB: An efficient alternative to SIFT or SURF(ORB:对SIFT或SURF的一种有效选择)
  8. background 旋转_基于HTML5 Canvas 实现矢量工控风机叶轮旋转
  9. Python IDLE入门
  10. c的关于数组初始化 和 memset用法