webrtc音频模块(三) windows Core Audio API及声音的播放

在前面介绍了ADM(Audio Device Module),它用于抽象音频设备管理和音频数据采集/播放接口。windows的实现是AudioDeviceWinowCode,它封装了Core Audio APIs实现了对音频设备的操作。

Core Audio APIs

windows提供了多种音频操作API,比如最常用的是 waveXxx系列API,DirectSound等。而Core Audio APIs是这些API的基础,这些API使用Core Audio APIs提供了更通用的功能。

如下图是Core Audio APIs的架构图:

  • Core Audio APIs是一些高阶API(例如MME,DirectSound等)的基础。
  • 箭头的方向表示了音频数据的流向。
  • 它有两种工作模式,共享模式和独占模式。共享模式就是大家(多个应用程序)同时播放声音(声音被混音),独占模式就是只能有一个程序播放,我一播,就没其他程序的声音了。
  • 共享模式下,会有一个Audio Service进行协调各应用程序间的音频数据处理。这个很容易理解,多路声音,总该需要一个大管家来协调使用设备。
  • 独占模式下,音频数据就直接到内核的驱动了。

Core Audio APIs特点是音频处理更高效,延时更低。对webrtc 这种RTC系统来说,正是需要其低延时的保证。

它四类子API

从上图中可以看到Core Audio APIs是一系列API的集合,它包括四类子API。

  • MMDevice API(用于检索播放采集设备)

用于应用程序检索音频终端设备,枚举出所有可使用的音频设备属性及确定其功能,并为这些设备创建驱动程序实例,是最基本的Core Audio API,服务于其它3个APIs。


  • WASAPI(控制播放和采集流)

应用程序可以通过它管理程序和音频终端设备之间音频数据的流。比如采集,回放音频。


  • DeviceTopology API(webrtc中没用到)

应用程序可以遍历音频适配器设备和音频终端设备的内部拓扑,并单步执行将设备链接到另一台设备的连接。通过 DeviceTopology API 中的接口和方法,客户端程序可直接沿着音频适配器 (audio adapters) 的硬件设备里的数据通道进入布局特征(例如,沿着音频终端设备的数据路径上进行音量控制) 。

  • EndpointVolume API(控制音量)

应用程序可以控制和监视音频终端设备的音量。

它们都以COM组件的方式提供,应用程序需要创建对应COM组件的实例,获取接口对象,再使用它们提供的方法。

AudioDeviceWindowCore

在webrtc中使用Core Audio APIs以下四个功能:

  1. 检索音频回放设备。
  2. 检索音频采集设备。
  3. 使用指定的音频设备回放声音。
  4. 使用指定的音频设备采集声音。
  5. 音频回放。
  6. 音频采集。

类图如下:

它直接管理Core Audio APIs的COM对象

**I**开头的都是对象接口类:

  • IMMDevice代表一个音频设备。
  • IMMDeviceCollection音频设备集。
  • IMMDeviceEnumerator用于枚举音频设备。
  • IMMEndpoint代表一个音频终端设备。

功能实现

检索音频设备

如下图,系统中一般都会有扬声器和麦克风,在声音设置中可以看到它们。

AudioDeviceWindowCore::Init方法中实现检索回放和采集设备,需要使用的接口对象是IMMDeviceEnumerator,检索出来的结果保存在 IMMDeviceCollection对象中。

音频设备有名字,音频参数(如:声道数,采样率等)等属性,这些都会一并获取到。

IMMDeviceCollection* pCollection = NULL;
hr = _ptrEnumerator->EnumAudioEndpoints(dataFlow,  // data-flow direction (input parameter)DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED | DEVICE_STATE_UNPLUGGED,&pCollection);
hr = pCollection->GetCount(&count);
for (ULONG i = 0; i < count; i++)  {//遍历每个设备,获取对应的属性
}

调用EnumAudioEndpoints方法检索指定状态的设备,通过GetCount获取数量,再遍历设备获取属性。

播放声音
指定回放设备

首先要指定要使用的回放设备,通过序号指定,在IMMDeviceCollection中检索,通过index获取到IMMDevice对象,它就代表了一个音频设备。

回放声音需要使用WASAPI模块的IAudioClient接口,它通过IMMDevice获取

hr = _ptrDeviceOut->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL,(void**)&_ptrClientOut);
根据设备支持的音频参数,确定一个输出格式。

