| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- #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>
- #include <QDebug>
- #include <mutex>
- namespace avrecorder {
- namespace video {
- // --- DxgiCapturerPrivate 实现 ---
- bool DxgiCapturerPrivate::Open(int left, int top, int width, int height) {
- std::lock_guard<std::mutex> lock(_deviceMutex);
- CloseInternal(); // 使用内部方法避免死锁
- HRESULT hr = S_OK;
- _isAttached = false;
-
- // 重置初始化状态,允许重新初始化
- _bInit = 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 - 修复RGB到NV12转换的颜色空间设置
- D3D11_VIDEO_PROCESSOR_COLOR_SPACE inputColorSpace;
- inputColorSpace.Usage = 0; // 0 = Playback (更适合屏幕捕获)
- inputColorSpace.RGB_Range = 0; // 0 = Full range (0-255) for RGB input
- inputColorSpace.YCbCr_Matrix = 0; // 对于RGB输入,这个值会被忽略,但设为0 (BT.601)
- inputColorSpace.YCbCr_xvYCC = 0; // 0 = Conventional YCbCr
- inputColorSpace.Nominal_Range = D3D11_VIDEO_PROCESSOR_NOMINAL_RANGE_0_255; // Full range for RGB
-
- D3D11_VIDEO_PROCESSOR_COLOR_SPACE outputColorSpace;
- outputColorSpace.Usage = 0; // 0 = Playback
- outputColorSpace.RGB_Range = 0; // 对于YUV输出,这个值会被忽略
- outputColorSpace.YCbCr_Matrix = 0; // 0 = BT.601 (标准定义电视)
- outputColorSpace.YCbCr_xvYCC = 0; // 0 = Conventional YCbCr
- outputColorSpace.Nominal_Range = D3D11_VIDEO_PROCESSOR_NOMINAL_RANGE_16_235; // Studio range for YUV output
-
- qDebug() << "DxgiCapturer::Open: Color space settings - Input: Usage=" << inputColorSpace.Usage
- << ", RGB_Range=" << inputColorSpace.RGB_Range << ", Nominal_Range=" << inputColorSpace.Nominal_Range
- << "; Output: Usage=" << outputColorSpace.Usage << ", YCbCr_Matrix=" << outputColorSpace.YCbCr_Matrix
- << ", Nominal_Range=" << outputColorSpace.Nominal_Range;
-
- // 确保RGB到NV12转换器正确初始化
- qDebug() << "DxgiCapturer::Open: Initializing RGB to NV12 converter";
- if (FAILED(_rgbToNv12.Open(_hDevice, _hContext, inputColorSpace, outputColorSpace))) {
- qDebug() << "DxgiCapturer::Open: RGB to NV12 converter initialization failed";
- Free(_hDeskDupl, [this]{ _hDeskDupl->Release(); });
- Free(_hContext, [this]{ _hContext->Release(); });
- Free(_hDevice, [this]{ _hDevice->Release(); });
- return false;
- }
- qDebug() << "DxgiCapturer::Open: RGB to NV12 converter initialized successfully";
- _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)
- {
- std::lock_guard<std::mutex> lock(_deviceMutex);
- 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;
-
- bool nv12Success = GenNv12Frame(_hDevice, _hContext, _desc, tmpImage, _nv12Buffers, _nv12Frame, _rgbToNv12);
- if (!nv12Success) {
- qDebug() << "DxgiCapturer::GetFrame: First NV12 conversion failed, retrying...";
- // 重试一次NV12转换
- nv12Success = GenNv12Frame(_hDevice, _hContext, _desc, tmpImage, _nv12Buffers, _nv12Frame, _rgbToNv12);
- }
-
- if (nv12Success) {
- frame = _nv12Frame;
- qDebug() << "DxgiCapturer::GetFrame: NV12 conversion successful";
- } else {
- qDebug() << "DxgiCapturer::GetFrame: NV12 conversion failed, falling back to RGB";
- _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::CloseInternal() {
- if (!_bInit) return;
- qDebug() << "DxgiCapturerPrivate::CloseInternal: Starting cleanup";
-
- _bInit = false;
-
- // 清理缓冲区
- qDebug() << "DxgiCapturerPrivate::CloseInternal: Clearing buffers";
- _nv12Buffers.Clear();
- _xrgbBuffers.Clear();
-
- // 释放D3D转换器
- qDebug() << "DxgiCapturerPrivate::CloseInternal: Closing D3D converter";
- _rgbToNv12.Close();
-
- // 释放帧
- qDebug() << "DxgiCapturerPrivate::CloseInternal: Freeing frames";
- Free(_nv12Frame, [this] { av_frame_free(&_nv12Frame); });
- Free(_xrgbFrame, [this] { av_frame_free(&_xrgbFrame); });
-
- // 释放DXGI资源
- qDebug() << "DxgiCapturerPrivate::CloseInternal: Releasing DXGI resources";
- Free(_hDeskDupl, [this] { _hDeskDupl->Release(); });
-
- // 最后释放D3D设备和上下文
- qDebug() << "DxgiCapturerPrivate::CloseInternal: Releasing D3D resources";
- Free(_hContext, [this] { _hContext->Release(); });
- Free(_hDevice, [this] { _hDevice->Release(); });
-
- qDebug() << "DxgiCapturerPrivate::CloseInternal: Cleanup completed";
- }
- void DxgiCapturerPrivate::Close() {
- std::lock_guard<std::mutex> lock(_deviceMutex);
- CloseInternal();
- }
- 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
|