wgc_session_impl.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. #include "pch.h"
  2. #include <functional>
  3. #include <memory>
  4. #define CHECK_INIT \
  5. if (!is_initialized_) \
  6. return AM_ERROR::AE_NEED_INIT
  7. #define CHECK_CLOSED \
  8. if (cleaned_.load() == true) { \
  9. throw winrt::hresult_error(RO_E_CLOSED); \
  10. }
  11. extern "C" {
  12. HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(
  13. ::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice);
  14. }
  15. namespace am {
  16. wgc_session_impl::wgc_session_impl() {}
  17. wgc_session_impl::~wgc_session_impl() {
  18. stop();
  19. cleanup();
  20. }
  21. void wgc_session_impl::release() { delete this; }
  22. int wgc_session_impl::initialize(HWND hwnd) {
  23. std::lock_guard locker(lock_);
  24. target_.hwnd = hwnd;
  25. target_.is_window = true;
  26. return initialize();
  27. }
  28. int wgc_session_impl::initialize(HMONITOR hmonitor) {
  29. std::lock_guard locker(lock_);
  30. target_.hmonitor = hmonitor;
  31. target_.is_window = false;
  32. return initialize();
  33. }
  34. void wgc_session_impl::register_observer(wgc_session_observer *observer) {
  35. std::lock_guard locker(lock_);
  36. observer_ = observer;
  37. }
  38. int wgc_session_impl::start() {
  39. std::lock_guard locker(lock_);
  40. if (is_running_)
  41. return AM_ERROR::AE_NO;
  42. int error = AM_ERROR::AE_WGC_CREATE_CAPTURER_FAILED;
  43. CHECK_INIT;
  44. try {
  45. if (!capture_session_) {
  46. auto current_size = capture_item_.Size();
  47. capture_framepool_ =
  48. winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
  49. CreateFreeThreaded(d3d11_direct_device_,
  50. winrt::Windows::Graphics::DirectX::
  51. DirectXPixelFormat::B8G8R8A8UIntNormalized,
  52. 2, current_size);
  53. capture_session_ = capture_framepool_.CreateCaptureSession(capture_item_);
  54. capture_frame_size_ = current_size;
  55. capture_framepool_trigger_ = capture_framepool_.FrameArrived(
  56. winrt::auto_revoke, {this, &wgc_session_impl::on_frame});
  57. capture_close_trigger_ = capture_item_.Closed(
  58. winrt::auto_revoke, {this, &wgc_session_impl::on_closed});
  59. }
  60. if (!capture_framepool_)
  61. throw std::exception();
  62. is_running_ = true;
  63. // we do not need to crate a thread to enter a message loop coz we use
  64. // CreateFreeThreaded instead of Create to create a capture frame pool,
  65. // we need to test the performance later
  66. // loop_ = std::thread(std::bind(&wgc_session_impl::message_func, this));
  67. capture_session_.StartCapture();
  68. error = AM_ERROR::AE_NO;
  69. } catch (winrt::hresult_error) {
  70. return AM_ERROR::AE_WGC_CREATE_CAPTURER_FAILED;
  71. } catch (...) {
  72. return AM_ERROR::AE_WGC_CREATE_CAPTURER_FAILED;
  73. }
  74. return error;
  75. }
  76. int wgc_session_impl::stop() {
  77. std::lock_guard locker(lock_);
  78. CHECK_INIT;
  79. is_running_ = false;
  80. if (loop_.joinable())
  81. loop_.join();
  82. if (capture_framepool_trigger_)
  83. capture_framepool_trigger_.revoke();
  84. if (capture_session_) {
  85. capture_session_.Close();
  86. capture_session_ = nullptr;
  87. }
  88. return AM_ERROR::AE_NO;
  89. }
  90. int wgc_session_impl::pause() {
  91. std::lock_guard locker(lock_);
  92. CHECK_INIT;
  93. return AM_ERROR::AE_NO;
  94. }
  95. int wgc_session_impl::resume() {
  96. std::lock_guard locker(lock_);
  97. CHECK_INIT;
  98. return AM_ERROR::AE_NO;
  99. }
  100. auto wgc_session_impl::create_d3d11_device() {
  101. auto create_d3d_device = [](D3D_DRIVER_TYPE const type,
  102. winrt::com_ptr<ID3D11Device> &device) {
  103. WINRT_ASSERT(!device);
  104. UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
  105. //#ifdef _DEBUG
  106. // flags |= D3D11_CREATE_DEVICE_DEBUG;
  107. //#endif
  108. return ::D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0,
  109. D3D11_SDK_VERSION, device.put(), nullptr,
  110. nullptr);
  111. };
  112. auto create_d3d_device_wrapper = [&create_d3d_device]() {
  113. winrt::com_ptr<ID3D11Device> device;
  114. HRESULT hr = create_d3d_device(D3D_DRIVER_TYPE_HARDWARE, device);
  115. if (DXGI_ERROR_UNSUPPORTED == hr) {
  116. hr = create_d3d_device(D3D_DRIVER_TYPE_WARP, device);
  117. }
  118. winrt::check_hresult(hr);
  119. return device;
  120. };
  121. auto d3d_device = create_d3d_device_wrapper();
  122. auto dxgi_device = d3d_device.as<IDXGIDevice>();
  123. winrt::com_ptr<::IInspectable> d3d11_device;
  124. winrt::check_hresult(CreateDirect3D11DeviceFromDXGIDevice(
  125. dxgi_device.get(), d3d11_device.put()));
  126. return d3d11_device
  127. .as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>();
  128. }
  129. auto wgc_session_impl::create_capture_item(HWND hwnd) {
  130. auto activation_factory = winrt::get_activation_factory<
  131. winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
  132. auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
  133. winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
  134. interop_factory->CreateForWindow(
  135. hwnd,
  136. winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
  137. reinterpret_cast<void **>(winrt::put_abi(item)));
  138. return item;
  139. }
  140. auto wgc_session_impl::create_capture_item(HMONITOR hmonitor) {
  141. auto activation_factory = winrt::get_activation_factory<
  142. winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
  143. auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
  144. winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
  145. interop_factory->CreateForMonitor(
  146. hmonitor,
  147. winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
  148. reinterpret_cast<void **>(winrt::put_abi(item)));
  149. return item;
  150. }
  151. HRESULT wgc_session_impl::create_mapped_texture(
  152. winrt::com_ptr<ID3D11Texture2D> src_texture, unsigned int width,
  153. unsigned int height) {
  154. D3D11_TEXTURE2D_DESC src_desc;
  155. src_texture->GetDesc(&src_desc);
  156. D3D11_TEXTURE2D_DESC map_desc;
  157. map_desc.Width = width == 0 ? src_desc.Width : width;
  158. map_desc.Height = height == 0 ? src_desc.Height : height;
  159. map_desc.MipLevels = src_desc.MipLevels;
  160. map_desc.ArraySize = src_desc.ArraySize;
  161. map_desc.Format = src_desc.Format;
  162. map_desc.SampleDesc = src_desc.SampleDesc;
  163. map_desc.Usage = D3D11_USAGE_STAGING;
  164. map_desc.BindFlags = 0;
  165. map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
  166. map_desc.MiscFlags = 0;
  167. auto d3dDevice = get_dxgi_interface<ID3D11Device>(d3d11_direct_device_);
  168. return d3dDevice->CreateTexture2D(&map_desc, nullptr,
  169. d3d11_texture_mapped_.put());
  170. }
  171. void wgc_session_impl::on_frame(
  172. winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender,
  173. winrt::Windows::Foundation::IInspectable const &args) {
  174. std::lock_guard locker(lock_);
  175. auto is_new_size = false;
  176. {
  177. auto frame = sender.TryGetNextFrame();
  178. auto frame_size = frame.ContentSize();
  179. if (frame_size.Width != capture_frame_size_.Width ||
  180. frame_size.Height != capture_frame_size_.Height) {
  181. // The thing we have been capturing has changed size.
  182. // We need to resize our swap chain first, then blit the pixels.
  183. // After we do that, retire the frame and then recreate our frame pool.
  184. is_new_size = true;
  185. capture_frame_size_ = frame_size;
  186. }
  187. // copy to mapped texture
  188. {
  189. auto frame_captured =
  190. get_dxgi_interface<ID3D11Texture2D>(frame.Surface());
  191. if (!d3d11_texture_mapped_ || is_new_size)
  192. create_mapped_texture(frame_captured);
  193. d3d11_device_context_->CopyResource(d3d11_texture_mapped_.get(),
  194. frame_captured.get());
  195. D3D11_MAPPED_SUBRESOURCE map_result;
  196. HRESULT hr = d3d11_device_context_->Map(
  197. d3d11_texture_mapped_.get(), 0, D3D11_MAP_READ,
  198. 0 /*coz we use CreateFreeThreaded, so we cant use flags
  199. D3D11_MAP_FLAG_DO_NOT_WAIT*/
  200. ,
  201. &map_result);
  202. if (FAILED(hr)) {
  203. OutputDebugStringW(
  204. (L"map resource failed: " + std::to_wstring(hr)).c_str());
  205. }
  206. // copy data from map_result.pData
  207. if (map_result.pData && observer_) {
  208. observer_->on_frame(wgc_session_frame{
  209. static_cast<unsigned int>(frame_size.Width),
  210. static_cast<unsigned int>(frame_size.Height), map_result.RowPitch,
  211. const_cast<const unsigned char *>(
  212. (unsigned char *)map_result.pData)});
  213. }
  214. #if 0
  215. if (map_result.pData) {
  216. static unsigned char *buffer = nullptr;
  217. if (buffer && is_new_size)
  218. delete[] buffer;
  219. if (!buffer)
  220. buffer = new unsigned char[frame_size.Width * frame_size.Height * 4];
  221. int dstRowPitch = frame_size.Width * 4;
  222. for (int h = 0; h < frame_size.Height; h++) {
  223. memcpy_s(buffer + h * dstRowPitch, dstRowPitch,
  224. (BYTE *)map_result.pData + h * map_result.RowPitch,
  225. min(map_result.RowPitch, dstRowPitch));
  226. }
  227. BITMAPINFOHEADER bi;
  228. bi.biSize = sizeof(BITMAPINFOHEADER);
  229. bi.biWidth = frame_size.Width;
  230. bi.biHeight = frame_size.Height * (-1);
  231. bi.biPlanes = 1;
  232. bi.biBitCount = 32; // should get from system color bits
  233. bi.biCompression = BI_RGB;
  234. bi.biSizeImage = 0;
  235. bi.biXPelsPerMeter = 0;
  236. bi.biYPelsPerMeter = 0;
  237. bi.biClrUsed = 0;
  238. bi.biClrImportant = 0;
  239. BITMAPFILEHEADER bf;
  240. bf.bfType = 0x4d42;
  241. bf.bfReserved1 = 0;
  242. bf.bfReserved2 = 0;
  243. bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
  244. bf.bfSize = bf.bfOffBits + frame_size.Width * frame_size.Height * 4;
  245. FILE *fp = nullptr;
  246. fopen_s(&fp, ".\\save.bmp", "wb+");
  247. fwrite(&bf, 1, sizeof(bf), fp);
  248. fwrite(&bi, 1, sizeof(bi), fp);
  249. fwrite(buffer, 1, frame_size.Width * frame_size.Height * 4, fp);
  250. fflush(fp);
  251. fclose(fp);
  252. }
  253. #endif
  254. d3d11_device_context_->Unmap(d3d11_texture_mapped_.get(), 0);
  255. }
  256. }
  257. if (is_new_size) {
  258. capture_framepool_.Recreate(d3d11_direct_device_,
  259. winrt::Windows::Graphics::DirectX::
  260. DirectXPixelFormat::B8G8R8A8UIntNormalized,
  261. 2, capture_frame_size_);
  262. }
  263. }
  264. void wgc_session_impl::on_closed(
  265. winrt::Windows::Graphics::Capture::GraphicsCaptureItem const &,
  266. winrt::Windows::Foundation::IInspectable const &) {
  267. OutputDebugStringW(L"wgc_session_impl::on_closed");
  268. }
  269. int wgc_session_impl::initialize() {
  270. if (is_initialized_)
  271. return AM_ERROR::AE_NO;
  272. if (!(d3d11_direct_device_ = create_d3d11_device()))
  273. return AM_ERROR::AE_D3D_CREATE_DEVICE_FAILED;
  274. try {
  275. if (target_.is_window)
  276. capture_item_ = create_capture_item(target_.hwnd);
  277. else
  278. capture_item_ = create_capture_item(target_.hmonitor);
  279. // Set up
  280. auto d3d11_device = get_dxgi_interface<ID3D11Device>(d3d11_direct_device_);
  281. d3d11_device->GetImmediateContext(d3d11_device_context_.put());
  282. } catch (winrt::hresult_error) {
  283. return AM_ERROR::AE_WGC_CREATE_CAPTURER_FAILED;
  284. } catch (...) {
  285. return AM_ERROR::AE_WGC_CREATE_CAPTURER_FAILED;
  286. }
  287. is_initialized_ = true;
  288. return AM_ERROR::AE_NO;
  289. }
  290. void wgc_session_impl::cleanup() {
  291. std::lock_guard locker(lock_);
  292. auto expected = false;
  293. if (cleaned_.compare_exchange_strong(expected, true)) {
  294. capture_close_trigger_.revoke();
  295. capture_framepool_trigger_.revoke();
  296. if (capture_framepool_)
  297. capture_framepool_.Close();
  298. if (capture_session_)
  299. capture_session_.Close();
  300. capture_framepool_ = nullptr;
  301. capture_session_ = nullptr;
  302. capture_item_ = nullptr;
  303. is_initialized_ = false;
  304. }
  305. }
  306. LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param,
  307. LPARAM l_param) {
  308. return DefWindowProc(window, message, w_param, l_param);
  309. }
  310. void wgc_session_impl::message_func() {
  311. const std::wstring kClassName = L"am_fake_window";
  312. WNDCLASS wc = {};
  313. wc.style = CS_HREDRAW | CS_VREDRAW;
  314. wc.lpfnWndProc = DefWindowProc;
  315. wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
  316. wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW);
  317. wc.lpszClassName = kClassName.c_str();
  318. if (!::RegisterClassW(&wc))
  319. return;
  320. hwnd_ = ::CreateWindowW(kClassName.c_str(), nullptr, WS_OVERLAPPEDWINDOW, 0,
  321. 0, 0, 0, nullptr, nullptr, nullptr, nullptr);
  322. MSG msg;
  323. while (is_running_) {
  324. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  325. if (!is_running_)
  326. break;
  327. TranslateMessage(&msg);
  328. DispatchMessage(&msg);
  329. }
  330. Sleep(10);
  331. }
  332. ::CloseWindow(hwnd_);
  333. ::DestroyWindow(hwnd_);
  334. }
  335. } // namespace am