DxgiCapturer.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. #include "DxgiCapturer.h"
  2. #include "basic/basic.h"
  3. #include "basic/frame.h"
  4. #include "d3d/buffer_filler.h"
  5. #include "d3d/convert.h"
  6. #include "d3d/gen_frame.h"
  7. #include "../finder.h"
  8. #include <cassert>
  9. #include <d3d11.h>
  10. #include <dxgi1_2.h>
  11. #include <QDebug>
  12. #include <mutex>
  13. namespace avrecorder {
  14. namespace video {
  15. // --- DxgiCapturerPrivate 实现 ---
  16. bool DxgiCapturerPrivate::Open(int left, int top, int width, int height) {
  17. std::lock_guard<std::mutex> lock(_deviceMutex);
  18. CloseInternal(); // 使用内部方法避免死锁
  19. HRESULT hr = S_OK;
  20. _isAttached = false;
  21. // 重置初始化状态,允许重新初始化
  22. _bInit = false;
  23. // Driver types supported
  24. D3D_DRIVER_TYPE DriverTypes[] = {
  25. D3D_DRIVER_TYPE_HARDWARE,
  26. D3D_DRIVER_TYPE_WARP,
  27. D3D_DRIVER_TYPE_REFERENCE,
  28. };
  29. UINT NumDriverTypes = ARRAYSIZE(DriverTypes);
  30. // Feature levels supported
  31. D3D_FEATURE_LEVEL FeatureLevels[] = {
  32. D3D_FEATURE_LEVEL_11_0,
  33. D3D_FEATURE_LEVEL_10_1,
  34. D3D_FEATURE_LEVEL_10_0,
  35. D3D_FEATURE_LEVEL_9_1};
  36. UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);
  37. D3D_FEATURE_LEVEL FeatureLevel;
  38. // Create D3D device
  39. for (UINT DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex) {
  40. hr = D3D11CreateDevice(nullptr,
  41. DriverTypes[DriverTypeIndex],
  42. nullptr,
  43. 0,
  44. FeatureLevels,
  45. NumFeatureLevels,
  46. D3D11_SDK_VERSION,
  47. &_hDevice,
  48. &FeatureLevel,
  49. &_hContext);
  50. if (SUCCEEDED(hr)) {
  51. break;
  52. }
  53. }
  54. if (FAILED(hr)) {
  55. __DebugPrint("D3D11CreateDevice failed: 0x%08lx", (unsigned long)hr);
  56. return false;
  57. }
  58. // Get DXGI device
  59. IDXGIDevice* hDxgiDevice = nullptr;
  60. hr = _hDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&hDxgiDevice));
  61. if (FAILED(hr)) {
  62. __DebugPrint("QueryInterface IDXGIDevice failed: 0x%08lx", (unsigned long)hr);
  63. Free(_hContext, [this]{ _hContext->Release(); });
  64. Free(_hDevice, [this]{ _hDevice->Release(); });
  65. return false;
  66. }
  67. // Get DXGI adapter
  68. IDXGIAdapter* hDxgiAdapter = nullptr;
  69. hr = hDxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&hDxgiAdapter));
  70. Free(hDxgiDevice, [=] { hDxgiDevice->Release(); });
  71. if (FAILED(hr)) {
  72. __DebugPrint("GetParent IDXGIAdapter failed: 0x%08lx", (unsigned long)hr);
  73. Free(_hContext, [this]{ _hContext->Release(); });
  74. Free(_hDevice, [this]{ _hDevice->Release(); });
  75. return false;
  76. }
  77. // Get output
  78. IDXGIOutput* hDxgiOutput = nullptr;
  79. DXGI_OUTPUT_DESC dxgiOutDesc;
  80. ZeroMemory(&dxgiOutDesc, sizeof(dxgiOutDesc));
  81. for (int idx = 0; SUCCEEDED(hr = hDxgiAdapter->EnumOutputs(idx, &hDxgiOutput)); ++idx) {
  82. hDxgiOutput->GetDesc(&dxgiOutDesc);
  83. if (dxgiOutDesc.DesktopCoordinates.left == left && dxgiOutDesc.DesktopCoordinates.top == top) {
  84. break;
  85. }
  86. }
  87. Free(hDxgiAdapter, [=] { hDxgiAdapter->Release(); });
  88. if (FAILED(hr)) {
  89. __DebugPrint("EnumOutputs failed: 0x%08lx", (unsigned long)hr);
  90. Free(hDxgiOutput, [=]{ hDxgiOutput->Release(); });
  91. Free(_hContext, [this]{ _hContext->Release(); });
  92. Free(_hDevice, [this]{ _hDevice->Release(); });
  93. return false;
  94. }
  95. // QI for Output 1
  96. IDXGIOutput1* hDxgiOutput1 = nullptr;
  97. hr = hDxgiOutput->QueryInterface(__uuidof(hDxgiOutput1), reinterpret_cast<void**>(&hDxgiOutput1));
  98. Free(hDxgiOutput, [=] { hDxgiOutput->Release(); });
  99. if (FAILED(hr)) {
  100. __DebugPrint("QueryInterface IDXGIOutput1 failed: 0x%08lx", (unsigned long)hr);
  101. Free(_hContext, [this]{ _hContext->Release(); });
  102. Free(_hDevice, [this]{ _hDevice->Release(); });
  103. return false;
  104. }
  105. // Create desktop duplication
  106. hr = hDxgiOutput1->DuplicateOutput(_hDevice, &_hDeskDupl);
  107. Free(hDxgiOutput1, [=] { hDxgiOutput1->Release(); });
  108. if (FAILED(hr)) {
  109. __DebugPrint("DuplicateOutput failed: 0x%08lx", (unsigned long)hr);
  110. Free(_hContext, [this]{ _hContext->Release(); });
  111. Free(_hDevice, [this]{ _hDevice->Release(); });
  112. return false;
  113. }
  114. // Set ColorSpace - 修复RGB到NV12转换的颜色空间设置
  115. D3D11_VIDEO_PROCESSOR_COLOR_SPACE inputColorSpace;
  116. inputColorSpace.Usage = 0; // 0 = Playback (更适合屏幕捕获)
  117. inputColorSpace.RGB_Range = 0; // 0 = Full range (0-255) for RGB input
  118. inputColorSpace.YCbCr_Matrix = 0; // 对于RGB输入,这个值会被忽略,但设为0 (BT.601)
  119. inputColorSpace.YCbCr_xvYCC = 0; // 0 = Conventional YCbCr
  120. inputColorSpace.Nominal_Range = D3D11_VIDEO_PROCESSOR_NOMINAL_RANGE_0_255; // Full range for RGB
  121. D3D11_VIDEO_PROCESSOR_COLOR_SPACE outputColorSpace;
  122. outputColorSpace.Usage = 0; // 0 = Playback
  123. outputColorSpace.RGB_Range = 0; // 对于YUV输出,这个值会被忽略
  124. outputColorSpace.YCbCr_Matrix = 0; // 0 = BT.601 (标准定义电视)
  125. outputColorSpace.YCbCr_xvYCC = 0; // 0 = Conventional YCbCr
  126. outputColorSpace.Nominal_Range = D3D11_VIDEO_PROCESSOR_NOMINAL_RANGE_16_235; // Studio range for YUV output
  127. qDebug() << "DxgiCapturer::Open: Color space settings - Input: Usage=" << inputColorSpace.Usage
  128. << ", RGB_Range=" << inputColorSpace.RGB_Range << ", Nominal_Range=" << inputColorSpace.Nominal_Range
  129. << "; Output: Usage=" << outputColorSpace.Usage << ", YCbCr_Matrix=" << outputColorSpace.YCbCr_Matrix
  130. << ", Nominal_Range=" << outputColorSpace.Nominal_Range;
  131. // 确保RGB到NV12转换器正确初始化
  132. qDebug() << "DxgiCapturer::Open: Initializing RGB to NV12 converter";
  133. if (FAILED(_rgbToNv12.Open(_hDevice, _hContext, inputColorSpace, outputColorSpace))) {
  134. qDebug() << "DxgiCapturer::Open: RGB to NV12 converter initialization failed";
  135. Free(_hDeskDupl, [this]{ _hDeskDupl->Release(); });
  136. Free(_hContext, [this]{ _hContext->Release(); });
  137. Free(_hDevice, [this]{ _hDevice->Release(); });
  138. return false;
  139. }
  140. qDebug() << "DxgiCapturer::Open: RGB to NV12 converter initialized successfully";
  141. _nv12Frame = Frame<MediaType::VIDEO>::Alloc(AV_PIX_FMT_NV12, width, height);
  142. _xrgbFrame = Frame<MediaType::VIDEO>::Alloc(AV_PIX_FMT_BGR0, width, height);
  143. if (!_nv12Frame) {
  144. __DebugPrint("Alloc NV12 frame failed");
  145. Free(_hDeskDupl, [this]{ _hDeskDupl->Release(); });
  146. Free(_hContext, [this]{ _hContext->Release(); });
  147. Free(_hDevice, [this]{ _hDevice->Release(); });
  148. return false;
  149. }
  150. if (!_xrgbFrame) {
  151. __DebugPrint("Alloc XRGB frame failed");
  152. Free(_nv12Frame, [this]{ av_frame_free(&_nv12Frame); });
  153. Free(_hDeskDupl, [this]{ _hDeskDupl->Release(); });
  154. Free(_hContext, [this]{ _hContext->Release(); });
  155. Free(_hDevice, [this]{ _hDevice->Release(); });
  156. return false;
  157. }
  158. _bInit = true;
  159. return true;
  160. }
  161. AVFrame* DxgiCapturerPrivate::GetFrame(
  162. bool shouldDrawCursor, int left, int top, int right, int bottom)
  163. {
  164. std::lock_guard<std::mutex> lock(_deviceMutex);
  165. if (!_bInit) return nullptr;
  166. _isCaptureSuccess = false;
  167. IDXGIResource* hDesktopResource = nullptr;
  168. DXGI_OUTDUPL_FRAME_INFO FrameInfo;
  169. HRESULT hr = _hDeskDupl->AcquireNextFrame(0, &FrameInfo, &hDesktopResource);
  170. if (FAILED(hr)) {
  171. if (hr == DXGI_ERROR_WAIT_TIMEOUT) return nullptr;
  172. return nullptr;
  173. }
  174. // query next frame staging buffer
  175. ID3D11Texture2D* srcImage = nullptr;
  176. hr = hDesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&srcImage));
  177. Free(hDesktopResource, [=] { hDesktopResource->Release(); });
  178. if (FAILED(hr)) {
  179. __DebugPrint("QueryInterface ID3D11Texture2D failed: 0x%08lx", (unsigned long)hr);
  180. _hDeskDupl->ReleaseFrame();
  181. return nullptr;
  182. }
  183. srcImage->GetDesc(&_desc);
  184. // create a new staging buffer for fill frame image
  185. auto desc = _desc;
  186. desc.ArraySize = 1;
  187. desc.BindFlags = D3D11_BIND_FLAG::D3D11_BIND_RENDER_TARGET;
  188. desc.MiscFlags = D3D11_RESOURCE_MISC_GDI_COMPATIBLE;
  189. desc.SampleDesc.Count = 1;
  190. desc.SampleDesc.Quality = 0;
  191. desc.MipLevels = 1;
  192. desc.CPUAccessFlags = 0;
  193. desc.Usage = D3D11_USAGE_DEFAULT;
  194. hr = _hDevice->CreateTexture2D(&desc, nullptr, &_gdiImage);
  195. if (FAILED(hr)) {
  196. __DebugPrint("Create _gdiImage failed");
  197. Free(srcImage, [=] { srcImage->Release(); });
  198. _hDeskDupl->ReleaseFrame();
  199. return nullptr;
  200. }
  201. // copy next staging buffer to new staging buffer
  202. _hContext->CopyResource(_gdiImage, srcImage);
  203. Free(srcImage, [=] { srcImage->Release(); });
  204. _hDeskDupl->ReleaseFrame();
  205. // create staging buffer for map bits
  206. _hStagingSurf = nullptr;
  207. hr = _gdiImage->QueryInterface(__uuidof(IDXGISurface), (void**) (&_hStagingSurf));
  208. if (FAILED(hr)) {
  209. __DebugPrint("_gdiImage->QueryInterface failed");
  210. Free(_gdiImage, [this] { _gdiImage->Release(); });
  211. return nullptr;
  212. }
  213. _isCaptureSuccess = true;
  214. HDC hdc = nullptr;
  215. _hStagingSurf->GetDC(FALSE, &hdc);
  216. // 合成鼠标指针
  217. if (hdc && shouldDrawCursor) {
  218. drawCursor(hdc, left, top, right, bottom);
  219. }
  220. // 释放 DC 并转换为 AVFrame
  221. if (_isCaptureSuccess) {
  222. _isCaptureSuccess = false;
  223. _hStagingSurf->ReleaseDC(nullptr);
  224. // 创建一个临时的纹理
  225. ID3D11Texture2D* tmpImage = nullptr;
  226. _desc.MiscFlags = 2050;
  227. hr = _hDevice->CreateTexture2D(&_desc, nullptr, &tmpImage);
  228. if (FAILED(hr)) {
  229. __DebugPrint("CreateTexture2D tmpImage failed: 0x%08lx", (unsigned long)hr);
  230. Free(_hStagingSurf, [this] { _hStagingSurf->Release(); });
  231. Free(_gdiImage, [this] { _gdiImage->Release(); });
  232. return nullptr;
  233. }
  234. _hContext->CopyResource(tmpImage, _gdiImage);
  235. // 首先尝试创建 NV12 纹理,如果失败则重试一次
  236. AVFrame* frame = nullptr;
  237. auto tmpFormat = _desc.Format;
  238. _desc.Format = DXGI_FORMAT_NV12;
  239. bool nv12Success = GenNv12Frame(_hDevice, _hContext, _desc, tmpImage, _nv12Buffers, _nv12Frame, _rgbToNv12);
  240. if (!nv12Success) {
  241. qDebug() << "DxgiCapturer::GetFrame: First NV12 conversion failed, retrying...";
  242. // 重试一次NV12转换
  243. nv12Success = GenNv12Frame(_hDevice, _hContext, _desc, tmpImage, _nv12Buffers, _nv12Frame, _rgbToNv12);
  244. }
  245. if (nv12Success) {
  246. frame = _nv12Frame;
  247. qDebug() << "DxgiCapturer::GetFrame: NV12 conversion successful";
  248. } else {
  249. qDebug() << "DxgiCapturer::GetFrame: NV12 conversion failed, falling back to RGB";
  250. _desc.Format = tmpFormat;
  251. GenRgbFrame(_hDevice, _hContext, _desc, _gdiImage, _xrgbBuffers, _xrgbFrame);
  252. frame = _xrgbFrame;
  253. }
  254. Free(_hStagingSurf, [this] { _hStagingSurf->Release(); });
  255. Free(tmpImage, [&tmpImage] { tmpImage->Release(); });
  256. Free(_gdiImage, [this] { _gdiImage->Release(); });
  257. return frame;
  258. }
  259. return nullptr;
  260. }
  261. void DxgiCapturerPrivate::drawCursor(HDC hdc, int left, int top, int right, int bottom)
  262. {
  263. CURSORINFO ci;
  264. ci.cbSize = sizeof(CURSORINFO);
  265. if (!GetCursorInfo(&ci)) {
  266. __DebugPrint("GetCursorInfo failed");
  267. return;
  268. }
  269. int cursorX = ci.ptScreenPos.x;
  270. int cursorY = ci.ptScreenPos.y;
  271. if (cursorX > right || cursorX < left || cursorY > bottom || cursorY < top) {
  272. return; // 超出显示范围
  273. }
  274. if (ci.flags == CURSOR_SHOWING) {
  275. // 将光标画到屏幕所在位置
  276. int x = cursorX - left;
  277. int y = cursorY - top;
  278. DrawIconEx(hdc, x, y, ci.hCursor, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT);
  279. }
  280. }
  281. // --- DxgiCapturer 实现 ---
  282. DxgiCapturer::DxgiCapturer() : d(new DxgiCapturerPrivate) {}
  283. DxgiCapturer::~DxgiCapturer() { close(); delete d; }
  284. bool DxgiCapturer::open(const CaptureTarget& target, int width, int height) {
  285. #ifdef PLATFORM_WINDOWS
  286. close();
  287. if (target.type != CaptureTargetType::Monitor) return false;
  288. auto monitors = MonitorFinder::GetList();
  289. if (target.monitorIdx < 0 || target.monitorIdx >= (int)monitors.size()) return false;
  290. auto& monitorInfo = monitors[target.monitorIdx];
  291. m_left = monitorInfo.rect.left;
  292. m_top = monitorInfo.rect.top;
  293. m_width = monitorInfo.rect.right - monitorInfo.rect.left;
  294. m_height = monitorInfo.rect.bottom - monitorInfo.rect.top;
  295. m_right = monitorInfo.rect.right;
  296. m_bottom = monitorInfo.rect.bottom;
  297. return d->Open(m_left, m_top, m_width, m_height);
  298. #else
  299. return false;
  300. #endif
  301. }
  302. void DxgiCapturerPrivate::CloseInternal() {
  303. if (!_bInit) return;
  304. qDebug() << "DxgiCapturerPrivate::CloseInternal: Starting cleanup";
  305. _bInit = false;
  306. // 清理缓冲区
  307. qDebug() << "DxgiCapturerPrivate::CloseInternal: Clearing buffers";
  308. _nv12Buffers.Clear();
  309. _xrgbBuffers.Clear();
  310. // 释放D3D转换器
  311. qDebug() << "DxgiCapturerPrivate::CloseInternal: Closing D3D converter";
  312. _rgbToNv12.Close();
  313. // 释放帧
  314. qDebug() << "DxgiCapturerPrivate::CloseInternal: Freeing frames";
  315. Free(_nv12Frame, [this] { av_frame_free(&_nv12Frame); });
  316. Free(_xrgbFrame, [this] { av_frame_free(&_xrgbFrame); });
  317. // 释放DXGI资源
  318. qDebug() << "DxgiCapturerPrivate::CloseInternal: Releasing DXGI resources";
  319. Free(_hDeskDupl, [this] { _hDeskDupl->Release(); });
  320. // 最后释放D3D设备和上下文
  321. qDebug() << "DxgiCapturerPrivate::CloseInternal: Releasing D3D resources";
  322. Free(_hContext, [this] { _hContext->Release(); });
  323. Free(_hDevice, [this] { _hDevice->Release(); });
  324. qDebug() << "DxgiCapturerPrivate::CloseInternal: Cleanup completed";
  325. }
  326. void DxgiCapturerPrivate::Close() {
  327. std::lock_guard<std::mutex> lock(_deviceMutex);
  328. CloseInternal();
  329. }
  330. void DxgiCapturer::close() {
  331. #ifdef PLATFORM_WINDOWS
  332. d->Close();
  333. #endif
  334. }
  335. AVFrame* DxgiCapturer::getFrame() {
  336. #ifdef PLATFORM_WINDOWS
  337. return d->GetFrame(m_drawCursor, m_left, m_top, m_right, m_bottom);
  338. #else
  339. return nullptr;
  340. #endif
  341. }
  342. } // namespace video
  343. } // namespace avrecorder