上一个wasapi demo已经很详细的说明如何采集音频了,也完全可以应用在商业的代码中。
但是人嘛,总是想使用更好的代码,我也不例外,我们知道obs在windows下采集音频就是使用wasapi,所以我们去看看obs是如何使用的。
obs是通过插件就行加载的,使得我们比较容易找到它的wasapi模块,路径:obs-studio\plugins\win-wasapi。
主要的逻辑实现放在了win-wasapi.cpp中。
obs音频采集插件在程序启动的时候就创建好了,包括扬声器声音捕获 wasapi_output_capture 和麦克风声音捕获 wasapi_input_capture 。两者管理都是通过 class WASAPISource 对象来管理。
也就是说,obs实现wasapi的关键代码,看class WASAPISource 类即可。
一 obs的class WASAPISource 介绍
1. 控制采集输入源
WASAPISource类支持采集扬声器和麦克风。通过以下枚举进行控制:
enum class SourceType {
Input,
DeviceOutput,
ProcessOutput,
};
其中:
● Input:代表采集麦克风。
● DeviceOutput:采集电脑的扬声器。
● ProcessOutput:暂未研究。可以先忽略。
在下面分析obs的WASAPISource类时,我们以采集扬声器即SourceType=DeviceOutput,并且以线程采集为例进行讲解。
2. WASAPISource的构造函数
WASAPISource的构造函数会进行对应的初始化,所以如果我们想知道obs如何初始化wasapi插件的话,可以在WASAPISource的构造函数加个断点。
下面我们开始对WASAPISource的构造函数逻辑进行说明。
- 通过windows的CreateEvent函数,创建相关事件,用于初始化、接收数据、重连、停止、退出等等。
- CoCreateInstance()枚举音频设备。
- 判断windows系统是否支持rtwq队列处理。若支持,rtwq_supported=true,则会使用rtwq的相关接口进行音频采集;若不支持,会开启一个线程进行音频采集,线程处理函数是WASAPISource::CaptureThread()。对于rtwq队列采集和线程采集,前者微软进行优化过,效果更好,但是需要更高的windows版本才能支持,而线程则兼容性更好。这里我们就只分析线程采集就行,因为这样会更兼容不同版本的windows。
- 开启Start()。
WASAPISource::WASAPISource(obs_data_t *settings, obs_source_t *source_,
SourceType type)
: source(source_),
sourceType(type),
startCapture(this),
sampleReady(this),
restart(this)
{
// 加载相关动态库接口
mmdevapi_module = LoadLibrary(L"Mmdevapi");
if (mmdevapi_module) {
activate_audio_interface_async =
(PFN_ActivateAudioInterfaceAsync)GetProcAddress(
mmdevapi_module, "ActivateAudioInterfaceAsync");
}
/* 参数设置相关, 可以替换为自己的参数 */
UpdateSettings(BuildUpdateParams(settings));
LogSettings();
// 创建相关事件,用于初始化、接收数据、重连、停止、退出等等。
idleSignal = CreateEvent(nullptr, true, false, nullptr);
if (!idleSignal.Valid())
throw "Could not create idle signal";
// 省略相关事件的创建...
notify = new WASAPINotify(this);
if (!notify)
throw "Could not create WASAPINotify";
// 枚举音频设备
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,
CLSCTX_ALL,
IID_PPV_ARGS(enumerator.Assign()));
if (FAILED(hr))
throw HRError("Failed to create enumerator", hr);
hr = enumerator->RegisterEndpointNotificationCallback(notify);
if (FAILED(hr))
throw HRError("Failed to register endpoint callback", hr);
/* OBS will already load DLL on startup if it exists */
const HMODULE rtwq_module = GetModuleHandle(L"RTWorkQ.dll");
// while RTWQ was introduced in Win 8.1, it silently fails
// to capture Desktop Audio for some reason. Disable for now.
struct win_version_info win1703 = {
}