音频,有采样率,声道,位率这些参数,不同的值决定了声音的质量及数据大小。WASAPI中用这个结构体来描述

在回放声音时,要指定这些参数,就是告诉WASAPI怎么去播放声音,但是首先要知道的是,音频设备支持怎样的播放参数。

hr = _ptrClientOut->GetMixFormat(&pWfxOut);

获取到的信息如下:

[017:755][95740] (audio_device_core_win.cc:1851): Audio Engine’s current rendering mix format:

[017:755][95740] (audio_device_core_win.cc:1853): wFormatTag : 0xfffe (65534)

[017:755][95740] (audio_device_core_win.cc:1857): nChannels : 2

[017:755][95740] (audio_device_core_win.cc:1859): nSamplesPerSec : 48000

[017:755][95740] (audio_device_core_win.cc:1861): nAvgBytesPerSec: 384000

[017:755][95740] (audio_device_core_win.cc:1863): nBlockAlign : 8

[017:755][95740] (audio_device_core_win.cc:1865): wBitsPerSample : 32

[017:755][95740] (audio_device_core_win.cc:1866): cbSize : 22

在webrtc中以采用率及声道数为标准现找一个与需求最贴合的参数,如下信息:

[017:802][95740] >>>>

[017:802][95740] (audio_device_core_win.cc:1927): VoE selected this rendering format:

[017:802][95740] (audio_device_core_win.cc:1928): wFormatTag : 0x1 (1)

[017:802][95740] (audio_device_core_win.cc:1931): nChannels : 2

[017:802][95740] (audio_device_core_win.cc:1932): nSamplesPerSec : 48000

[017:802][95740] (audio_device_core_win.cc:1933): nAvgBytesPerSec : 192000

[017:802][95740] (audio_device_core_win.cc:1934): nBlockAlign : 4

[017:802][95740] (audio_device_core_win.cc:1935): wBitsPerSample : 16

[017:802][95740] (audio_device_core_win.cc:1936): cbSize : 0

[017:802][95740] (audio_device_core_win.cc:1937): Additional settings:

[017:802][95740] (audio_device_core_win.cc:1938): _playAudioFrameSize: 4

[017:802][95740] (audio_device_core_win.cc:1939): _playBlockSize : 480

[017:802][95740] (audio_device_core_win.cc:1940): _playChannels : 2

确定了这些参数,就可以确定喂入设备的音频数据量大小。

获取流输出控制接口IAudioRenderClient

通过IAudioClient获取IAudioRenderClient,它就是控制音频流的接口。

hr = _ptrClientOut->GetService(__uuidof(IAudioRenderClient),(void**)&_ptrRenderClient);

相关代码在AudioDeviceWindowsCore::InitPlayout方法中。

播放

在webrtc使用的Core Audio API的共享模式,在共享模式下将会有一个Audio Service(在上面的图中可以看出来),应用程序将通过Enpoint Buffer与Service交互。

播放声音,就是往这个buffer中写入音频数据,应用程序写入数据,Audio Service读取数据。

一端写,一端读,就需要判断buffer的空间,所以需要程如下几步:

  • 先通过IAudioClientGetBufferSize接口获取buffer大小。
  • 再通过IAudioClientGetCurrentPadding接口,获取buffer待Audio Service的处理的数据。
  • 计算可用空间:buffer size - padding data size 就是buffer中可用的空间。
  • 通过IAudioRenderClientGetBuffer接口获取buffer的地址。
  • 往buffer中写数据。

完整的流程可以看看AudioDeviceWindowsCore::DoRenderThread()方法。

需要注意一点,这里的buffer size不是以字节为单位,而是以audio frame为单位,通过API获取的是buffer可存放的audio frame数,及可用的frame空间。

