使用 WASAPI 捕获蓝牙音频数据

Posted

技术标签:

【中文标题】使用 WASAPI 捕获蓝牙音频数据【英文标题】:capturing bluetooth audio data with WASAPI 【发布时间】:2021-02-15 00:20:00 【问题描述】:

我正在做一个小项目,我需要混合来自我的内置麦克风和蓝牙耳机 (Bose) 的传入数据。我以为我会使用 WASAPI。然而,虽然我可以很容易地读出我的内置麦克风,但我的蓝牙耳机却不是这样。 我几乎完全按照文档给出的示例进行了操作,但是我做了一个小改动,以便能够使用他们提供的 ID 选择我自己的“输入设备”(内置麦克风或耳机)。

方法如下:

HRESULT RecordAudiostreamBLE(MyAudioSink *pMySink, LPWSTR pwszID)


    HRESULT hr;
    REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
    REFERENCE_TIME hnsActualDuration;
    UINT32 bufferFrameCount;
    UINT32 numFramesAvailable;
    IMMDeviceEnumerator *pEnumerator = NULL;
    IMMDevice *pDevice = NULL;
    IAudioClient *pAudioClient = NULL;
    IAudioCaptureClient *pCaptureClient = NULL;
    WAVEFORMATEX *pwfx = NULL;
    UINT32 packetLength = 0;
    BOOL bDone = FALSE;
    BYTE *pData;
    DWORD flags;

    hr = CoInitialize(0);

    hr = CoCreateInstance(
           CLSID_MMDeviceEnumerator, NULL,
           CLSCTX_ALL, IID_IMMDeviceEnumerator,
           (void**)&pEnumerator);
    EXIT_ON_ERROR(hr)

    hr = pEnumerator->GetDevice(pwszID, &pDevice);
    EXIT_ON_ERROR(hr)

    hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL,
                          NULL, (void**)&pAudioClient);
    EXIT_ON_ERROR(hr)

    hr = pAudioClient->GetMixFormat(&pwfx);
    EXIT_ON_ERROR(hr)

    hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
                                  0, hnsRequestedDuration,
                                  0, pwfx, NULL);
    EXIT_ON_ERROR(hr)

    // Get the size of the allocated buffer.
    hr = pAudioClient->GetBufferSize(&bufferFrameCount);
    EXIT_ON_ERROR(hr)

    hr = pAudioClient->GetService(IID_IAudioCaptureClient,
                                  (void**)&pCaptureClient);
    EXIT_ON_ERROR(hr)

    // Notify the audio sink which format to use.
    hr = pMySink->SetFormat(pwfx);
    EXIT_ON_ERROR(hr)

    // Calculate the actual duration of the allocated buffer.
    hnsActualDuration = (double)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;

    hr = pAudioClient->Start();  // Start recording.
    EXIT_ON_ERROR(hr)

    // Each loop fills about half of the shared buffer.
    while (bDone == FALSE)
    
        // Sleep for half the buffer duration.
        Sleep(hnsActualDuration/REFTIMES_PER_MILLISEC/2);

        hr = pCaptureClient->GetNextPacketSize(&packetLength);
        EXIT_ON_ERROR(hr)

        printf("packet size = %d\n", packetLength);
        while (packetLength != 0)
        
            // Get the available data in the shared buffer.
            hr = pCaptureClient->GetBuffer( &pData,
                                            &numFramesAvailable,
                                            &flags, NULL, NULL);
            EXIT_ON_ERROR(hr)

            if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
            
                pData = NULL;  // Tell CopyData to write silence.
            

            // Copy the available capture data to the audio sink.
            hr = pMySink->CopyData(pData, numFramesAvailable, &bDone);
            EXIT_ON_ERROR(hr)

            hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
            EXIT_ON_ERROR(hr)

            hr = pCaptureClient->GetNextPacketSize(&packetLength);
            EXIT_ON_ERROR(hr)
        
    

    hr = pAudioClient->Stop();  // Stop recording.
    EXIT_ON_ERROR(hr)

Exit:
    printf("%s\n", hr);
    CoTaskMemFree(pwfx);
    SAFE_RELEASE(pEnumerator)
    SAFE_RELEASE(pDevice)
    SAFE_RELEASE(pAudioClient)
    SAFE_RELEASE(pCaptureClient)

    return hr;

使用内置麦克风的 ID 执行此操作时,一切正常。但是,如果使用蓝牙设备的 ID,我会得到以下输出:(我首先列出所有活动设备并在终端中选择 ID:

Endpoint 0: "S24D330 (Intel(R) Display Audio)" (0.0.0.00000000.0f483f83-6e29-482e-94b5-fb9cc257a03d)
Endpoint 1: "Hoofdtelefoon (LE-My headphone Hands-Free AG Audio)" (0.0.0.00000000.75c8e0c3-2e44-4538-940d-f8c2ae6424ca)
Endpoint 2: "Hoofdtelefoon (My headphone Stereo)" (0.0.0.00000000.849b4cc2-ed72-4d40-9725-d1ce6b4abfa0)
Endpoint 3: "Luidsprekers (2- High Definition Audio Device)" (0.0.0.00000000.cb8d7625-257e-4fd0-84b8-26de6aeb1e1b)
Endpoint 4: "Microfoon (2- High Definition Audio Device)" (0.0.1.00000000.5edec961-7a46-4554-bdcd-43fb7d9a9d9a)
Endpoint 5: "Hoofdtelefoon (LE-My headphone Hands-Free AG Audio)" (0.0.1.00000000.f937a0fa-1475-495b-81be-7aec0c1c7ea5)
Which input would you like?5
samples per second 16000
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
^C

数据包大小以上述方法在循环中打印。表明每次的数据包大小都是0。 有谁知道如何解决这个问题,并从中获取“常规”数据? 我可能需要使用不同的 API 吗?然而,速度是关键。

致以诚挚的问候

【问题讨论】:

【参考方案1】:

许多蓝牙耳机同时支持用于立体声播放的 A2DP 配置文件和用于双向单声道通信的免提协议,但不能同时支持。

如果其中一个协议处于活动状态,另一个将停止。

我怀疑您的问题是 A2DP 端点“Hoofdtelefoon(我的耳机立体声)”正在播放某些东西,这导致两个 HF 端点上的所有活动都停止。

【讨论】:

以上是关于使用 WASAPI 捕获蓝牙音频数据的主要内容,如果未能解决你的问题,请参考以下文章

WASAPI 在 Windows 上捕获的缓冲区大小

修改系统卷时如何修改 WASAPI 环回捕获卷?

渲染音频流(WASAPI / WINAPI)

WASAPI 捕获的数据包不对齐

WASAPI + windows 商店应用初始化

在 ffmpeg 中使用蓝牙耳机设备作为音频源