| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- #include "DxgiCapturer.h"
- #include "basic/basic.h"
- #include "basic/frame.h"
- #include "d3d/buffer_filler.h"
- #include "d3d/convert.h"
- #include "d3d/gen_frame.h"
- #include "../finder.h"
- #include <cassert>
- #include <d3d11.h>
- #include <dxgi1_2.h>
- namespace avrecorder {
- namespace video {
- // --- DxgiCapturerPrivate 实现 ---
- bool DxgiCapturerPrivate::Open(int left, int top, int width, int height) {
- Close();
- HRESULT hr = S_OK;
- _isAttached = false;
- if (_bInit) return false;
- // Driver types supported
- D3D_DRIVER_TYPE DriverTypes[] = {
- D3D_DRIVER_TYPE_HARDWARE,
- D3D_DRIVER_TYPE_WARP,
- D3D_DRIVER_TYPE_REFERENCE,
- };
- UINT NumDriverTypes = ARRAYSIZE(DriverTypes);
- // Feature levels supported
- D3D_FEATURE_LEVEL FeatureLevels[] = {
- D3D_FEATURE_LEVEL_11_0,
- D3D_FEATURE_LEVEL_10_1,
- D3D_FEATURE_LEVEL_10_0,
- D3D_FEATURE_LEVEL_9_1};
- UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);
- D3D_FEATURE_LEVEL FeatureLevel;
- // Create D3D device
- for (UINT DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex) {
- hr = D3D11CreateDevice(nullptr,
- DriverTypes[DriverTypeIndex],
- nullptr,
- 0,
- FeatureLevels,
- NumFeatureLevels,
- D3D11_SDK_VERSION,
- &_hDevice,
- &FeatureLevel,
- &_hContext);
- if (SUCCEEDED(hr)) {
- break;
- }
- }
- if (FAILED(hr)) {
- __DebugPrint("D3D11CreateDevice failed: 0x%08lx", (unsigned long)hr);
- return false;
- }
- // Get DXGI device
- IDXGIDevice* hDxgiDevice = nullptr;
- hr = _hDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&hDxgiDevice));
- if (FAILED(hr)) {
- __DebugPrint("QueryInterface IDXGIDevice failed: 0x%08lx", (unsigned long)hr);
- Free(_hContext, [this]{ _hContext->Release(); });
- Free(_hDevice, [this]{ _hDevice->Release(); });
- return false;
- }
- // Get DXGI adapter
- IDXGIAdapter* hDxgiAdapter = nullptr;
- hr = hDxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&hDxgiAdapter));
- Free(hDxgiDevice, [=] { hDxgiDevice->Release(); });
- if (FAILED(hr)) {
- __DebugPrint("GetParent IDXGIAdapter failed: 0x%08lx", (unsigned long)hr);
- Free(_hContext, [this]{ _hContext->Release(); });
- Free(_hDevice, [this]{ _hDevice->Release(); });
- return false;
- }
- // Get output
- IDXGIOutput* hDxgiOutput = nullptr;
- DXGI_OUTPUT_DESC dxgiOutDesc;
- ZeroMemory(&dxgiOutDesc, sizeof(dxgiOutDesc));
- for (int idx = 0; SUCCEEDED(hr = hDxgiAdapter->EnumOutputs(idx, &hDxgiOutput)); ++idx) {
- hDxgiOutput->GetDesc(&dxgiOutDesc);
- if (dxgiOutDesc.DesktopCoordinates.left == left && dxgiOutDesc.DesktopCoordinates.top == top) {
- break;
- }
- }
- Free(hDxgiAdapter, [=] { hDxgiAdapter->Release(); });
- if (FAILED(hr)) {
- __DebugPrint("EnumOutputs failed: 0x%08lx", (unsigned long)hr);
- Free(hDxgiOutput, [=]{ hDxgiOutput->Release(); });
- Free(_hContext, [this]{ _hContext->Release(); });
- Free(_hDevice, [this]{ _hDevice->Release(); });
- return false;
- }
- // QI for Output 1
- IDXGIOutput1* hDxgiOutput1 = nullptr;
- hr = hDxgiOutput->QueryInterface(__uuidof(hDxgiOutput1), reinterpret_cast<void**>(&hDxgiOutput1));
- Free(hDxgiOutput, [=] { hDxgiOutput->Release(); });
- if (FAILED(hr)) {
- __DebugPrint("QueryInterface IDXGIOutput1 failed: 0x%08lx", (unsigned long)hr);
- Free(_hContext, [this]{ _hContext->Release(); });
- Free(_hDevice, [this]{ _hDevice->Release(); });
- return false;
- }
- // Create desktop duplication
- hr = hDxgiOutput1->DuplicateOutput(_hDevice, &_hDeskDupl);
- Free(hDxgiOutput1, [=] { hDxgiOutput1->Release(); });
- if (FAILED(hr)) {
- __DebugPrint("DuplicateOutput failed: 0x%08lx", (unsigned long)hr);
- Free(_hContext, [this]{ _hContext->Release(); });
- Free(_hDevice, [this]{ _hDevice->Release(); });
- return false;
- }
- // Set ColorSpace
- D3D11_VIDEO_PROCESSOR_COLOR_SPACE inputColorSpace;
- inputColorSpace.Usage = 1;
- inputColorSpace.RGB_Range = 0;
- inputColorSpace.YCbCr_Matrix = 1;
- inputColorSpace.YCbCr_xvYCC = 0;
- inputColorSpace.Nominal_Range = D3D11_VIDEO_PROCESSOR_NOMINAL_RANGE_0_255;
- D3D11_VIDEO_PROCESSOR_COLOR_SPACE outputColorSpace;
- outputColorSpace.Usage = 0;
- outputColorSpace.RGB_Range = 0;
- outputColorSpace.YCbCr_Matrix = 1;
- outputColorSpace.YCbCr_xvYCC = 0;
- outputColorSpace.Nominal_Range = D3D11_VIDEO_PROCESSOR_NOMINAL_RANGE_16_235;
- _rgbToNv12.Open(_hDevice, _hContext, inputColorSpace, outputColorSpace);
- _nv12Frame = Frame<MediaType::VIDEO>::Alloc(AV_PIX_FMT_NV12, width, height);
- _xrgbFrame = Frame<MediaType::VIDEO>::Alloc(AV_PIX_FMT_BGR0, width, height);
- if (!_nv12Frame) {
- __DebugPrint("Alloc NV12 frame failed");
- Free(_hDeskDupl, [this]{ _hDeskDupl->Release(); });
- Free(_hContext, [this]{ _hContext->Release(); });
- Free(_hDevice, [this]{ _hDevice->Release(); });
- return false;
- }
- if (!_xrgbFrame) {
- __DebugPrint("Alloc XRGB frame failed");
- Free(_nv12Frame, [this]{ av_frame_free(&_nv12Frame); });
- Free(_hDeskDupl, [this]{ _hDeskDupl->Release(); });
- Free(_hContext, [this]{ _hContext->Release(); });
- Free(_hDevice, [this]{ _hDevice->Release(); });
- return false;
- }
- _bInit = true;
- return true;
- }
- AVFrame* DxgiCapturerPrivate::GetFrame(
- bool shouldDrawCursor, int left, int top, int right, int bottom)
- {
- if (!_bInit) return nullptr;
- _isCaptureSuccess = false;
- IDXGIResource* hDesktopResource = nullptr;
- DXGI_OUTDUPL_FRAME_INFO FrameInfo;
- HRESULT hr = _hDeskDupl->AcquireNextFrame(0, &FrameInfo, &hDesktopResource);
- if (FAILED(hr)) {
- if (hr == DXGI_ERROR_WAIT_TIMEOUT) return nullptr;
- return nullptr;
- }
- // query next frame staging buffer
- ID3D11Texture2D* srcImage = nullptr;
- hr = hDesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&srcImage));
- Free(hDesktopResource, [=] { hDesktopResource->Release(); });
- if (FAILED(hr)) {
- __DebugPrint("QueryInterface ID3D11Texture2D failed: 0x%08lx", (unsigned long)hr);
- _hDeskDupl->ReleaseFrame();
- return nullptr;
- }
- srcImage->GetDesc(&_desc);
- // create a new staging buffer for fill frame image
- auto desc = _desc;
- desc.ArraySize = 1;
- desc.BindFlags = D3D11_BIND_FLAG::D3D11_BIND_RENDER_TARGET;
- desc.MiscFlags = D3D11_RESOURCE_MISC_GDI_COMPATIBLE;
- desc.SampleDesc.Count = 1;
- desc.SampleDesc.Quality = 0;
- desc.MipLevels = 1;
- desc.CPUAccessFlags = 0;
- desc.Usage = D3D11_USAGE_DEFAULT;
- hr = _hDevice->CreateTexture2D(&desc, nullptr, &_gdiImage);
- if (FAILED(hr)) {
- __DebugPrint("Create _gdiImage failed");
- Free(srcImage, [=] { srcImage->Release(); });
- _hDeskDupl->ReleaseFrame();
- return nullptr;
- }
- // copy next staging buffer to new staging buffer
- _hContext->CopyResource(_gdiImage, srcImage);
- Free(srcImage, [=] { srcImage->Release(); });
- _hDeskDupl->ReleaseFrame();
- // create staging buffer for map bits
- _hStagingSurf = nullptr;
- hr = _gdiImage->QueryInterface(__uuidof(IDXGISurface), (void**) (&_hStagingSurf));
- if (FAILED(hr)) {
- __DebugPrint("_gdiImage->QueryInterface failed");
- Free(_gdiImage, [this] { _gdiImage->Release(); });
- return nullptr;
- }
- _isCaptureSuccess = true;
- HDC hdc = nullptr;
- _hStagingSurf->GetDC(FALSE, &hdc);
- // 合成鼠标指针
- if (hdc && shouldDrawCursor) {
- drawCursor(hdc, left, top, right, bottom);
- }
- // 释放 DC 并转换为 AVFrame
- if (_isCaptureSuccess) {
- _isCaptureSuccess = false;
- _hStagingSurf->ReleaseDC(nullptr);
- // 创建一个临时的纹理
- ID3D11Texture2D* tmpImage = nullptr;
- _desc.MiscFlags = 2050;
- hr = _hDevice->CreateTexture2D(&_desc, nullptr, &tmpImage);
- if (FAILED(hr)) {
- __DebugPrint("CreateTexture2D tmpImage failed: 0x%08lx", (unsigned long)hr);
- Free(_hStagingSurf, [this] { _hStagingSurf->Release(); });
- Free(_gdiImage, [this] { _gdiImage->Release(); });
- return nullptr;
- }
- _hContext->CopyResource(tmpImage, _gdiImage);
- // 首先尝试创建 NV12 纹理
- AVFrame* frame = nullptr;
- auto tmpFormat = _desc.Format;
- _desc.Format = DXGI_FORMAT_NV12;
- if (GenNv12Frame(_hDevice, _hContext, _desc, tmpImage, _nv12Buffers, _nv12Frame, _rgbToNv12)) {
- frame = _nv12Frame;
- } else {
- _desc.Format = tmpFormat;
- GenRgbFrame(_hDevice, _hContext, _desc, _gdiImage, _xrgbBuffers, _xrgbFrame);
- frame = _xrgbFrame;
- }
- Free(_hStagingSurf, [this] { _hStagingSurf->Release(); });
- Free(tmpImage, [&tmpImage] { tmpImage->Release(); });
- Free(_gdiImage, [this] { _gdiImage->Release(); });
- return frame;
- }
- return nullptr;
- }
- void DxgiCapturerPrivate::drawCursor(HDC hdc, int left, int top, int right, int bottom)
- {
- CURSORINFO ci;
- ci.cbSize = sizeof(CURSORINFO);
- if (!GetCursorInfo(&ci)) {
- __DebugPrint("GetCursorInfo failed");
- return;
- }
- int cursorX = ci.ptScreenPos.x;
- int cursorY = ci.ptScreenPos.y;
- if (cursorX > right || cursorX < left || cursorY > bottom || cursorY < top) {
- return; // 超出显示范围
- }
- if (ci.flags == CURSOR_SHOWING) {
- // 将光标画到屏幕所在位置
- int x = cursorX - left;
- int y = cursorY - top;
- DrawIconEx(hdc, x, y, ci.hCursor, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT);
- }
- }
- // --- DxgiCapturer 实现 ---
- DxgiCapturer::DxgiCapturer() : d(new DxgiCapturerPrivate) {}
- DxgiCapturer::~DxgiCapturer() { close(); delete d; }
- bool DxgiCapturer::open(const CaptureTarget& target, int width, int height) {
- #ifdef PLATFORM_WINDOWS
- close();
- if (target.type != CaptureTargetType::Monitor) return false;
- auto monitors = MonitorFinder::GetList();
- if (target.monitorIdx < 0 || target.monitorIdx >= (int)monitors.size()) return false;
- auto& monitorInfo = monitors[target.monitorIdx];
- m_left = monitorInfo.rect.left;
- m_top = monitorInfo.rect.top;
- m_width = monitorInfo.rect.right - monitorInfo.rect.left;
- m_height = monitorInfo.rect.bottom - monitorInfo.rect.top;
- m_right = monitorInfo.rect.right;
- m_bottom = monitorInfo.rect.bottom;
- return d->Open(m_left, m_top, m_width, m_height);
- #else
- return false;
- #endif
- }
- void DxgiCapturerPrivate::Close() {
- if (!_bInit) return;
- _bInit = false;
- _nv12Buffers.Clear();
- _xrgbBuffers.Clear();
- _rgbToNv12.Close();
- Free(_nv12Frame, [this] { av_frame_free(&_nv12Frame); });
- Free(_xrgbFrame, [this] { av_frame_free(&_xrgbFrame); });
- Free(_hDeskDupl, [this] { _hDeskDupl->Release(); });
- Free(_hDevice, [this] { _hDevice->Release(); });
- Free(_hContext, [this] { _hContext->Release(); });
- }
- void DxgiCapturer::close() {
- #ifdef PLATFORM_WINDOWS
- d->Close();
- #endif
- }
- AVFrame* DxgiCapturer::getFrame() {
- #ifdef PLATFORM_WINDOWS
- return d->GetFrame(m_drawCursor, m_left, m_top, m_right, m_bottom);
- #else
- return nullptr;
- #endif
- }
- } // namespace video
- } // namespace avrecorder
|