audio frame的大小由采样率和采样时长决定,在webrtc中以10ms作为采样时长,那么48000HZ的采样率,一个audio frame的大小就是480采样点(换算成字节数:每个采样点2个字节,10ms的数据960个字节)。

播放线程

音频数据是不停的往Audio Service的buffer中写入,webrtc通过一个线程实现取应用层音频数据到写入buffer流程,如下流程图:

播放线程不会停,会持续不断的取数据,写入Audio Service Buffer,线程对应的方法为 AudioDeviceWindowsCore::DoRenderThread()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/890538.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

cursor保存更改操作技巧

1. 当我们在agent模式时&#xff0c;要求cursor更改代码时&#xff0c;cursor回答后&#xff0c;就已经更改了代码了&#xff0c;这时候就可以对程序进行编译和测试&#xff0c; 不一定先要点” accept“, 先测试如果没有问题再点“accept”&#xff0c;这样composer就会多一条…

运维工程师面试系统监控与优化自动化与脚本云计算的理解虚拟化技术的优点和缺点

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c; 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把…

2.在 Vue 3 中使用 ECharts 实现动态时间轴效果

在前端开发中&#xff0c;时间轴&#xff08;Timeline&#xff09;是一种常见且有效的方式来展示时间相关的数据。在本篇文章中&#xff0c;我们将展示如何在 Vue 3 项目中使用 ECharts 创建一个具有动态时间范围的时间轴&#xff0c;并添加了今日时间的标记以及通过按钮来前进…

Android修行手册 - 移动端几种常用动画方案对比

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…

Java日志框架:log4j、log4j2、logback

文章目录 配置文件相关1. properties测试 2. XMl使用Dom4j解析XML Log4j与Log4j2日志门面 一、Log4j1.1 Logges1.2 Appenders1.3 Layouts1.4 使用1.5 配置文件详解1.5.1 配置根目录1.5.2 配置日志信息输出目的地Appender1.5.3 输出格式设置 二、Log4j22.1 XML配置文件解析2.2 使…

《信管通低代码信息管理系统开发平台》Linux环境安装说明

1 简介 信管通低代码信息管理系统应用平台提供多环境软件产品开发服务&#xff0c;包括单机、局域网和互联网。我们专注于适用国产硬件和操作系统应用软件开发应用。为事业单位和企业提供行业软件定制开发&#xff0c;满足其独特需求。无论是简单的应用还是复杂的系统&#xff…

HTTPS协议原理与CA认证

目录 引言 HTTPS 是什么 1.什么是"加密" 2. 为什么要加密 3. 常⻅的加密⽅式 对称加密 ⾮对称加密 4.数据摘要 && 数据指纹 5. 数字签名 HTTPS 的⼯作过程探究 ⽅案 1 - 只使⽤对称加密 ⽅案 2 - 只使⽤⾮对称加密 ⽅案 3 - 双⽅都使⽤⾮对称加密…

vulnhub靶场——Log4j2

第一步:搭建靶场环境 #开启环境 cd vulhub/log4j/CVE-2021-44228 docker-compose up -d 来到网站首页 第二步:搭建一个dnslog平台上获取我们注入的效果 第三步:发现 /solr/admin/cores?action 这里有个参数可以传 我们可以看到留下了访问记录并且前面的参数被执行后给我们回…

使用idea创建JDK8的SpringBoot项目

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 使用idea创建JDK8的SpringBoot项目 前言我们经常在创建新的springboot项目&#xff0c;默认使用的是spring.io进行创建&#xff0c;但是它总是只会提供高版本的创建方式&…

U9多组织销退业务流程的总结

多组织销退业务流程&#xff0c;它的运行模式也是奇葩&#xff0c;确实不好理解其中的道理。用户实践中更是障碍重重&#xff0c;束手无策。左也不是右也不是&#xff0c;无路可走。 不能理解透彻造成的吧&#xff0c;所以做一个总结。 既有退货&#xff0c;必有出货单。从出货…

cudnn版本gpu架构

