// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved #include "DuplicationManager.h" #include #include #include #include // // Constructor sets up references / variables // DUPLICATIONMANAGER::DUPLICATIONMANAGER() : m_DeskDupl(nullptr), m_AcquiredDesktopImage(nullptr), m_MetaDataBuffer(nullptr), m_MetaDataSize(0), m_OutputNumber(0), m_Device(nullptr) { RtlZeroMemory(&m_OutputDesc, sizeof(m_OutputDesc)); } // // Destructor simply calls CleanRefs to destroy everything // DUPLICATIONMANAGER::~DUPLICATIONMANAGER() { if (m_DeskDupl) { m_DeskDupl->Release(); m_DeskDupl = nullptr; } if (m_AcquiredDesktopImage) { m_AcquiredDesktopImage->Release(); m_AcquiredDesktopImage = nullptr; } if (m_MetaDataBuffer) { delete[] m_MetaDataBuffer; m_MetaDataBuffer = nullptr; } if (m_Device) { m_Device->Release(); m_Device = nullptr; } } // // Initialize duplication interfaces // DUPL_RETURN DUPLICATIONMANAGER::InitDupl(_In_ ID3D11Device* Device, UINT Output) { m_OutputNumber = Output; // Take a reference on the device m_Device = Device; m_Device->AddRef(); // Get DXGI device IDXGIDevice* DxgiDevice = nullptr; HRESULT hr = m_Device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast(&DxgiDevice)); if (FAILED(hr)) { return ProcessFailure(nullptr, L"Failed to QI for DXGI Device", L"Error", hr); } // Get DXGI adapter IDXGIAdapter* DxgiAdapter = nullptr; hr = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast(&DxgiAdapter)); DxgiDevice->Release(); DxgiDevice = nullptr; if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to get parent DXGI Adapter", L"Error", hr, SystemTransitionsExpectedErrors); } // Get output IDXGIOutput* DxgiOutput = nullptr; hr = DxgiAdapter->EnumOutputs(Output, &DxgiOutput); DxgiAdapter->Release(); DxgiAdapter = nullptr; if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to get specified output in DUPLICATIONMANAGER", L"Error", hr, EnumOutputsExpectedErrors); } DxgiOutput->GetDesc(&m_OutputDesc); // QI for Output 1 IDXGIOutput1* DxgiOutput1 = nullptr; hr = DxgiOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast(&DxgiOutput1)); DxgiOutput->Release(); DxgiOutput = nullptr; if (FAILED(hr)) { return ProcessFailure(nullptr, L"Failed to QI for DxgiOutput1 in DUPLICATIONMANAGER", L"Error", hr); } // Create desktop duplication hr = DxgiOutput1->DuplicateOutput(m_Device, &m_DeskDupl); DxgiOutput1->Release(); DxgiOutput1 = nullptr; if (FAILED(hr)) { if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) { 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); return DUPL_RETURN_ERROR_UNEXPECTED; } return ProcessFailure(m_Device, L"Failed to get duplicate output in DUPLICATIONMANAGER", L"Error", hr, CreateDuplicationExpectedErrors); } return DUPL_RETURN_SUCCESS; } // // Retrieves mouse info and write it into PtrInfo // DUPL_RETURN DUPLICATIONMANAGER::GetMouse(_Inout_ PTR_INFO* PtrInfo, _In_ DXGI_OUTDUPL_FRAME_INFO* FrameInfo, INT OffsetX, INT OffsetY) { // A non-zero mouse update timestamp indicates that there is a mouse position update and optionally a shape change if (FrameInfo->LastMouseUpdateTime.QuadPart == 0) { return DUPL_RETURN_SUCCESS; } bool UpdatePosition = true; // Make sure we don't update pointer position wrongly // If pointer is invisible, make sure we did not get an update from another output that the last time that said pointer // was visible, if so, don't set it to invisible or update. if (!FrameInfo->PointerPosition.Visible && (PtrInfo->WhoUpdatedPositionLast != m_OutputNumber)) { UpdatePosition = false; } // If two outputs both say they have a visible, only update if new update has newer timestamp if (FrameInfo->PointerPosition.Visible && PtrInfo->Visible && (PtrInfo->WhoUpdatedPositionLast != m_OutputNumber) && (PtrInfo->LastTimeStamp.QuadPart > FrameInfo->LastMouseUpdateTime.QuadPart)) { UpdatePosition = false; } // Update position if (UpdatePosition) { PtrInfo->Position.x = FrameInfo->PointerPosition.Position.x + m_OutputDesc.DesktopCoordinates.left - OffsetX; PtrInfo->Position.y = FrameInfo->PointerPosition.Position.y + m_OutputDesc.DesktopCoordinates.top - OffsetY; PtrInfo->WhoUpdatedPositionLast = m_OutputNumber; PtrInfo->LastTimeStamp = FrameInfo->LastMouseUpdateTime; PtrInfo->Visible = FrameInfo->PointerPosition.Visible != 0; } // No new shape if (FrameInfo->PointerShapeBufferSize == 0) { return DUPL_RETURN_SUCCESS; } // Old buffer too small if (FrameInfo->PointerShapeBufferSize > PtrInfo->BufferSize) { if (PtrInfo->PtrShapeBuffer) { delete[] PtrInfo->PtrShapeBuffer; PtrInfo->PtrShapeBuffer = nullptr; } PtrInfo->PtrShapeBuffer = new(std::nothrow) BYTE[FrameInfo->PointerShapeBufferSize]; if (!PtrInfo->PtrShapeBuffer) { PtrInfo->BufferSize = 0; return ProcessFailure(nullptr, L"Failed to allocate memory for pointer shape in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY); } // Update buffer size PtrInfo->BufferSize = FrameInfo->PointerShapeBufferSize; } // Get shape UINT BufferSizeRequired; HRESULT hr = m_DeskDupl->GetFramePointerShape(FrameInfo->PointerShapeBufferSize, reinterpret_cast(PtrInfo->PtrShapeBuffer), &BufferSizeRequired, &(PtrInfo->ShapeInfo)); if (FAILED(hr)) { delete[] PtrInfo->PtrShapeBuffer; PtrInfo->PtrShapeBuffer = nullptr; PtrInfo->BufferSize = 0; return ProcessFailure(m_Device, L"Failed to get frame pointer shape in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors); } return DUPL_RETURN_SUCCESS; } int bmp_write(unsigned char* image, int imageWidth, int imageHeight, char* filename) { unsigned char header[54] = { 0x42, 0x4d, 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; long file_size = (long)imageWidth * (long)imageHeight * 4 + 54; header[2] = (unsigned char)(file_size & 0x000000ff); header[3] = (file_size >> 8) & 0x000000ff; header[4] = (file_size >> 16) & 0x000000ff; header[5] = (file_size >> 24) & 0x000000ff; long width = imageWidth; header[18] = width & 0x000000ff; header[19] = (width >> 8) & 0x000000ff; header[20] = (width >> 16) & 0x000000ff; header[21] = (width >> 24) & 0x000000ff; long height = imageHeight; header[22] = height & 0x000000ff; header[23] = (height >> 8) & 0x000000ff; header[24] = (height >> 16) & 0x000000ff; header[25] = (height >> 24) & 0x000000ff; char fname_bmp[128]; sprintf(fname_bmp, "%s", filename); FILE* fp; if (!(fp = fopen(fname_bmp, "wb"))) return -1; fwrite(header, sizeof(unsigned char), 54, fp); fwrite(image, sizeof(unsigned char), (size_t)(long)imageWidth * imageHeight * 4, fp); fclose(fp); return 0; } ffvideo* play = nullptr; int pts = 0; FILE* ff; void output(ffvideo* ctx) { if (ff == nullptr) { ff = fopen("a.h264", "wb"); //sps pps fwrite(ctx->encoder_ctx->extradata, sizeof(uint8_t), ctx->encoder_ctx->extradata_size, ff); } fwrite(ctx->dst_packet->data, sizeof(uint8_t), ctx->dst_packet->size, ff); std::cout << "ENCODE_SUCCESS: size = " << ctx->dst_packet->size << std::endl; } void extract_pixel(ID3D11Device* m_Device, ID3D11Texture2D* m_AcquiredDesktopImage) { if (pts > 500) return; if (pts == 500) { fclose(ff); exit(0); } ID3D11DeviceContext* ctx; m_Device->GetImmediateContext(&ctx); ID3D11Texture2D* copy_tex = nullptr; D3D11_TEXTURE2D_DESC desc; m_AcquiredDesktopImage->GetDesc(&desc); desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; desc.Usage = D3D11_USAGE_STAGING; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.BindFlags = 0; desc.MiscFlags = 0; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; int hr = m_Device->CreateTexture2D(&desc, nullptr, ©_tex); if (FAILED(hr) || copy_tex == nullptr) { return; } ctx->CopyResource(copy_tex, m_AcquiredDesktopImage); D3D11_MAPPED_SUBRESOURCE resource; ctx->Map(copy_tex, 0, D3D11_MAP_READ, 0, &resource); if (!pts) { play = new ffvideo(); play->info = { 1920, 1080, AV_PIX_FMT_BGRA, 1920 / 2, 1080 / 2, AV_PIX_FMT_YUV420P, 30, 2 * 1024 * 1024, 30 }; play->initSws(); play->initH264(); } play->src_buffer = (uint8_t*)resource.pData; for (int i = 0; i < 4; i++) play->line_size[i] = resource.RowPitch; play->process_data(pts, pts, output); ctx->Unmap(copy_tex, 0); pts++; } // // Get next frame and write it into Data // _Success_(*Timeout == false && return == DUPL_RETURN_SUCCESS) DUPL_RETURN DUPLICATIONMANAGER::GetFrame(_Out_ FRAME_DATA* Data, _Out_ bool* Timeout) { IDXGIResource* DesktopResource = nullptr; DXGI_OUTDUPL_FRAME_INFO FrameInfo; // Get new frame HRESULT hr = m_DeskDupl->AcquireNextFrame(500, &FrameInfo, &DesktopResource); if (hr == DXGI_ERROR_WAIT_TIMEOUT) { *Timeout = true; return DUPL_RETURN_SUCCESS; } *Timeout = false; if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to acquire next frame in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors); } // If still holding old frame, destroy it if (m_AcquiredDesktopImage) { m_AcquiredDesktopImage->Release(); m_AcquiredDesktopImage = nullptr; } // QI for IDXGIResource hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast(&m_AcquiredDesktopImage)); DesktopResource->Release(); DesktopResource = nullptr; if (FAILED(hr)) { return ProcessFailure( nullptr, L"Failed to QI for ID3D11Texture2D from acquired IDXGIResource in DUPLICATIONMANAGER", L"Error", hr); } static int tx = 0; if (tx == 100) { } extract_pixel(m_Device, m_AcquiredDesktopImage); tx++; // Get metadata if (FrameInfo.TotalMetadataBufferSize) { // Old buffer too small if (FrameInfo.TotalMetadataBufferSize > m_MetaDataSize) { if (m_MetaDataBuffer) { delete[] m_MetaDataBuffer; m_MetaDataBuffer = nullptr; } m_MetaDataBuffer = new(std::nothrow) BYTE[FrameInfo.TotalMetadataBufferSize]; if (!m_MetaDataBuffer) { m_MetaDataSize = 0; Data->MoveCount = 0; Data->DirtyCount = 0; return ProcessFailure(nullptr, L"Failed to allocate memory for metadata in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY); } m_MetaDataSize = FrameInfo.TotalMetadataBufferSize; } UINT BufSize = FrameInfo.TotalMetadataBufferSize; // Get move rectangles hr = m_DeskDupl->GetFrameMoveRects(BufSize, reinterpret_cast(m_MetaDataBuffer), &BufSize); if (FAILED(hr)) { Data->MoveCount = 0; Data->DirtyCount = 0; return ProcessFailure(nullptr, L"Failed to get frame move rects in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors); } Data->MoveCount = BufSize / sizeof(DXGI_OUTDUPL_MOVE_RECT); BYTE* DirtyRects = m_MetaDataBuffer + BufSize; BufSize = FrameInfo.TotalMetadataBufferSize - BufSize; // Get dirty rectangles hr = m_DeskDupl->GetFrameDirtyRects(BufSize, reinterpret_cast(DirtyRects), &BufSize); if (FAILED(hr)) { Data->MoveCount = 0; Data->DirtyCount = 0; return ProcessFailure(nullptr, L"Failed to get frame dirty rects in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors); } Data->DirtyCount = BufSize / sizeof(RECT); Data->MetaData = m_MetaDataBuffer; } Data->Frame = m_AcquiredDesktopImage; Data->FrameInfo = FrameInfo; return DUPL_RETURN_SUCCESS; } // // Release frame // DUPL_RETURN DUPLICATIONMANAGER::DoneWithFrame() { HRESULT hr = m_DeskDupl->ReleaseFrame(); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to release frame in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors); } if (m_AcquiredDesktopImage) { m_AcquiredDesktopImage->Release(); m_AcquiredDesktopImage = nullptr; } return DUPL_RETURN_SUCCESS; } // // Gets output desc into DescPtr // void DUPLICATIONMANAGER::GetOutputDesc(_Out_ DXGI_OUTPUT_DESC* DescPtr) { *DescPtr = m_OutputDesc; }