#include "wasapi_loopback_capturer.h" #include #include #include #include #include #define DEFAULT_SAMPLE_RATE 48000 // 默认采样率:48kHz #define DEFAULT_BITS_PER_SAMPLE 16 // 默认位深:16bit #define DEFAULT_CHANNELS 1 // 默认音频通道数:1 #undef min class WASAPILoopbackCapturerPrivate { public: WASAPILoopbackCapturerPrivate() { CoInitialize(nullptr); } ~WASAPILoopbackCapturerPrivate() { if (pwfx) CoTaskMemFree(pwfx); if (pCaptureClient) pCaptureClient->Release(); if (pAudioClient) pAudioClient->Release(); if (pDevice) pDevice->Release(); if (pEnumerator) pEnumerator->Release(); CoUninitialize(); } bool init() { HRESULT hr; hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**) &pEnumerator); if (FAILED(hr)) { qDebug() << "Failed to create MMDeviceEnumerator"; return false; } hr = pEnumerator->GetDefaultAudioEndpoint(eRender, 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 getPreferredFormat() { HRESULT hr = pAudioClient->GetMixFormat(&pwfx); if (FAILED(hr)) { qDebug() << "Failed to GetMixFormat, HRESULT:" << QString::number(hr, 16); return false; } qDebug() << "Audio Format Info:"; qDebug() << " Sample Rate:" << pwfx->nSamplesPerSec; qDebug() << " Channels:" << pwfx->nChannels; qDebug() << " Bits Per Sample:" << pwfx->wBitsPerSample; qDebug() << " Format Tag:" << pwfx->wFormatTag; qDebug() << " Block Align:" << pwfx->nBlockAlign; qDebug() << " Avg Bytes Per Sec:" << pwfx->nAvgBytesPerSec; return true; } // 初始化音频客户端 bool initializeAudioClient() { AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED; DWORD streamFlags = AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; 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& 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 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 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; }; WASAPILoopbackCapturer::WASAPILoopbackCapturer(QObject* parent) : QObject(parent) , d(new WASAPILoopbackCapturerPrivate) { } WASAPILoopbackCapturer::~WASAPILoopbackCapturer() { Stop(); delete d; } bool WASAPILoopbackCapturer::Init(Type deviceType) { // 只支持扬声器 if (deviceType != Type::Speaker) return false; if (!d->init()) { qDebug() << "Failed to initialize WASAPI components"; return false; } // 总是获取首选格式,因为我们需要 pwfx 来初始化音频客户端 if (!d->getPreferredFormat()) { qDebug() << "Failed to get preferred format"; return false; } if (!d->initializeAudioClient()) { qDebug() << "Failed to initialize audio client"; 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 (!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 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"; }