DuplicationManager.cxx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  2. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  3. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  4. // PARTICULAR PURPOSE.
  5. //
  6. // Copyright (c) Microsoft Corporation. All rights reserved
  7. #include "DuplicationManager.h"
  8. #include "tinymedia/tmedia_producer.h"
  9. #include <Mfapi.h>
  10. static inline HRESULT CopyRGBb32DownTop(
  11. BYTE* pDst,
  12. const BYTE* pSrc,
  13. INT dwWidthDstPixels,
  14. INT dwWidthSrcPixels,
  15. INT dwHeightPixels
  16. );
  17. //
  18. // Constructor sets up references / variables
  19. //
  20. DUPLICATIONMANAGER::DUPLICATIONMANAGER() : m_DeskDupl(nullptr),
  21. m_AcquiredDesktopImage(nullptr),
  22. m_MetaDataBuffer(nullptr),
  23. m_MetaDataSize(0),
  24. m_OutputNumber(0),
  25. m_Device(nullptr),
  26. m_DeviceContext(nullptr),
  27. m_BufferPtr(nullptr),
  28. m_BufferSize(0)
  29. {
  30. RtlZeroMemory(&m_OutputDesc, sizeof(m_OutputDesc));
  31. }
  32. //
  33. // Destructor simply calls CleanRefs to destroy everything
  34. //
  35. DUPLICATIONMANAGER::~DUPLICATIONMANAGER()
  36. {
  37. if (m_DeskDupl) {
  38. m_DeskDupl->Release();
  39. m_DeskDupl = nullptr;
  40. }
  41. if (m_AcquiredDesktopImage) {
  42. m_AcquiredDesktopImage->Release();
  43. m_AcquiredDesktopImage = nullptr;
  44. }
  45. if (m_MetaDataBuffer) {
  46. delete [] m_MetaDataBuffer;
  47. m_MetaDataBuffer = nullptr;
  48. }
  49. if (m_DeviceContext) {
  50. m_DeviceContext->Release();
  51. m_DeviceContext = nullptr;
  52. }
  53. if (m_Device) {
  54. m_Device->Release();
  55. m_Device = nullptr;
  56. }
  57. if (m_BufferPtr) {
  58. VirtualFree(m_BufferPtr, 0, MEM_RELEASE);
  59. m_BufferPtr = nullptr;
  60. }
  61. }
  62. //
  63. // Initialize duplication interfaces
  64. //
  65. DUPL_RETURN DUPLICATIONMANAGER::InitDupl(_In_ ID3D11Device* Device, ID3D11DeviceContext* DeviceContext, UINT Output)
  66. {
  67. m_OutputNumber = Output;
  68. // Take a reference on the device
  69. m_Device = Device;
  70. m_Device->AddRef();
  71. m_DeviceContext = DeviceContext;
  72. m_DeviceContext->AddRef();
  73. // Get DXGI device
  74. IDXGIDevice* DxgiDevice = nullptr;
  75. HRESULT hr = m_Device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&DxgiDevice));
  76. if (FAILED(hr)) {
  77. return ProcessFailure(nullptr, L"Failed to QI for DXGI Device", L"Error", hr);
  78. }
  79. // Get DXGI adapter
  80. IDXGIAdapter* DxgiAdapter = nullptr;
  81. hr = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&DxgiAdapter));
  82. DxgiDevice->Release();
  83. DxgiDevice = nullptr;
  84. if (FAILED(hr)) {
  85. return ProcessFailure(m_Device, L"Failed to get parent DXGI Adapter", L"Error", hr, SystemTransitionsExpectedErrors);
  86. }
  87. // Get output
  88. IDXGIOutput* DxgiOutput = nullptr;
  89. hr = DxgiAdapter->EnumOutputs(Output, &DxgiOutput);
  90. DxgiAdapter->Release();
  91. DxgiAdapter = nullptr;
  92. if (FAILED(hr)) {
  93. return ProcessFailure(m_Device, L"Failed to get specified output in DUPLICATIONMANAGER", L"Error", hr, EnumOutputsExpectedErrors);
  94. }
  95. DxgiOutput->GetDesc(&m_OutputDesc);
  96. // QI for Output 1
  97. IDXGIOutput1* DxgiOutput1 = nullptr;
  98. hr = DxgiOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast<void**>(&DxgiOutput1));
  99. DxgiOutput->Release();
  100. DxgiOutput = nullptr;
  101. if (FAILED(hr)) {
  102. return ProcessFailure(nullptr, L"Failed to QI for DxgiOutput1 in DUPLICATIONMANAGER", L"Error", hr);
  103. }
  104. // Create desktop duplication
  105. hr = DxgiOutput1->DuplicateOutput(m_Device, &m_DeskDupl);
  106. DxgiOutput1->Release();
  107. DxgiOutput1 = nullptr;
  108. if (FAILED(hr)) {
  109. if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) {
  110. MessageBoxW(nullptr, L"There is already the maximum number of applications using the Desktop Duplication API running, please close one of those applications and then try again.", L"Error", MB_OK);
  111. return DUPL_RETURN_ERROR_UNEXPECTED;
  112. }
  113. return ProcessFailure(m_Device, L"Failed to get duplicate output in DUPLICATIONMANAGER", L"Error", hr, CreateDuplicationExpectedErrors);
  114. }
  115. return DUPL_RETURN_SUCCESS;
  116. }
  117. //
  118. // Retrieves mouse info and write it into PtrInfo
  119. //
  120. DUPL_RETURN DUPLICATIONMANAGER::GetMouse(_Inout_ PTR_INFO* PtrInfo, _In_ DXGI_OUTDUPL_FRAME_INFO* FrameInfo, INT OffsetX, INT OffsetY)
  121. {
  122. // A non-zero mouse update timestamp indicates that there is a mouse position update and optionally a shape change
  123. if (FrameInfo->LastMouseUpdateTime.QuadPart == 0) {
  124. return DUPL_RETURN_SUCCESS;
  125. }
  126. bool UpdatePosition = true;
  127. // Make sure we don't update pointer position wrongly
  128. // If pointer is invisible, make sure we did not get an update from another output that the last time that said pointer
  129. // was visible, if so, don't set it to invisible or update.
  130. if (!FrameInfo->PointerPosition.Visible && (PtrInfo->WhoUpdatedPositionLast != m_OutputNumber)) {
  131. UpdatePosition = false;
  132. }
  133. // If two outputs both say they have a visible, only update if new update has newer timestamp
  134. if (FrameInfo->PointerPosition.Visible && PtrInfo->Visible && (PtrInfo->WhoUpdatedPositionLast != m_OutputNumber) && (PtrInfo->LastTimeStamp.QuadPart > FrameInfo->LastMouseUpdateTime.QuadPart)) {
  135. UpdatePosition = false;
  136. }
  137. // Update position
  138. if (UpdatePosition) {
  139. PtrInfo->Position.x = FrameInfo->PointerPosition.Position.x + m_OutputDesc.DesktopCoordinates.left - OffsetX;
  140. PtrInfo->Position.y = FrameInfo->PointerPosition.Position.y + m_OutputDesc.DesktopCoordinates.top - OffsetY;
  141. PtrInfo->WhoUpdatedPositionLast = m_OutputNumber;
  142. PtrInfo->LastTimeStamp = FrameInfo->LastMouseUpdateTime;
  143. PtrInfo->Visible = FrameInfo->PointerPosition.Visible != 0;
  144. }
  145. // No new shape
  146. if (FrameInfo->PointerShapeBufferSize == 0) {
  147. return DUPL_RETURN_SUCCESS;
  148. }
  149. // Old buffer too small
  150. if (FrameInfo->PointerShapeBufferSize > PtrInfo->BufferSize) {
  151. if (PtrInfo->PtrShapeBuffer) {
  152. delete [] PtrInfo->PtrShapeBuffer;
  153. PtrInfo->PtrShapeBuffer = nullptr;
  154. }
  155. PtrInfo->PtrShapeBuffer = new (std::nothrow) BYTE[FrameInfo->PointerShapeBufferSize];
  156. if (!PtrInfo->PtrShapeBuffer) {
  157. PtrInfo->BufferSize = 0;
  158. return ProcessFailure(nullptr, L"Failed to allocate memory for pointer shape in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY);
  159. }
  160. // Update buffer size
  161. PtrInfo->BufferSize = FrameInfo->PointerShapeBufferSize;
  162. }
  163. // Get shape
  164. UINT BufferSizeRequired;
  165. HRESULT hr = m_DeskDupl->GetFramePointerShape(FrameInfo->PointerShapeBufferSize, reinterpret_cast<VOID*>(PtrInfo->PtrShapeBuffer), &BufferSizeRequired, &(PtrInfo->ShapeInfo));
  166. if (FAILED(hr)) {
  167. delete [] PtrInfo->PtrShapeBuffer;
  168. PtrInfo->PtrShapeBuffer = nullptr;
  169. PtrInfo->BufferSize = 0;
  170. return ProcessFailure(m_Device, L"Failed to get frame pointer shape in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors);
  171. }
  172. return DUPL_RETURN_SUCCESS;
  173. }
  174. //
  175. // Get next frame and write it into Data
  176. //
  177. _Success_(*Timeout == false && return == DUPL_RETURN_SUCCESS)
  178. DUPL_RETURN DUPLICATIONMANAGER::GetFrame(_Out_ FRAME_DATA* Data, _Out_ bool* Timeout)
  179. {
  180. IDXGIResource* DesktopResource = nullptr;
  181. DXGI_OUTDUPL_FRAME_INFO FrameInfo;
  182. // Get new frame
  183. HRESULT hr = m_DeskDupl->AcquireNextFrame(500, &FrameInfo, &DesktopResource);
  184. if (hr == DXGI_ERROR_WAIT_TIMEOUT) {
  185. *Timeout = true;
  186. return DUPL_RETURN_SUCCESS;
  187. }
  188. *Timeout = false;
  189. if (FAILED(hr)) {
  190. return ProcessFailure(m_Device, L"Failed to acquire next frame in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors);
  191. }
  192. // If still holding old frame, destroy it
  193. if (m_AcquiredDesktopImage) {
  194. m_AcquiredDesktopImage->Release();
  195. m_AcquiredDesktopImage = nullptr;
  196. }
  197. // QI for IDXGIResource
  198. hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&m_AcquiredDesktopImage));
  199. DesktopResource->Release();
  200. DesktopResource = nullptr;
  201. if (FAILED(hr)) {
  202. return ProcessFailure(nullptr, L"Failed to QI for ID3D11Texture2D from acquired IDXGIResource in DUPLICATIONMANAGER", L"Error", hr);
  203. }
  204. // Get metadata
  205. if (FrameInfo.TotalMetadataBufferSize) {
  206. // Old buffer too small
  207. if (FrameInfo.TotalMetadataBufferSize > m_MetaDataSize) {
  208. if (m_MetaDataBuffer) {
  209. delete [] m_MetaDataBuffer;
  210. m_MetaDataBuffer = nullptr;
  211. }
  212. m_MetaDataBuffer = new (std::nothrow) BYTE[FrameInfo.TotalMetadataBufferSize];
  213. if (!m_MetaDataBuffer) {
  214. m_MetaDataSize = 0;
  215. Data->MoveCount = 0;
  216. Data->DirtyCount = 0;
  217. return ProcessFailure(nullptr, L"Failed to allocate memory for metadata in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY);
  218. }
  219. m_MetaDataSize = FrameInfo.TotalMetadataBufferSize;
  220. }
  221. UINT BufSize = FrameInfo.TotalMetadataBufferSize;
  222. // Get move rectangles
  223. hr = m_DeskDupl->GetFrameMoveRects(BufSize, reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(m_MetaDataBuffer), &BufSize);
  224. if (FAILED(hr)) {
  225. Data->MoveCount = 0;
  226. Data->DirtyCount = 0;
  227. return ProcessFailure(nullptr, L"Failed to get frame move rects in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors);
  228. }
  229. Data->MoveCount = BufSize / sizeof(DXGI_OUTDUPL_MOVE_RECT);
  230. BYTE* DirtyRects = m_MetaDataBuffer + BufSize;
  231. BufSize = FrameInfo.TotalMetadataBufferSize - BufSize;
  232. // Get dirty rectangles
  233. hr = m_DeskDupl->GetFrameDirtyRects(BufSize, reinterpret_cast<RECT*>(DirtyRects), &BufSize);
  234. if (FAILED(hr)) {
  235. Data->MoveCount = 0;
  236. Data->DirtyCount = 0;
  237. return ProcessFailure(nullptr, L"Failed to get frame dirty rects in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors);
  238. }
  239. Data->DirtyCount = BufSize / sizeof(RECT);
  240. Data->MetaData = m_MetaDataBuffer;
  241. }
  242. Data->Frame = m_AcquiredDesktopImage;
  243. Data->FrameInfo = FrameInfo;
  244. return DUPL_RETURN_SUCCESS;
  245. }
  246. //
  247. // Release frame
  248. //
  249. DUPL_RETURN DUPLICATIONMANAGER::DoneWithFrame()
  250. {
  251. HRESULT hr = m_DeskDupl->ReleaseFrame();
  252. if (FAILED(hr)) {
  253. return ProcessFailure(m_Device, L"Failed to release frame in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors);
  254. }
  255. if (m_AcquiredDesktopImage) {
  256. m_AcquiredDesktopImage->Release();
  257. m_AcquiredDesktopImage = nullptr;
  258. }
  259. return DUPL_RETURN_SUCCESS;
  260. }
  261. //
  262. // Gets output desc into DescPtr
  263. //
  264. void DUPLICATIONMANAGER::GetOutputDesc(_Out_ DXGI_OUTPUT_DESC* DescPtr)
  265. {
  266. *DescPtr = m_OutputDesc;
  267. }
  268. HRESULT DUPLICATIONMANAGER::SendData(struct tmedia_producer_s* pProducer, FRAME_DATA* FrameData)
  269. {
  270. HRESULT hr = E_FAIL;
  271. D3D11_TEXTURE2D_DESC CopyBufferDesc = {0};
  272. D3D11_TEXTURE2D_DESC FullDesc;
  273. DXGI_MAPPED_RECT MappedSurface;
  274. D3D11_BOX Box;
  275. UINT BuffSize;
  276. ID3D11Texture2D* CopyBuffer = nullptr;
  277. IDXGISurface* CopySurface = nullptr;
  278. ID3D11Device* Device = nullptr;
  279. FrameData->Frame->GetDesc(&FullDesc);
  280. CopyBufferDesc.Width = FullDesc.Width;
  281. CopyBufferDesc.Height = FullDesc.Height;
  282. CopyBufferDesc.MipLevels = 1;
  283. CopyBufferDesc.ArraySize = 1;
  284. CopyBufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
  285. CopyBufferDesc.SampleDesc.Count = 1;
  286. CopyBufferDesc.SampleDesc.Quality = 0;
  287. CopyBufferDesc.Usage = D3D11_USAGE_STAGING;
  288. CopyBufferDesc.BindFlags = 0;
  289. CopyBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
  290. CopyBufferDesc.MiscFlags = 0;
  291. FrameData->Frame->GetDevice(&Device);
  292. if (!Device) {
  293. hr = E_POINTER;
  294. ProcessFailure(m_Device, L"Failed to get device", L"Error", hr, SystemTransitionsExpectedErrors);
  295. goto bail;
  296. }
  297. hr = Device->CreateTexture2D(&CopyBufferDesc, nullptr, &CopyBuffer);
  298. if (FAILED(hr)) {
  299. ProcessFailure(m_Device, L"Failed creating staging texture for pointer", L"Error", hr, SystemTransitionsExpectedErrors);
  300. goto bail;
  301. }
  302. Box.left = 0;
  303. Box.top = 0;
  304. Box.right = CopyBufferDesc.Width;
  305. Box.bottom = CopyBufferDesc.Height;
  306. Box.front = 0;
  307. Box.back = 1;
  308. m_DeviceContext->CopySubresourceRegion(CopyBuffer, 0, 0, 0, 0, FrameData->Frame, 0, &Box);
  309. hr = CopyBuffer->QueryInterface(__uuidof(IDXGISurface), (void **)&CopySurface);
  310. if (FAILED(hr)) {
  311. ProcessFailure(nullptr, L"Failed to QI staging texture into IDXGISurface for pointer", L"Error", hr, SystemTransitionsExpectedErrors);
  312. goto bail;
  313. }
  314. BuffSize = CopyBufferDesc.Width * CopyBufferDesc.Height * 4;
  315. if (m_BufferSize < BuffSize) {
  316. if (m_BufferPtr) {
  317. VirtualFree(m_BufferPtr, 0, MEM_RELEASE);
  318. m_BufferSize = 0;
  319. }
  320. if (!(m_BufferPtr = (BYTE*)VirtualAlloc(NULL, BuffSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE))) {
  321. ProcessFailure(Device, L"Failed to allocate memory", L"Error", hr, SystemTransitionsExpectedErrors);
  322. goto bail;
  323. }
  324. m_BufferSize = BuffSize;
  325. }
  326. hr = CopySurface->Map(&MappedSurface, DXGI_MAP_READ); // *** MAP *** //
  327. if (FAILED(hr)) {
  328. ProcessFailure(Device, L"Failed to map surface for pointer", L"Error", hr, SystemTransitionsExpectedErrors);
  329. goto bail;
  330. }
  331. pProducer->video.width = CopyBufferDesc.Width;
  332. pProducer->video.height = CopyBufferDesc.Height;
  333. #if 0
  334. hr = MFCopyImage(
  335. m_BufferPtr,
  336. (LONG)(CopyBufferDesc.Width << 2),
  337. (BYTE*)MappedSurface.pBits,
  338. (LONG)MappedSurface.Pitch,
  339. (DWORD)(CopyBufferDesc.Width << 2),
  340. (DWORD)CopyBufferDesc.Height
  341. );
  342. #else;
  343. hr = CopyRGBb32DownTop(
  344. m_BufferPtr,
  345. MappedSurface.pBits,
  346. CopyBufferDesc.Width,
  347. (MappedSurface.Pitch >> 2), // Bytes -> Pixels
  348. CopyBufferDesc.Height);
  349. #endif
  350. pProducer->enc_cb.callback(pProducer->enc_cb.callback_data, m_BufferPtr, BuffSize);
  351. CopySurface->Unmap(); // *** UNMAP *** //
  352. bail:
  353. if (CopyBuffer) {
  354. CopyBuffer->Release();
  355. }
  356. if (CopySurface) {
  357. CopySurface->Release();
  358. }
  359. if (Device) {
  360. Device->Release();
  361. }
  362. return hr;
  363. }
  364. // For RGB32:
  365. // Direct3D -> Top-Down
  366. // Video Processor -> Down-Top
  367. static inline HRESULT CopyRGBb32DownTop(
  368. BYTE* pDst,
  369. const BYTE* pSrc,
  370. INT dwWidthDstPixels,
  371. INT dwWidthSrcPixels,
  372. INT dwHeightPixels
  373. )
  374. {
  375. RGBQUAD *pSrcPixel = &((RGBQUAD*)pSrc)[(dwWidthSrcPixels * dwHeightPixels) - dwWidthSrcPixels];
  376. RGBQUAD *pDestPixel = &((RGBQUAD*)pDst)[0];
  377. register INT x;
  378. register INT y;
  379. for (y = dwHeightPixels; y > 0; --y) {
  380. for (x = 0; x < dwWidthDstPixels; ++x) {
  381. pDestPixel[x] = pSrcPixel[x];
  382. }
  383. pDestPixel += dwWidthDstPixels;
  384. pSrcPixel -= dwWidthSrcPixels;
  385. }
  386. return S_OK;
  387. }