wasapi_loopback_capturer.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. #include "wasapi_loopback_capturer.h"
  2. #include <audioclient.h>
  3. #include <comdef.h>
  4. #include <mmdeviceapi.h>
  5. #include <windows.h>
  6. #include <QDebug>
  7. #define DEFAULT_SAMPLE_RATE 48000 // 默认采样率:48kHz
  8. #define DEFAULT_BITS_PER_SAMPLE 16 // 默认位深:16bit
  9. #define DEFAULT_CHANNELS 1 // 默认音频通道数:1
  10. #undef min
  11. class WASAPILoopbackCapturerPrivate
  12. {
  13. public:
  14. WASAPILoopbackCapturerPrivate() { CoInitialize(nullptr); }
  15. ~WASAPILoopbackCapturerPrivate()
  16. {
  17. if (pwfx)
  18. CoTaskMemFree(pwfx);
  19. if (pCaptureClient)
  20. pCaptureClient->Release();
  21. if (pAudioClient)
  22. pAudioClient->Release();
  23. if (pDevice)
  24. pDevice->Release();
  25. if (pEnumerator)
  26. pEnumerator->Release();
  27. CoUninitialize();
  28. }
  29. bool init()
  30. {
  31. HRESULT hr;
  32. hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
  33. nullptr,
  34. CLSCTX_ALL,
  35. __uuidof(IMMDeviceEnumerator),
  36. (void**) &pEnumerator);
  37. if (FAILED(hr)) {
  38. qDebug() << "Failed to create MMDeviceEnumerator";
  39. return false;
  40. }
  41. hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
  42. if (FAILED(hr)) {
  43. qDebug() << "Failed to get default audio endpoint";
  44. return false;
  45. }
  46. hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**) &pAudioClient);
  47. if (FAILED(hr)) {
  48. qDebug() << "Failed to activate audio client";
  49. return false;
  50. }
  51. return true;
  52. }
  53. // 检查并获取首选格式
  54. bool getPreferredFormat() {
  55. HRESULT hr = pAudioClient->GetMixFormat(&pwfx);
  56. if (FAILED(hr)) {
  57. qDebug() << "Failed to GetMixFormat, HRESULT:" << QString::number(hr, 16);
  58. return false;
  59. }
  60. qDebug() << "Audio Format Info:";
  61. qDebug() << " Sample Rate:" << pwfx->nSamplesPerSec;
  62. qDebug() << " Channels:" << pwfx->nChannels;
  63. qDebug() << " Bits Per Sample:" << pwfx->wBitsPerSample;
  64. qDebug() << " Format Tag:" << pwfx->wFormatTag;
  65. qDebug() << " Block Align:" << pwfx->nBlockAlign;
  66. qDebug() << " Avg Bytes Per Sec:" << pwfx->nAvgBytesPerSec;
  67. return true;
  68. }
  69. // 初始化音频客户端
  70. bool initializeAudioClient()
  71. {
  72. AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
  73. DWORD streamFlags = AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
  74. | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
  75. REFERENCE_TIME hnsBufferDuration = 0;
  76. HRESULT hr
  77. = pAudioClient->Initialize(shareMode, streamFlags, hnsBufferDuration, 0, pwfx, nullptr);
  78. if (FAILED(hr)) {
  79. qDebug() << "Failed to initialize audio client, HRESULT:" << QString::number(hr, 16);
  80. return false;
  81. }
  82. qDebug() << "Audio client initialized successfully";
  83. return true;
  84. }
  85. bool setupCaptureClient()
  86. {
  87. if (!pAudioClient) {
  88. qDebug() << "Audio client is null, cannot get capture client";
  89. return false;
  90. }
  91. HRESULT hr = pAudioClient->GetService(__uuidof(IAudioCaptureClient), (void**) &pCaptureClient);
  92. if (FAILED(hr)) {
  93. qDebug() << "Failed to get capture client, HRESULT:" << QString::number(hr, 16)
  94. << "AudioClient:" << pAudioClient << "CaptureClient:" << pCaptureClient;
  95. return false;
  96. }
  97. qDebug() << "Capture client obtained successfully:" << pCaptureClient;
  98. return true;
  99. }
  100. bool startCapture()
  101. {
  102. if (!pAudioClient) {
  103. qDebug() << "Audio client is null, cannot start capture";
  104. return false;
  105. }
  106. HRESULT hr = pAudioClient->Start();
  107. if (FAILED(hr)) {
  108. qDebug() << "Failed to start audio client, HRESULT:" << QString::number(hr, 16);
  109. return false;
  110. }
  111. qDebug() << "Audio client started successfully";
  112. return true;
  113. }
  114. void stopCapture()
  115. {
  116. if (pAudioClient) {
  117. pAudioClient->Stop();
  118. }
  119. }
  120. // 获取音频格式并设置到AudioFormat结构中
  121. bool setupAudioFormat(AudioFormat& audioFormat)
  122. {
  123. if (!pwfx) {
  124. return false;
  125. }
  126. // 设置音频格式
  127. audioFormat.sampleRate = pwfx->nSamplesPerSec;
  128. audioFormat.channels = pwfx->nChannels;
  129. audioFormat.bitsPerSample = pwfx->wBitsPerSample;
  130. audioFormat.blockAlign = pwfx->nBlockAlign;
  131. audioFormat.avgBytesPerSec = pwfx->nAvgBytesPerSec;
  132. // 如果是浮点格式,保持32bit,让混音器处理格式转换
  133. if (pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
  134. qDebug() << "Keeping 32t float format for mixer processing";
  135. }
  136. return true;
  137. }
  138. // 处理音频数据
  139. int processAudioData(std::vector<char>& buffer, std::mutex& mutex)
  140. {
  141. UINT32 packetLength = 0;
  142. HRESULT hr = pCaptureClient->GetNextPacketSize(&packetLength);
  143. if (FAILED(hr)) {
  144. qDebug() << "Failed to get next packet size";
  145. return -1;
  146. }
  147. if (packetLength == 0) {
  148. return 0;
  149. }
  150. BYTE* pData = nullptr;
  151. UINT32 numFrames = 0;
  152. DWORD flags = 0;
  153. hr = pCaptureClient->GetBuffer(&pData, &numFrames, &flags, nullptr, nullptr);
  154. if (FAILED(hr)) {
  155. qDebug() << "Failed to get buffer";
  156. return -1;
  157. }
  158. int bytes = numFrames * pwfx->nBlockAlign;
  159. if (pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
  160. // 32bit浮点格式,直接传递原始数据,让混音器处理格式转换
  161. std::lock_guard<std::mutex> lock(mutex);
  162. // 限制缓冲区大小,避免内存无限增长
  163. const size_t maxBufferSize = 1024 * 1024; // 1MB
  164. int bytesToAdd = numFrames * pwfx->nBlockAlign;
  165. if (buffer.size() + bytesToAdd > maxBufferSize) {
  166. buffer.erase(buffer.begin(), buffer.begin() + (buffer.size() - bytesToAdd));
  167. }
  168. buffer.insert(buffer.end(),
  169. (char*) pData,
  170. (char*) pData + bytesToAdd);
  171. } else {
  172. // PCM 直接拷贝
  173. std::lock_guard<std::mutex> lock(mutex);
  174. // 限制缓冲区大小,避免内存无限增长
  175. const size_t maxBufferSize = 1024 * 1024; // 1MB
  176. int bytesToAdd = numFrames * pwfx->nBlockAlign;
  177. if (buffer.size() + bytesToAdd > maxBufferSize) {
  178. buffer.erase(buffer.begin(), buffer.begin() + (buffer.size() - bytesToAdd));
  179. }
  180. buffer.insert(buffer.end(),
  181. (char*) pData,
  182. (char*) pData + bytesToAdd);
  183. }
  184. pCaptureClient->ReleaseBuffer(numFrames);
  185. return bytes;
  186. }
  187. IMMDeviceEnumerator* pEnumerator = nullptr;
  188. IMMDevice* pDevice = nullptr;
  189. IAudioClient* pAudioClient = nullptr;
  190. IAudioCaptureClient* pCaptureClient = nullptr;
  191. WAVEFORMATEX* pwfx = nullptr;
  192. WAVEFORMATEXTENSIBLE _formatex;
  193. };
  194. WASAPILoopbackCapturer::WASAPILoopbackCapturer(QObject* parent)
  195. : QObject(parent)
  196. , d(new WASAPILoopbackCapturerPrivate)
  197. {
  198. }
  199. WASAPILoopbackCapturer::~WASAPILoopbackCapturer()
  200. {
  201. Stop();
  202. delete d;
  203. }
  204. bool WASAPILoopbackCapturer::Init(Type deviceType)
  205. {
  206. // 只支持扬声器
  207. if (deviceType != Type::Speaker)
  208. return false;
  209. if (!d->init()) {
  210. qDebug() << "Failed to initialize WASAPI components";
  211. return false;
  212. }
  213. // 总是获取首选格式,因为我们需要 pwfx 来初始化音频客户端
  214. if (!d->getPreferredFormat()) {
  215. qDebug() << "Failed to get preferred format";
  216. return false;
  217. }
  218. if (!d->initializeAudioClient()) {
  219. qDebug() << "Failed to initialize audio client";
  220. return false;
  221. }
  222. // 设置音频格式
  223. if (!d->setupAudioFormat(m_audioFormat)) {
  224. qDebug() << "Failed to setup audio format";
  225. return false;
  226. }
  227. qDebug() << "WASAPI Loopback Capturer initialized successfully";
  228. return true;
  229. }
  230. bool WASAPILoopbackCapturer::Start()
  231. {
  232. if (m_running)
  233. return false;
  234. if (!d->setupCaptureClient()) {
  235. return false;
  236. }
  237. if (!d->startCapture()) {
  238. return false;
  239. }
  240. m_running = true;
  241. m_captureThread = std::thread(&WASAPILoopbackCapturer::captureThreadFunc, this);
  242. return true;
  243. }
  244. void WASAPILoopbackCapturer::Stop()
  245. {
  246. if (!m_running)
  247. return;
  248. m_running = false;
  249. if (m_captureThread.joinable())
  250. m_captureThread.join();
  251. d->stopCapture();
  252. }
  253. const AudioFormat& WASAPILoopbackCapturer::GetFormat() const
  254. {
  255. return m_audioFormat;
  256. }
  257. int WASAPILoopbackCapturer::readAudioData(char* buf, int maxLen)
  258. {
  259. std::unique_lock<std::mutex> lock(m_mutex);
  260. // 按帧对齐读取,确保不会破坏音频帧
  261. int blockAlign = m_audioFormat.blockAlign;
  262. if (blockAlign <= 0) {
  263. return 0;
  264. }
  265. // 计算可以读取的完整帧数
  266. int availableFrames = m_buffer.size() / blockAlign;
  267. int requestedFrames = maxLen / blockAlign;
  268. int framesToRead = std::min(availableFrames, requestedFrames);
  269. if (framesToRead > 0) {
  270. int bytesToRead = framesToRead * blockAlign;
  271. memcpy(buf, m_buffer.data(), bytesToRead);
  272. m_buffer.erase(m_buffer.begin(), m_buffer.begin() + bytesToRead);
  273. return bytesToRead;
  274. }
  275. return 0;
  276. }
  277. void WASAPILoopbackCapturer::captureThreadFunc()
  278. {
  279. qDebug() << "WASAPI Loopback capture started successfully";
  280. while (m_running) {
  281. int result = d->processAudioData(m_buffer, m_mutex);
  282. if (result < 0) {
  283. qDebug() << "Error processing audio data";
  284. break;
  285. }
  286. if (result == 0) {
  287. Sleep(10);
  288. }
  289. }
  290. qDebug() << "WASAPI Loopback capture stopped";
  291. }