wasapi_loopback_capturer.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  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. #undef min
  8. class WASAPILoopbackCapturerPrivate
  9. {
  10. public:
  11. IAudioCapturer::Type deviceType;
  12. WASAPILoopbackCapturerPrivate()
  13. {
  14. CoInitialize(nullptr);
  15. }
  16. ~WASAPILoopbackCapturerPrivate()
  17. {
  18. cleanupSilencePlayer();
  19. if (pwfx)
  20. CoTaskMemFree(pwfx);
  21. if (pCaptureClient)
  22. pCaptureClient->Release();
  23. if (pAudioClient)
  24. pAudioClient->Release();
  25. if (pDevice)
  26. pDevice->Release();
  27. if (pEnumerator)
  28. pEnumerator->Release();
  29. CoUninitialize();
  30. }
  31. bool init(IAudioCapturer::Type type)
  32. {
  33. HRESULT hr;
  34. hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
  35. nullptr,
  36. CLSCTX_ALL,
  37. __uuidof(IMMDeviceEnumerator),
  38. (void**)&pEnumerator);
  39. if (FAILED(hr)) {
  40. qDebug() << "Failed to create MMDeviceEnumerator";
  41. return false;
  42. }
  43. deviceType = type;
  44. EDataFlow dataFlow = (deviceType == IAudioCapturer::Type::Speaker) ? eRender : eCapture;
  45. hr = pEnumerator->GetDefaultAudioEndpoint(dataFlow, eConsole, &pDevice);
  46. if (FAILED(hr)) {
  47. qDebug() << "Failed to get default audio endpoint";
  48. return false;
  49. }
  50. hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&pAudioClient);
  51. if (FAILED(hr)) {
  52. qDebug() << "Failed to activate audio client";
  53. return false;
  54. }
  55. return true;
  56. }
  57. // 初始化音频客户端
  58. bool initializeAudioClient()
  59. {
  60. AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
  61. DWORD streamFlags = AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
  62. | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
  63. if (deviceType == IAudioCapturer::Type::Speaker) {
  64. streamFlags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
  65. }
  66. REFERENCE_TIME hnsBufferDuration = 0;
  67. HRESULT hr
  68. = pAudioClient->Initialize(shareMode, streamFlags, hnsBufferDuration, 0, pwfx, nullptr);
  69. if (FAILED(hr)) {
  70. qDebug() << "Failed to initialize audio client, HRESULT:" << QString::number(hr, 16);
  71. return false;
  72. }
  73. qDebug() << "Audio client initialized successfully";
  74. return true;
  75. }
  76. bool setupCaptureClient()
  77. {
  78. if (!pAudioClient) {
  79. qDebug() << "Audio client is null, cannot get capture client";
  80. return false;
  81. }
  82. HRESULT hr = pAudioClient->GetService(__uuidof(IAudioCaptureClient), (void**) &pCaptureClient);
  83. if (FAILED(hr)) {
  84. qDebug() << "Failed to get capture client, HRESULT:" << QString::number(hr, 16)
  85. << "AudioClient:" << pAudioClient << "CaptureClient:" << pCaptureClient;
  86. return false;
  87. }
  88. qDebug() << "Capture client obtained successfully:" << pCaptureClient;
  89. return true;
  90. }
  91. bool startCapture()
  92. {
  93. if (!pAudioClient) {
  94. qDebug() << "Audio client is null, cannot start capture";
  95. return false;
  96. }
  97. HRESULT hr = pAudioClient->Start();
  98. if (FAILED(hr)) {
  99. qDebug() << "Failed to start audio client, HRESULT:" << QString::number(hr, 16);
  100. return false;
  101. }
  102. qDebug() << "Audio client started successfully";
  103. return true;
  104. }
  105. void stopCapture()
  106. {
  107. if (pAudioClient) {
  108. pAudioClient->Stop();
  109. }
  110. }
  111. // 获取音频格式并设置到AudioFormat结构中
  112. bool setupAudioFormat(AudioFormat& audioFormat)
  113. {
  114. if (!pwfx) {
  115. return false;
  116. }
  117. // 设置音频格式
  118. audioFormat.sampleRate = pwfx->nSamplesPerSec;
  119. audioFormat.channels = pwfx->nChannels;
  120. audioFormat.bitsPerSample = pwfx->wBitsPerSample;
  121. audioFormat.blockAlign = pwfx->nBlockAlign;
  122. audioFormat.avgBytesPerSec = pwfx->nAvgBytesPerSec;
  123. // 如果是浮点格式,保持32bit,让混音器处理格式转换
  124. if (pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
  125. qDebug() << "Keeping 32t float format for mixer processing";
  126. }
  127. return true;
  128. }
  129. // 处理音频数据
  130. int processAudioData(std::vector<char>& buffer, std::mutex& mutex)
  131. {
  132. UINT32 packetLength = 0;
  133. HRESULT hr = pCaptureClient->GetNextPacketSize(&packetLength);
  134. if (FAILED(hr)) {
  135. qDebug() << "Failed to get next packet size";
  136. return -1;
  137. }
  138. if (packetLength == 0) {
  139. return 0;
  140. }
  141. BYTE* pData = nullptr;
  142. UINT32 numFrames = 0;
  143. DWORD flags = 0;
  144. hr = pCaptureClient->GetBuffer(&pData, &numFrames, &flags, nullptr, nullptr);
  145. if (FAILED(hr)) {
  146. qDebug() << "Failed to get buffer";
  147. return -1;
  148. }
  149. int bytes = numFrames * pwfx->nBlockAlign;
  150. if (pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
  151. // 32bit浮点格式,直接传递原始数据,让混音器处理格式转换
  152. std::lock_guard<std::mutex> lock(mutex);
  153. // 限制缓冲区大小,避免内存无限增长
  154. const size_t maxBufferSize = 1024 * 1024; // 1MB
  155. int bytesToAdd = numFrames * pwfx->nBlockAlign;
  156. if (buffer.size() + bytesToAdd > maxBufferSize) {
  157. buffer.erase(buffer.begin(), buffer.begin() + (buffer.size() - bytesToAdd));
  158. }
  159. buffer.insert(buffer.end(),
  160. (char*) pData,
  161. (char*) pData + bytesToAdd);
  162. } else {
  163. // PCM 直接拷贝
  164. std::lock_guard<std::mutex> lock(mutex);
  165. // 限制缓冲区大小,避免内存无限增长
  166. const size_t maxBufferSize = 1024 * 1024; // 1MB
  167. int bytesToAdd = numFrames * pwfx->nBlockAlign;
  168. if (buffer.size() + bytesToAdd > maxBufferSize) {
  169. buffer.erase(buffer.begin(), buffer.begin() + (buffer.size() - bytesToAdd));
  170. }
  171. buffer.insert(buffer.end(),
  172. (char*) pData,
  173. (char*) pData + bytesToAdd);
  174. }
  175. pCaptureClient->ReleaseBuffer(numFrames);
  176. return bytes;
  177. }
  178. IMMDeviceEnumerator* pEnumerator = nullptr;
  179. IMMDevice* pDevice = nullptr;
  180. IAudioClient* pAudioClient = nullptr;
  181. IAudioCaptureClient* pCaptureClient = nullptr;
  182. WAVEFORMATEX* pwfx = nullptr;
  183. WAVEFORMATEXTENSIBLE _formatex;
  184. // 静音播放器相关成员
  185. std::atomic<bool> m_silencePlayerRunning{false};
  186. std::thread m_silencePlayerThread;
  187. IMMDevice* pSilenceDevice = nullptr;
  188. IAudioClient* pSilenceAudioClient = nullptr;
  189. IAudioRenderClient* pSilenceRenderClient = nullptr;
  190. // 静音播放器方法
  191. bool initializeSilencePlayer();
  192. void cleanupSilencePlayer();
  193. void silencePlayerThreadFunc();
  194. };
  195. WASAPILoopbackCapturer::WASAPILoopbackCapturer(QObject* parent)
  196. : QObject(parent)
  197. , d(new WASAPILoopbackCapturerPrivate)
  198. {
  199. }
  200. WASAPILoopbackCapturer::~WASAPILoopbackCapturer()
  201. {
  202. Stop();
  203. delete d;
  204. }
  205. bool WASAPILoopbackCapturer::Init(Type deviceType)
  206. {
  207. m_deviceType = deviceType;
  208. if (!d->init(deviceType)) {
  209. qDebug() << "Failed to initialize WASAPI components";
  210. return false;
  211. }
  212. // 尝试使用默认格式
  213. AudioFormat defaultFormat = IAudioCapturer::GetDefaultFormat();
  214. WAVEFORMATEX* formatToUse = (WAVEFORMATEX*)CoTaskMemAlloc(sizeof(WAVEFORMATEX));
  215. if (!formatToUse) {
  216. qDebug() << "Failed to allocate memory for WAVEFORMATEX";
  217. return false;
  218. }
  219. formatToUse->wFormatTag = WAVE_FORMAT_PCM;
  220. formatToUse->nChannels = defaultFormat.channels;
  221. formatToUse->nSamplesPerSec = defaultFormat.sampleRate;
  222. formatToUse->wBitsPerSample = defaultFormat.bitsPerSample;
  223. formatToUse->nBlockAlign = (formatToUse->nChannels * formatToUse->wBitsPerSample) / 8;
  224. formatToUse->nAvgBytesPerSec = formatToUse->nSamplesPerSec * formatToUse->nBlockAlign;
  225. formatToUse->cbSize = 0;
  226. d->pwfx = formatToUse;
  227. // 尝试使用默认格式初始化,如果失败则回退
  228. if (!d->initializeAudioClient()) {
  229. qWarning("Default audio format not supported, falling back to GetMixFormat.");
  230. CoTaskMemFree(d->pwfx);
  231. d->pwfx = nullptr;
  232. // 使用GetMixFormat获取系统首选格式
  233. HRESULT hr = d->pAudioClient->GetMixFormat(&d->pwfx);
  234. if (FAILED(hr)) {
  235. qDebug() << "Failed to GetMixFormat on fallback, HRESULT:" << QString::number(hr, 16);
  236. return false;
  237. }
  238. // 再次尝试初始化
  239. if (!d->initializeAudioClient()) {
  240. qDebug() << "Failed to initialize audio client on fallback.";
  241. CoTaskMemFree(d->pwfx);
  242. d->pwfx = nullptr;
  243. return false;
  244. }
  245. }
  246. // 设置音频格式
  247. if (!d->setupAudioFormat(m_audioFormat)) {
  248. qDebug() << "Failed to setup audio format";
  249. return false;
  250. }
  251. qDebug() << "WASAPI Loopback Capturer initialized successfully";
  252. return true;
  253. }
  254. bool WASAPILoopbackCapturer::Start()
  255. {
  256. if (m_running)
  257. return false;
  258. // 如果是扬声器设备,启动静音播放器确保音频引擎活跃
  259. if (m_deviceType == Type::Speaker) {
  260. if (!d->initializeSilencePlayer()) {
  261. qDebug() << "Failed to initialize silence player";
  262. return false;
  263. }
  264. }
  265. if (!d->setupCaptureClient()) {
  266. return false;
  267. }
  268. if (!d->startCapture()) {
  269. return false;
  270. }
  271. m_running = true;
  272. m_captureThread = std::thread(&WASAPILoopbackCapturer::captureThreadFunc, this);
  273. return true;
  274. }
  275. void WASAPILoopbackCapturer::Stop()
  276. {
  277. if (!m_running)
  278. return;
  279. m_running = false;
  280. if (m_captureThread.joinable())
  281. m_captureThread.join();
  282. d->stopCapture();
  283. }
  284. const AudioFormat& WASAPILoopbackCapturer::GetFormat() const
  285. {
  286. return m_audioFormat;
  287. }
  288. int WASAPILoopbackCapturer::readAudioData(char* buf, int maxLen)
  289. {
  290. std::unique_lock<std::mutex> lock(m_mutex);
  291. // 按帧对齐读取,确保不会破坏音频帧
  292. int blockAlign = m_audioFormat.blockAlign;
  293. if (blockAlign <= 0) {
  294. return 0;
  295. }
  296. // 计算可以读取的完整帧数
  297. int availableFrames = m_buffer.size() / blockAlign;
  298. int requestedFrames = maxLen / blockAlign;
  299. int framesToRead = std::min(availableFrames, requestedFrames);
  300. if (framesToRead > 0) {
  301. int bytesToRead = framesToRead * blockAlign;
  302. memcpy(buf, m_buffer.data(), bytesToRead);
  303. m_buffer.erase(m_buffer.begin(), m_buffer.begin() + bytesToRead);
  304. return bytesToRead;
  305. }
  306. return 0;
  307. }
  308. void WASAPILoopbackCapturer::captureThreadFunc()
  309. {
  310. qDebug() << "WASAPI Loopback capture started successfully";
  311. while (m_running) {
  312. int result = d->processAudioData(m_buffer, m_mutex);
  313. if (result < 0) {
  314. qDebug() << "Error processing audio data";
  315. break;
  316. }
  317. if (result == 0) {
  318. Sleep(10);
  319. }
  320. }
  321. qDebug() << "WASAPI Loopback capture stopped";
  322. }
  323. // 静音播放器实现
  324. bool WASAPILoopbackCapturerPrivate::initializeSilencePlayer()
  325. {
  326. HRESULT hr;
  327. // 获取默认音频渲染设备
  328. hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pSilenceDevice);
  329. if (FAILED(hr)) {
  330. qDebug() << "Failed to get default audio render endpoint for silence player";
  331. return false;
  332. }
  333. // 激活音频客户端
  334. hr = pSilenceDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&pSilenceAudioClient);
  335. if (FAILED(hr)) {
  336. qDebug() << "Failed to activate audio client for silence player";
  337. return false;
  338. }
  339. // 获取混音格式
  340. WAVEFORMATEX* pSilenceFormat = nullptr;
  341. hr = pSilenceAudioClient->GetMixFormat(&pSilenceFormat);
  342. if (FAILED(hr)) {
  343. qDebug() << "Failed to get mix format for silence player";
  344. return false;
  345. }
  346. // 初始化音频客户端(共享模式)
  347. hr = pSilenceAudioClient->Initialize(
  348. AUDCLNT_SHAREMODE_SHARED,
  349. 0,
  350. 10000000, // 1秒缓冲区
  351. 0,
  352. pSilenceFormat,
  353. nullptr
  354. );
  355. CoTaskMemFree(pSilenceFormat);
  356. if (FAILED(hr)) {
  357. qDebug() << "Failed to initialize audio client for silence player";
  358. return false;
  359. }
  360. // 获取渲染客户端
  361. hr = pSilenceAudioClient->GetService(__uuidof(IAudioRenderClient), (void**)&pSilenceRenderClient);
  362. if (FAILED(hr)) {
  363. qDebug() << "Failed to get render client for silence player";
  364. return false;
  365. }
  366. // 启动音频客户端
  367. hr = pSilenceAudioClient->Start();
  368. if (FAILED(hr)) {
  369. qDebug() << "Failed to start audio client for silence player";
  370. return false;
  371. }
  372. // 启动静音播放线程
  373. m_silencePlayerRunning = true;
  374. m_silencePlayerThread = std::thread(&WASAPILoopbackCapturerPrivate::silencePlayerThreadFunc, this);
  375. qDebug() << "Silence player initialized successfully";
  376. return true;
  377. }
  378. void WASAPILoopbackCapturerPrivate::cleanupSilencePlayer()
  379. {
  380. // 停止静音播放线程
  381. if (m_silencePlayerRunning) {
  382. m_silencePlayerRunning = false;
  383. if (m_silencePlayerThread.joinable()) {
  384. m_silencePlayerThread.join();
  385. }
  386. }
  387. // 清理 WASAPI 资源
  388. if (pSilenceAudioClient) {
  389. pSilenceAudioClient->Stop();
  390. pSilenceAudioClient->Release();
  391. pSilenceAudioClient = nullptr;
  392. }
  393. if (pSilenceRenderClient) {
  394. pSilenceRenderClient->Release();
  395. pSilenceRenderClient = nullptr;
  396. }
  397. if (pSilenceDevice) {
  398. pSilenceDevice->Release();
  399. pSilenceDevice = nullptr;
  400. }
  401. }
  402. void WASAPILoopbackCapturerPrivate::silencePlayerThreadFunc()
  403. {
  404. qDebug() << "Silence player thread started";
  405. UINT32 bufferFrameCount;
  406. HRESULT hr = pSilenceAudioClient->GetBufferSize(&bufferFrameCount);
  407. if (FAILED(hr)) {
  408. qDebug() << "Failed to get buffer size for silence player";
  409. return;
  410. }
  411. while (m_silencePlayerRunning) {
  412. UINT32 numFramesPadding;
  413. hr = pSilenceAudioClient->GetCurrentPadding(&numFramesPadding);
  414. if (FAILED(hr)) {
  415. qDebug() << "Failed to get current padding for silence player";
  416. break;
  417. }
  418. UINT32 numFramesAvailable = bufferFrameCount - numFramesPadding;
  419. if (numFramesAvailable > 0) {
  420. BYTE* pData;
  421. hr = pSilenceRenderClient->GetBuffer(numFramesAvailable, &pData);
  422. if (SUCCEEDED(hr)) {
  423. // 填充静音数据(全零)
  424. memset(pData, 0, numFramesAvailable * sizeof(float) * 2); // 假设立体声
  425. hr = pSilenceRenderClient->ReleaseBuffer(numFramesAvailable, 0);
  426. if (FAILED(hr)) {
  427. qDebug() << "Failed to release buffer for silence player";
  428. }
  429. }
  430. }
  431. Sleep(10); // 10ms 间隔
  432. }
  433. qDebug() << "Silence player thread stopped";
  434. }