| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513 |
- #include "wasapi_loopback_capturer.h"
- #include <audioclient.h>
- #include <comdef.h>
- #include <mmdeviceapi.h>
- #include <windows.h>
- #include <QDebug>
- #undef min
- class WASAPILoopbackCapturerPrivate
- {
- public:
- IAudioCapturer::Type deviceType;
- WASAPILoopbackCapturerPrivate()
- {
- CoInitialize(nullptr);
- }
- ~WASAPILoopbackCapturerPrivate()
- {
- cleanupSilencePlayer();
-
- if (pwfx)
- CoTaskMemFree(pwfx);
- if (pCaptureClient)
- pCaptureClient->Release();
- if (pAudioClient)
- pAudioClient->Release();
- if (pDevice)
- pDevice->Release();
- if (pEnumerator)
- pEnumerator->Release();
- CoUninitialize();
- }
- bool init(IAudioCapturer::Type type)
- {
- HRESULT hr;
- hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
- nullptr,
- CLSCTX_ALL,
- __uuidof(IMMDeviceEnumerator),
- (void**)&pEnumerator);
- if (FAILED(hr)) {
- qDebug() << "Failed to create MMDeviceEnumerator";
- return false;
- }
- deviceType = type;
- EDataFlow dataFlow = (deviceType == IAudioCapturer::Type::Speaker) ? eRender : eCapture;
- hr = pEnumerator->GetDefaultAudioEndpoint(dataFlow, eConsole, &pDevice);
- if (FAILED(hr)) {
- qDebug() << "Failed to get default audio endpoint";
- return false;
- }
- hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&pAudioClient);
- if (FAILED(hr)) {
- qDebug() << "Failed to activate audio client";
- return false;
- }
- return true;
- }
- // 初始化音频客户端
- bool initializeAudioClient()
- {
- AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
- DWORD streamFlags = AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
- | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
- if (deviceType == IAudioCapturer::Type::Speaker) {
- streamFlags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
- }
- REFERENCE_TIME hnsBufferDuration = 0;
- HRESULT hr
- = pAudioClient->Initialize(shareMode, streamFlags, hnsBufferDuration, 0, pwfx, nullptr);
- if (FAILED(hr)) {
- qDebug() << "Failed to initialize audio client, HRESULT:" << QString::number(hr, 16);
- return false;
- }
- qDebug() << "Audio client initialized successfully";
- return true;
- }
- bool setupCaptureClient()
- {
- if (!pAudioClient) {
- qDebug() << "Audio client is null, cannot get capture client";
- return false;
- }
-
- HRESULT hr = pAudioClient->GetService(__uuidof(IAudioCaptureClient), (void**) &pCaptureClient);
- if (FAILED(hr)) {
- qDebug() << "Failed to get capture client, HRESULT:" << QString::number(hr, 16)
- << "AudioClient:" << pAudioClient << "CaptureClient:" << pCaptureClient;
- return false;
- }
-
- qDebug() << "Capture client obtained successfully:" << pCaptureClient;
- return true;
- }
- bool startCapture()
- {
- if (!pAudioClient) {
- qDebug() << "Audio client is null, cannot start capture";
- return false;
- }
-
- HRESULT hr = pAudioClient->Start();
- if (FAILED(hr)) {
- qDebug() << "Failed to start audio client, HRESULT:" << QString::number(hr, 16);
- return false;
- }
-
- qDebug() << "Audio client started successfully";
- return true;
- }
- void stopCapture()
- {
- if (pAudioClient) {
- pAudioClient->Stop();
- }
- }
- // 获取音频格式并设置到AudioFormat结构中
- bool setupAudioFormat(AudioFormat& audioFormat)
- {
- if (!pwfx) {
- return false;
- }
- // 设置音频格式
- audioFormat.sampleRate = pwfx->nSamplesPerSec;
- audioFormat.channels = pwfx->nChannels;
- audioFormat.bitsPerSample = pwfx->wBitsPerSample;
- audioFormat.blockAlign = pwfx->nBlockAlign;
- audioFormat.avgBytesPerSec = pwfx->nAvgBytesPerSec;
- // 如果是浮点格式,保持32bit,让混音器处理格式转换
- if (pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
- qDebug() << "Keeping 32t float format for mixer processing";
- }
- return true;
- }
- // 处理音频数据
- int processAudioData(std::vector<char>& buffer, std::mutex& mutex)
- {
- UINT32 packetLength = 0;
- HRESULT hr = pCaptureClient->GetNextPacketSize(&packetLength);
- if (FAILED(hr)) {
- qDebug() << "Failed to get next packet size";
- return -1;
- }
-
- if (packetLength == 0) {
- return 0;
- }
-
- BYTE* pData = nullptr;
- UINT32 numFrames = 0;
- DWORD flags = 0;
- hr = pCaptureClient->GetBuffer(&pData, &numFrames, &flags, nullptr, nullptr);
- if (FAILED(hr)) {
- qDebug() << "Failed to get buffer";
- return -1;
- }
-
- int bytes = numFrames * pwfx->nBlockAlign;
- if (pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
- // 32bit浮点格式,直接传递原始数据,让混音器处理格式转换
- std::lock_guard<std::mutex> lock(mutex);
- // 限制缓冲区大小,避免内存无限增长
- const size_t maxBufferSize = 1024 * 1024; // 1MB
- int bytesToAdd = numFrames * pwfx->nBlockAlign;
- if (buffer.size() + bytesToAdd > maxBufferSize) {
- buffer.erase(buffer.begin(), buffer.begin() + (buffer.size() - bytesToAdd));
- }
- buffer.insert(buffer.end(),
- (char*) pData,
- (char*) pData + bytesToAdd);
- } else {
- // PCM 直接拷贝
- std::lock_guard<std::mutex> lock(mutex);
- // 限制缓冲区大小,避免内存无限增长
- const size_t maxBufferSize = 1024 * 1024; // 1MB
- int bytesToAdd = numFrames * pwfx->nBlockAlign;
- if (buffer.size() + bytesToAdd > maxBufferSize) {
- buffer.erase(buffer.begin(), buffer.begin() + (buffer.size() - bytesToAdd));
- }
- buffer.insert(buffer.end(),
- (char*) pData,
- (char*) pData + bytesToAdd);
- }
- pCaptureClient->ReleaseBuffer(numFrames);
- return bytes;
- }
- IMMDeviceEnumerator* pEnumerator = nullptr;
- IMMDevice* pDevice = nullptr;
- IAudioClient* pAudioClient = nullptr;
- IAudioCaptureClient* pCaptureClient = nullptr;
- WAVEFORMATEX* pwfx = nullptr;
- WAVEFORMATEXTENSIBLE _formatex;
-
- // 静音播放器相关成员
- std::atomic<bool> m_silencePlayerRunning{false};
- std::thread m_silencePlayerThread;
- IMMDevice* pSilenceDevice = nullptr;
- IAudioClient* pSilenceAudioClient = nullptr;
- IAudioRenderClient* pSilenceRenderClient = nullptr;
-
- // 静音播放器方法
- bool initializeSilencePlayer();
- void cleanupSilencePlayer();
- void silencePlayerThreadFunc();
- };
- WASAPILoopbackCapturer::WASAPILoopbackCapturer(QObject* parent)
- : QObject(parent)
- , d(new WASAPILoopbackCapturerPrivate)
- {
- }
- WASAPILoopbackCapturer::~WASAPILoopbackCapturer()
- {
- Stop();
- delete d;
- }
- bool WASAPILoopbackCapturer::Init(Type deviceType)
- {
- m_deviceType = deviceType;
- if (!d->init(deviceType)) {
- qDebug() << "Failed to initialize WASAPI components";
- return false;
- }
- // 尝试使用默认格式
- AudioFormat defaultFormat = IAudioCapturer::GetDefaultFormat();
- WAVEFORMATEX* formatToUse = (WAVEFORMATEX*)CoTaskMemAlloc(sizeof(WAVEFORMATEX));
- if (!formatToUse) {
- qDebug() << "Failed to allocate memory for WAVEFORMATEX";
- return false;
- }
- formatToUse->wFormatTag = WAVE_FORMAT_PCM;
- formatToUse->nChannels = defaultFormat.channels;
- formatToUse->nSamplesPerSec = defaultFormat.sampleRate;
- formatToUse->wBitsPerSample = defaultFormat.bitsPerSample;
- formatToUse->nBlockAlign = (formatToUse->nChannels * formatToUse->wBitsPerSample) / 8;
- formatToUse->nAvgBytesPerSec = formatToUse->nSamplesPerSec * formatToUse->nBlockAlign;
- formatToUse->cbSize = 0;
- d->pwfx = formatToUse;
- // 尝试使用默认格式初始化,如果失败则回退
- if (!d->initializeAudioClient()) {
- qWarning("Default audio format not supported, falling back to GetMixFormat.");
- CoTaskMemFree(d->pwfx);
- d->pwfx = nullptr;
- // 使用GetMixFormat获取系统首选格式
- HRESULT hr = d->pAudioClient->GetMixFormat(&d->pwfx);
- if (FAILED(hr)) {
- qDebug() << "Failed to GetMixFormat on fallback, HRESULT:" << QString::number(hr, 16);
- return false;
- }
- // 再次尝试初始化
- if (!d->initializeAudioClient()) {
- qDebug() << "Failed to initialize audio client on fallback.";
- CoTaskMemFree(d->pwfx);
- d->pwfx = nullptr;
- return false;
- }
- }
- // 设置音频格式
- if (!d->setupAudioFormat(m_audioFormat)) {
- qDebug() << "Failed to setup audio format";
- return false;
- }
- qDebug() << "WASAPI Loopback Capturer initialized successfully";
- return true;
- }
- bool WASAPILoopbackCapturer::Start()
- {
- if (m_running)
- return false;
-
- // 如果是扬声器设备,启动静音播放器确保音频引擎活跃
- if (m_deviceType == Type::Speaker) {
- if (!d->initializeSilencePlayer()) {
- qDebug() << "Failed to initialize silence player";
- return false;
- }
- }
-
- if (!d->setupCaptureClient()) {
- return false;
- }
-
- if (!d->startCapture()) {
- return false;
- }
-
- m_running = true;
- m_captureThread = std::thread(&WASAPILoopbackCapturer::captureThreadFunc, this);
- return true;
- }
- void WASAPILoopbackCapturer::Stop()
- {
- if (!m_running)
- return;
- m_running = false;
- if (m_captureThread.joinable())
- m_captureThread.join();
- d->stopCapture();
- }
- const AudioFormat& WASAPILoopbackCapturer::GetFormat() const
- {
- return m_audioFormat;
- }
- int WASAPILoopbackCapturer::readAudioData(char* buf, int maxLen)
- {
- std::unique_lock<std::mutex> lock(m_mutex);
-
- // 按帧对齐读取,确保不会破坏音频帧
- int blockAlign = m_audioFormat.blockAlign;
- if (blockAlign <= 0) {
- return 0;
- }
-
- // 计算可以读取的完整帧数
- int availableFrames = m_buffer.size() / blockAlign;
- int requestedFrames = maxLen / blockAlign;
- int framesToRead = std::min(availableFrames, requestedFrames);
-
- if (framesToRead > 0) {
- int bytesToRead = framesToRead * blockAlign;
- memcpy(buf, m_buffer.data(), bytesToRead);
- m_buffer.erase(m_buffer.begin(), m_buffer.begin() + bytesToRead);
- return bytesToRead;
- }
-
- return 0;
- }
- void WASAPILoopbackCapturer::captureThreadFunc()
- {
- qDebug() << "WASAPI Loopback capture started successfully";
- while (m_running) {
- int result = d->processAudioData(m_buffer, m_mutex);
- if (result < 0) {
- qDebug() << "Error processing audio data";
- break;
- }
- if (result == 0) {
- Sleep(10);
- }
- }
- qDebug() << "WASAPI Loopback capture stopped";
- }
- // 静音播放器实现
- bool WASAPILoopbackCapturerPrivate::initializeSilencePlayer()
- {
- HRESULT hr;
-
- // 获取默认音频渲染设备
- hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pSilenceDevice);
- if (FAILED(hr)) {
- qDebug() << "Failed to get default audio render endpoint for silence player";
- return false;
- }
-
- // 激活音频客户端
- hr = pSilenceDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&pSilenceAudioClient);
- if (FAILED(hr)) {
- qDebug() << "Failed to activate audio client for silence player";
- return false;
- }
-
- // 获取混音格式
- WAVEFORMATEX* pSilenceFormat = nullptr;
- hr = pSilenceAudioClient->GetMixFormat(&pSilenceFormat);
- if (FAILED(hr)) {
- qDebug() << "Failed to get mix format for silence player";
- return false;
- }
-
- // 初始化音频客户端(共享模式)
- hr = pSilenceAudioClient->Initialize(
- AUDCLNT_SHAREMODE_SHARED,
- 0,
- 10000000, // 1秒缓冲区
- 0,
- pSilenceFormat,
- nullptr
- );
-
- CoTaskMemFree(pSilenceFormat);
-
- if (FAILED(hr)) {
- qDebug() << "Failed to initialize audio client for silence player";
- return false;
- }
-
- // 获取渲染客户端
- hr = pSilenceAudioClient->GetService(__uuidof(IAudioRenderClient), (void**)&pSilenceRenderClient);
- if (FAILED(hr)) {
- qDebug() << "Failed to get render client for silence player";
- return false;
- }
-
- // 启动音频客户端
- hr = pSilenceAudioClient->Start();
- if (FAILED(hr)) {
- qDebug() << "Failed to start audio client for silence player";
- return false;
- }
-
- // 启动静音播放线程
- m_silencePlayerRunning = true;
- m_silencePlayerThread = std::thread(&WASAPILoopbackCapturerPrivate::silencePlayerThreadFunc, this);
-
- qDebug() << "Silence player initialized successfully";
- return true;
- }
- void WASAPILoopbackCapturerPrivate::cleanupSilencePlayer()
- {
- // 停止静音播放线程
- if (m_silencePlayerRunning) {
- m_silencePlayerRunning = false;
- if (m_silencePlayerThread.joinable()) {
- m_silencePlayerThread.join();
- }
- }
-
- // 清理 WASAPI 资源
- if (pSilenceAudioClient) {
- pSilenceAudioClient->Stop();
- pSilenceAudioClient->Release();
- pSilenceAudioClient = nullptr;
- }
-
- if (pSilenceRenderClient) {
- pSilenceRenderClient->Release();
- pSilenceRenderClient = nullptr;
- }
-
- if (pSilenceDevice) {
- pSilenceDevice->Release();
- pSilenceDevice = nullptr;
- }
- }
- void WASAPILoopbackCapturerPrivate::silencePlayerThreadFunc()
- {
- qDebug() << "Silence player thread started";
-
- UINT32 bufferFrameCount;
- HRESULT hr = pSilenceAudioClient->GetBufferSize(&bufferFrameCount);
- if (FAILED(hr)) {
- qDebug() << "Failed to get buffer size for silence player";
- return;
- }
-
- while (m_silencePlayerRunning) {
- UINT32 numFramesPadding;
- hr = pSilenceAudioClient->GetCurrentPadding(&numFramesPadding);
- if (FAILED(hr)) {
- qDebug() << "Failed to get current padding for silence player";
- break;
- }
-
- UINT32 numFramesAvailable = bufferFrameCount - numFramesPadding;
-
- if (numFramesAvailable > 0) {
- BYTE* pData;
- hr = pSilenceRenderClient->GetBuffer(numFramesAvailable, &pData);
- if (SUCCEEDED(hr)) {
- // 填充静音数据(全零)
- memset(pData, 0, numFramesAvailable * sizeof(float) * 2); // 假设立体声
- hr = pSilenceRenderClient->ReleaseBuffer(numFramesAvailable, 0);
- if (FAILED(hr)) {
- qDebug() << "Failed to release buffer for silence player";
- }
- }
- }
-
- Sleep(10); // 10ms 间隔
- }
-
- qDebug() << "Silence player thread stopped";
- }
|