wasapi_loopback_capturer.cpp 10 KB

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