422 lines
13 KiB
C++
422 lines
13 KiB
C++
// 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 <algorithm>
|
|
#include <cstdio>
|
|
#include <iostream>
|
|
#include <memory>
|
|
|
|
//
|
|
// 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<void**>(&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<void**>(&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<void**>(&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<VOID*>(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<void**>(&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<DXGI_OUTDUPL_MOVE_RECT*>(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<RECT*>(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;
|
|
}
|