nvcc --help 可以看 --gpu-architecture 写到的支持的架构 NVIDIA 的 GPU 架构是按代次发布的&#xff0c;以下是这些架构的对应说明&#xff1a; NVIDIA Hopper: 这是 NVIDIA 于 2022 年推出的架构之一&#xff0c;面向高性能计算&#xff08;HPC&#xff09;和人工智能&…

Prompt格式到底有多重要?它竟然这样影响LLM函数调用能力(附提示词模版)

函数调用能力的关键地位 在当前大语言模型&#xff08;LLM&#xff09;的应用生态中&#xff0c;函数调用能力&#xff08;Function Calling&#xff09;已经成为一项不可或缺的核心能力。它使LLM能够通过调用外部API获取实时信息、操作第三方服务&#xff0c;从而将模型的语言…

有了chatgpt4o,普通人还需要学代码吗?

或许AI到达“终极智能”时&#xff0c;普通人就不用学代码了。不过现阶段或很长的一段时间内这还是不可能的。目前AI编程还是以辅助编程为主&#xff0c;普通人可以借助AI实现一些简单的编程。 其实这个问题可以扩大到AI编程的出现对编程行业或程序员职业影响的问题。就这个问…

CE8.【C++ Cont】练习题组6

目录 1.矩阵转置 题目描述 输入格式 输出格式 输入输出样例 错误代码 提交结果 正确代码 提交结果 2.图像相似度 题目描述 输入格式 输出格式 输入输出样例 代码 提交结果 3. 计算矩阵边缘元素之和 题目描述 输入格式 输出格式 输入输出样例 说明/提示 …

哪个网页版思维导图好用?这5款高效软件不容错过!

眼下虽然每个人的电脑硬盘越来越大&#xff0c;但很多人还是保留着“能不装软件就不装”的理念&#xff0c;在选择办公软件时&#xff0c;会更倾向于选用推出了网页版的软件&#xff0c;这对思维导图来说也不例外。 那具体到思维导图网页版&#xff0c;有哪些软件值得推荐&…

【双指针算法】--复写零

文章目录 1. 题目2. 题目解析3. 代码 1. 题目 在线oj 给你一个长度固定的整数数组 arr &#xff0c;请你将该数组中出现的每个零都复写一遍&#xff0c;并将其余的元素向右平移。 注意&#xff1a;请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改&a…

石岩基督教福音堂

周末娃&#xff0c;娃的阿婆和我一起去石岩基督教福音堂做礼拜。 这是我第一次进入石岩的教堂。教堂很高。应该有3,4层楼高。 这周末做礼拜的人很多一楼人满了&#xff0c;阿婆去二楼做礼拜&#xff0c;娃和我去三楼的儿童室。很多家长楼下做礼拜&#xff0c;小朋友被安排三楼…

Flutter 异步编程简述

1、isolate 机制 1.1 基本使用 Dart 是基于单线程模型的语言。但是在开发当中我们经常会进行耗时操作比如网络请求&#xff0c;这种耗时操作会堵塞我们的代码。因此 Dart 也有并发机制 —— isolate。APP 的启动入口main函数就是一个类似 Android 主线程的一个主 isolate。与…

IDEA用jformdesigner插件做管理系统MVC架构

在 IntelliJ IDEA 中结合 JFormDesigner 插件&#xff0c;通过 Swing 框架实现一个管理系统的 MVC 架构是一种经典的开发方式。以下是具体的步骤和实现思路&#xff0c;包含从项目创建到 MVC 架构的核心代码实现。 1. 项目结构设计 为了清晰的 MVC 分层架构&#xff0c;建议按…

Linux内核调度优先级详解:如何优化你的系统性能

个人主页&#xff1a;chian-ocean 文章专栏-Linux 前言&#xff1a; 进程优先级调度是操作系统中的一种调度机制&#xff0c;其核心是为每个进程分配一个 优先级&#xff08;Priority&#xff09;&#xff0c;然后根据优先级的高低决定进程执行的顺序和时间。这种机制确保了关…