Я хочу записать системный звук с помощью кольцевой записи WASAPI ( https://learn.microsoft.com/en-us/windo ... -recording ).
Я создал небольшое приложение, которое перечисляет доступные устройства и активные на них сеансы. Затем приложение записывает несколько секунд звука и сохраняет его в WAV-файл.
Кажется, это работает хорошо, но проблема возникает с Bluetooth-гарнитурами во время видеовызова.
Что работает
Запись с проводной гарнитуры
Запись с гарнитуры 2,4 ГГц
Запись с встроенный динамик ноутбука
Запись через Bluetooth-гарнитуру при использовании только динамика.
Что не получается
Когда Bluetooth-гарнитура используется как микрофон и динамик в видеовызове (например, вызов Chrome/WebRTC), петлевой захват на этом устройстве создает нулевые байты аудио, даже если счетчик сеанса показывает, что устройство имеет активный сеанс.
Что я вижу в своих журналах
Во время тестового звонка (Chrome на собрании) мой счетчик устройств рендеринга печатает что-то вроде этого:
Я вижу на нем активный сеанс (Chrome, через IAudioSessionManager2 /
IAudioSessionControl2).
Я успешно инициализирую петлевой захват именно в этой конечной точке.
Но в потоке обратной связи никакие данные не поступают (0) байт на всю
длительность).
IAudioCaptureClient::GetNextPacketSize последовательно возвращает 0, и
событие готовности к выборке никогда не сигнализируется.
Если я повторю тот же тест, используя:
Проводную гарнитуру или
A 2,4 ГГц USB-гарнитура или
Та же Bluetooth-гарнитура, но воспроизводит музыку/видео (без использования микрофона каким-либо приложением),
тогда работает петлевой захват, и я получаю действительные данные WAV.
Код: перечисление устройств и обнаружение сеанса
Вот как я перечисляю устройства и обнаруживаю активные сеансы:
// Set up event callback
HANDLE sampleReadyEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
audioClient->SetEventHandle(sampleReadyEvent);
// Get capture client
wil::com_ptr captureClient;
audioClient->GetService(__uuidof(IAudioCaptureClient),
(void**)captureClient.put());
// Start capture
audioClient->Start();
// Capture loop
while (capturing) {
DWORD waitResult = WaitForSingleObject(sampleReadyEvent, 1000);
// Process all available packets
UINT32 packetLength = 0;
while (SUCCEEDED(captureClient->GetNextPacketSize(&packetLength))
&& packetLength > 0) {
BYTE* pData = nullptr;
UINT32 numFrames = 0;
DWORD flags = 0;
hr = captureClient->GetBuffer(&pData, &numFrames, &flags,
nullptr, nullptr);
if (SUCCEEDED(hr)) {
UINT32 bytesToWrite = numFrames * mixFormat->nBlockAlign;
if (!(flags & AUDCLNT_BUFFERFLAGS_SILENT)) {
WriteFile(hFile, pData, bytesToWrite, &bytesWritten, nullptr);
totalBytes += bytesWritten; // Always 0 for Bluetooth during calls
}
captureClient->ReleaseBuffer(numFrames);
}
}
}
audioClient->Stop();
Что я уже пробовал
Подтвердил, что сеанс виден и активен через IAudioSessionManager2 на том же устройстве, которое я открываю для обратной связи.
Подтвердил, что устройство открыто в общем режиме (AUDCLNT_SHAREMODE_SHARED).
Отключен эксклюзивный режим как для воспроизведения, так и для записи в mmsys.cpl для этого устройства (без изменений).
Попробовал использовать конечную точку консоли по умолчанию, конечную точку связи по умолчанию и явно выбрал идентификатор устройства.
Проверено, что Initialize() и Start() возвращают S_OK — API не дают сбоя, они просто не предоставляют данных.
Проверено, что тот же код работает для:
Встроенных динамиков,
Проводные гарнитуры / USB / 2,4 ГГц.
Одна и та же Bluetooth-гарнитура при воспроизведении музыки (YouTube, Spotify) без использования микрофона.
Одна и та же Bluetooth-гарнитура воспроизводит звук вызова при использовании другого устройства в качестве микрофона.
Окружающая среда
Windows 10/11 (воспроизводится на нескольких машинах).
Современные Bluetooth-гарнитуры (например, OnePlus Buds 3, Razer BlackShark V2 HS BT, Anker Soundcore), которые в моем случае представлены как единая конечная точка звука в настройках звука Windows.
Вопросы
Есть ли способ захвата звука с Bluetooth-гарнитур во время двунаправленных вызовов использование шлейфа WASAPI или альтернативных API Windows?
Если нет, то является ли это ожидаемым поведением для гарнитур Bluetooth и шлейфа WASAPI?
Существует ли официальная документация Microsoft об этом ограничении?
Я хочу записать системный звук с помощью кольцевой записи WASAPI ( https://learn.microsoft.com/en-us/windows/win32/coreaudio/loopback-recording ). Я создал небольшое приложение, которое перечисляет доступные устройства и активные на них сеансы. Затем приложение записывает несколько секунд звука и сохраняет его в WAV-файл. Кажется, это работает хорошо, но проблема возникает с Bluetooth-гарнитурами во время видеовызова. Что работает [list] [*]Запись с проводной гарнитуры [*]Запись с гарнитуры 2,4 ГГц [*]Запись с встроенный динамик ноутбука [*]Запись через Bluetooth-гарнитуру при использовании только динамика. [/list] Что не получается Когда Bluetooth-гарнитура используется как микрофон и динамик в видеовызове (например, вызов Chrome/WebRTC), петлевой захват на этом устройстве создает нулевые байты аудио, даже если счетчик сеанса показывает, что устройство имеет активный сеанс. Что я вижу в своих журналах Во время тестового звонка (Chrome на собрании) мой счетчик устройств рендеринга печатает что-то вроде этого: [code]=== Render Devices === [0] Headphones (OnePlus Buds 3) : 0 active sessions ID: {0.0.0.00000000}.{19c0ada4-d680-46a9-919c-dca80bee9807} [1] Speakers (Realtek(R) Audio) : 0 active sessions ID: {0.0.0.00000000}.{357dc357-13e3-4cdb-90c9-ab54f23dedcf} [2] XG27ACS (Intel(R) Display Audio) : 0 active sessions ID: {0.0.0.00000000}.{4af2ff2a-e7bf-4a47-8de4-876e9ba2d6ce} [3] CABLE Input (VB-Audio Virtual Cable) : 0 active sessions ID: {0.0.0.00000000}.{65fbe74c-81c9-4c39-8a75-55033aa39f4c} [4] CABLE In 16ch (VB-Audio Virtual Cable) : 0 active sessions ID: {0.0.0.00000000}.{a2a0ed57-95c3-4689-b0ff-efabb8c3efe1} [5] Headphones (2- Razer BlackShark V2 HS BT) [Default: Console, Multimedia, Communications] : 1 active session ID: {0.0.0.00000000}.{dd3f0c1e-aa79-46ff-a3f2-528034df115d} - chrome.exe (PID: 19412)
[/code] Затем начинается запись для активного устройства: [code]=== Active Devices (1) === [0] Headphones (2- Razer BlackShark V2 HS BT) File: Headphones_2-_Razer_BlackShark_V2_HS_BT_13fa25c5.wav Sessions: 1 [0] Headphones (2- Razer BlackShark V2 HS BT) File: Headphones_2-_Razer_BlackShark_V2_HS_BT_13fa25c5.wav Mix format: 44100 Hz, 32-bit, 2 channels
=== Starting Captures === Recording from 1 device(s)...
[/code] Итак: [list] [*]Я вижу устройство Bluetooth-гарнитуры. [*]Я вижу на нем активный сеанс (Chrome, через IAudioSessionManager2 / IAudioSessionControl2). [*]Я успешно инициализирую петлевой захват именно в этой конечной точке. [*]Но в потоке обратной связи никакие данные не поступают (0) байт на всю длительность). [*]IAudioCaptureClient::GetNextPacketSize последовательно возвращает 0, и событие готовности к выборке никогда не сигнализируется. [/list] Если я повторю тот же тест, используя: [list] [*]Проводную гарнитуру или [*]A 2,4 ГГц USB-гарнитура или [*]Та же Bluetooth-гарнитура, но воспроизводит музыку/видео (без использования микрофона каким-либо приложением), [/list] тогда работает петлевой захват, и я получаю действительные данные WAV. Код: перечисление устройств и обнаружение сеанса Вот как я перечисляю устройства и обнаруживаю активные сеансы: [code]// Enumerate render devices wil::com_ptr deviceEnum; CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, IID_PPV_ARGS(deviceEnum.put()));
for (UINT i = 0; i < deviceCount; i++) { wil::com_ptr device; deviceCollection->Item(i, device.put());
// Get device ID wil::unique_cotaskmem_string deviceId; device->GetId(&deviceId);
// Get session manager to count active sessions wil::com_ptr sessionMgr; device->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, nullptr, (void**)sessionMgr.put());
if (state == AudioSessionStateActive) { activeSessions++; } }
// If device has active sessions, capture from it if (activeSessions > 0) { StartLoopbackCapture(deviceId.get()); } }
[/code] Как открыть петлевой захват Код захвата представляет собой стандартный петлевой контроль WASAPI: [list] [*]Получите IMMDevice по идентификатору с помощью IMMDeviceEnumerator::GetDevice.
[*]IMMDevice::Activate, чтобы получить IAudioClient.
[*]IAudioClient::GetMixFormat, чтобы получить формат микса устройства.
[*]IAudioClient::Initialize в общем режиме с флагами обратной связи: [code]hr = audioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, hnsBufferDuration, 0, mixFormat, nullptr);
[/code]
[/list] Мой цикл захвата: [code]// Set up event callback HANDLE sampleReadyEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); audioClient->SetEventHandle(sampleReadyEvent);
// Get capture client wil::com_ptr captureClient; audioClient->GetService(__uuidof(IAudioCaptureClient), (void**)captureClient.put());
if (!(flags & AUDCLNT_BUFFERFLAGS_SILENT)) { WriteFile(hFile, pData, bytesToWrite, &bytesWritten, nullptr); totalBytes += bytesWritten; // Always 0 for Bluetooth during calls }
captureClient->ReleaseBuffer(numFrames); } } }
audioClient->Stop();
[/code] Что я уже пробовал [list] [*]Подтвердил, что сеанс виден и активен через IAudioSessionManager2 на том же устройстве, которое я открываю для обратной связи. [*]Подтвердил, что устройство открыто в общем режиме (AUDCLNT_SHAREMODE_SHARED). [*]Отключен эксклюзивный режим как для воспроизведения, так и для записи в mmsys.cpl для этого устройства (без изменений). [*]Попробовал использовать конечную точку консоли по умолчанию, конечную точку связи по умолчанию и явно выбрал идентификатор устройства. [*]Проверено, что Initialize() и Start() возвращают S_OK — API не дают сбоя, они просто не предоставляют данных. [*]Проверено, что тот же код работает для: [*]Встроенных динамиков, [*]Проводные гарнитуры / USB / 2,4 ГГц. [*]Одна и та же Bluetooth-гарнитура при воспроизведении музыки (YouTube, Spotify) без использования микрофона. [*]Одна и та же Bluetooth-гарнитура воспроизводит звук вызова при использовании другого устройства в качестве микрофона. [/list] Окружающая среда [list] [*]Windows 10/11 (воспроизводится на нескольких машинах). [*]Современные Bluetooth-гарнитуры (например, OnePlus Buds 3, Razer BlackShark V2 HS BT, Anker Soundcore), которые в моем случае представлены как единая конечная точка звука в настройках звука Windows. [/list] Вопросы [list] [*]Есть ли способ захвата звука с Bluetooth-гарнитур во время двунаправленных вызовов использование шлейфа WASAPI или альтернативных API Windows? [*]Если нет, то является ли это ожидаемым поведением для гарнитур Bluetooth и шлейфа WASAPI? [*]Существует ли официальная документация Microsoft об этом ограничении? [/list]