obs系列文章入口:https://blog.csdn.net/qq_33844311/article/details/121479224
windows平台下obs官方自带的音频插件只有一个 win-wasapi ,负责采集扬声器和麦克风的声音。
这里推荐一个非官方的音频捕获插件 win-audio-capture,可以采集指定进程的音频输入到obs。
obs论坛:https://obsproject.com/forum/threads/win-capture-audio.147240/
项目地址:https://github.com/bozbez/win-capture-audio
这个插件的使用对 win10 版本有最低要求 Windows 10 2004 (released 2020-05-27) or later.
视频介绍:https://www.bilibili.com/video/av676469967
obs音频采集插件在程序启动的时候就创建好了,包括扬声器声音捕获 wasapi_output_capture 和麦克风声音捕获 wasapi_input_capture 。两者管理都是通过 class WASAPISource 对象来管理。
以下是创建音频插件的调用堆栈。
> win-wasapi.dll!CreateWASAPISource(obs_data * settings, obs_source * source, bool input) 行 1044 C++
win-wasapi.dll!CreateWASAPIOutput(obs_data * settings, obs_source * source) 行 1062 C++
obs.dll!obs_source_create_internal(const char * id, const char * name, obs_data * settings, obs_data * hotkey_data, bool private, unsigned int last_obs_ver) 行 387 C
obs.dll!obs_source_create_set_last_ver(const char * id, const char * name, obs_data * settings, obs_data * hotkey_data, unsigned int last_obs_ver) 行 432 C
obs.dll!obs_load_source_type(obs_data * source_data) 行 1781 C
obs.dll!obs_load_source(obs_data * source_data) 行 1890 C
obs64.exe!LoadAudioDevice(const char * name, int channel, obs_data * parent) 行 747 C++
obs64.exe!OBSBasic::LoadData(obs_data * data, const char * file) 行 1026 C++
obs64.exe!OBSBasic::Load(const char * file) 行 970 C++
obs64.exe!OBSBasic::OBSInit() 行 1893 C++
obs64.exe!OBSApp::OBSInit() 行 1474 C++
obs64.exe!run_program(std::basic_fstream<char,std::char_traits<char>> & logFile, int argc, char * * argv) 行 2138 C++
obs64.exe!main(int argc, char * * argv) 行 2839 C++
obs64.exe!WinMain(HINSTANCE__ * __formal, HINSTANCE__ * __formal, char * __formal, int __formal) 行 97 C++
音频的捕获工作在 WASAPISource 对象的构造函数里的完成所有的初始化工作。还注册了默认音频设备切换的回调通知,win8以上系统使用了微软的实时调度工作队列相关api,微软声称,如果使用他们的API,它可以更好地安排音频。这个是我在提交日志里面看到。
对于不支持的系统创建了 WASAPISource::CaptureThread 音频采集队列来捕获windows的声音。
提交: 24d82062aecca2f702dbf1cd7a85e84036b24ccf [24d8206]
作者: jpark37 [email protected] 日期: 2021年9月26日 4:21:22
下面贴一下管理音频捕获对象的构造函数,通过注释的方式,分析一下创建过程。
WASAPISource::WASAPISource(obs_data_t *settings, obs_source_t *source_, bool input)
: source(source_),
isInputDevice(input), // true: 表示创建麦克风音频捕获 false:表示创建扬声器音频捕获
startCapture(this),
sampleReady(this),
restart(this)
{
// 获取音频设备id 是否使用设备时间 是否使用默认音频设备
UpdateSettings(settings);
// 各种event的创建
idleSignal = CreateEvent(nullptr, true, false, nullptr);
if (!idleSignal.Valid())
throw "Could not create idle signal";
stopSignal = CreateEvent(nullptr, true, false, nullptr);
if (!stopSignal.Valid())
throw "Could not create stop signal";
receiveSignal = CreateEvent(nullptr, false, false, nullptr);
if (!receiveSignal.Valid())
throw "Could not create receive signal";
restartSignal = CreateEvent(nullptr, true, false, nullptr);
if (!restartSignal.Valid())
throw "Could not create restart signal";
exitSignal = CreateEvent(nullptr, true, false, nullptr);
if (!exitSignal.Valid())
throw "Could not create exit signal";
initSignal = CreateEvent(nullptr, false, false, nullptr);
if (!initSignal.Valid())
throw "Could not create init signal";
reconnectSignal = CreateEvent(nullptr, false, false, nullptr);
if (!reconnectSignal.Valid())
throw "Could not create reconnect signal";
// 创建重新初始化音频采集的线程
reconnectThread = CreateThread(
nullptr, 0, WASAPISource::ReconnectThread, this, 0, nullptr);
if (!reconnectThread.Valid())
throw "Failed to create reconnect thread";
// 创建默认音频设备切换的通知对象 IMMNotificationClient
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 */
// 检测系统是否支持 实时工作队列新的api (win8及以上系统才支持)
// true:支持创建工作队列调度音频的捕获
// false:不支持则创建 CaptureThread 线程负责音频的捕获
const HMODULE rtwq_module = GetModuleHandle(L"RTWorkQ.dll");
rtwq_supported = rtwq_module != NULL;
if (rtwq_supported) {
rtwq_unlock_work_queue =
(PFN_RtwqUnlockWorkQueue)GetProcAddress(
rtwq_module, "RtwqUnlockWorkQueue");
rtwq_lock_shared_work_queue =
(PFN_RtwqLockSharedWorkQueue)GetProcAddress(
rtwq_module, "RtwqLockSharedWorkQueue");
rtwq_create_async_result =
(PFN_RtwqCreateAsyncResult)GetProcAddress(
rtwq_module, "RtwqCreateAsyncResult");
rtwq_put_work_item = (PFN_RtwqPutWorkItem)GetProcAddress(
rtwq_module, "RtwqPutWorkItem");
rtwq_put_waiting_work_item =
(PFN_RtwqPutWaitingWorkItem)GetProcAddress(
rtwq_module, "RtwqPutWaitingWorkItem");
hr = rtwq_create_async_result(nullptr, &startCapture, nullptr,
&startCaptureAsyncResult);
if (FAILED(hr)) {
enumerator->UnregisterEndpointNotificationCallback(
notify);
throw HRError(
"Could not create startCaptureAsyncResult", hr);
}
hr = rtwq_create_async_result(nullptr, &sampleReady, nullptr,
&sampleReadyAsyncResult);
if (FAILED(hr)) {
enumerator->UnregisterEndpointNotificationCallback(
notify);
throw HRError("Could not create sampleReadyAsyncResult",
hr);
}
hr = rtwq_create_async_result(nullptr, &restart, nullptr,
&restartAsyncResult);
if (FAILED(hr)) {
enumerator->UnregisterEndpointNotificationCallback(
notify);
throw HRError("Could not create restartAsyncResult",
hr);
}
DWORD taskId = 0;
DWORD id = 0;
hr = rtwq_lock_shared_work_queue(L"Capture", 0, &taskId, &id);
if (FAILED(hr)) {
enumerator->UnregisterEndpointNotificationCallback(
notify);
throw HRError("RtwqLockSharedWorkQueue failed", hr);
}
startCapture.SetQueueId(id);
sampleReady.SetQueueId(id);
restart.SetQueueId(id);
} else {
// =======创建 CaptureThread 线程负责音频的捕获 win7 操作系统走这个分支 ============
captureThread = CreateThread(nullptr, 0,
WASAPISource::CaptureThread, this,
0, nullptr);
if (!captureThread.Valid()) {
enumerator->UnregisterEndpointNotificationCallback(
notify);
throw "Failed to create capture thread";
}
}
// 开始音频捕获工作
Start();
}
下面的堆栈是使用微软的实时调度工作队列相关api的输出堆栈
obs_source_output_audio(source, &data); // obs的音频数据都是异步的输出
win-wasapi.dll!WASAPISource::ProcessCaptureData() 行 772 C++
win-wasapi.dll!WASAPISource::OnSampleReady() 行 965 C++
win-wasapi.dll!WASAPISource::CallbackSampleReady::Invoke(IRtwqAsyncResult * __formal) 行 141 C++
下面的堆栈是使用 CaptureThread 输出采集的音频数据
obs_source_output_audio(source, &data); // obs的音频数据都是异步的输出
win-wasapi.dll!WASAPISource::ProcessCaptureData() 行 775 C++
win-wasapi.dll!WASAPISource::CaptureThread(void * param) 行 863 C++
通过 obs_source_output_audio 输出到obs的音频队列里面,具体的音频的处理线程参考: audio_thread 音频编码线程
windows下的音频采集是基于 windows audio session 相关api实现的音频采集工作。具体的api使用和介绍,我就不班门弄斧了,参考微软的官方文档即可。
以上都是个人工作当中对obs-studio开源项目的理解,难免有错误的地方,如果有欢迎指出。
若有帮助幸甚。