You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
9.2 KiB
298 lines
9.2 KiB
/*****************************************************************************
|
|
* dxgi.cpp: Screen capture module for DXGI.
|
|
*****************************************************************************
|
|
* Copyright (C) 2018-2022 VLC authors and VideoLAN
|
|
*
|
|
* Authors: Steve Lhomme <robux4@gmail.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
*****************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <vlc_common.h>
|
|
|
|
#include "screen.h"
|
|
|
|
#include "../../video_chroma/d3d11_fmt.h"
|
|
|
|
#include <d3d11_1.h>
|
|
|
|
#include <new>
|
|
|
|
#include <wrl/client.h>
|
|
using Microsoft::WRL::ComPtr;
|
|
|
|
struct screen_data_t
|
|
{
|
|
const d3d_format_t *output_format = nullptr;
|
|
vlc_video_context *vctx = nullptr;
|
|
|
|
int screen_x = 0;
|
|
int screen_y = 0;
|
|
|
|
ComPtr<IDXGIOutputDuplication> duplication;
|
|
|
|
~screen_data_t()
|
|
{
|
|
if (vctx)
|
|
vlc_video_context_Release(vctx);
|
|
}
|
|
};
|
|
|
|
static void CaptureBlockRelease( block_t *p_block )
|
|
{
|
|
block_sys_d3d11_t *d3d11_block = D3D11BLOCK_FROM_BLOCK(p_block);
|
|
picture_Release(d3d11_block->d3d11_pic);
|
|
delete d3d11_block;
|
|
}
|
|
|
|
static block_t *screen_Capture(demux_t *p_demux)
|
|
{
|
|
demux_sys_t *p_sys = static_cast<demux_sys_t*>(p_demux->p_sys);
|
|
screen_data_t *p_data = p_sys->p_data;
|
|
block_sys_d3d11_t *d3d11_block = new (std::nothrow) block_sys_d3d11_t();
|
|
ComPtr<IDXGIResource> resource;
|
|
ComPtr<ID3D11Resource> d3d11res;
|
|
picture_sys_d3d11_t *pic_sys;
|
|
D3D11_BOX copyBox;
|
|
|
|
if( unlikely(d3d11_block == nullptr) )
|
|
return nullptr;
|
|
|
|
d3d11_decoder_device_t *d3d_dev = GetD3D11OpaqueContext(p_data->vctx);
|
|
|
|
static const struct vlc_frame_callbacks cbs = {
|
|
CaptureBlockRelease,
|
|
};
|
|
block_Init( &d3d11_block->self, &cbs, nullptr, 1 );
|
|
|
|
d3d11_block->d3d11_pic = D3D11_AllocPicture(VLC_OBJECT(p_demux),
|
|
&p_sys->fmt.video, p_data->vctx,
|
|
true, p_data->output_format);
|
|
if ( d3d11_block->d3d11_pic == nullptr )
|
|
{
|
|
msg_Err(p_demux, "Failed to allocate the output texture");
|
|
goto error;
|
|
}
|
|
|
|
DXGI_OUTDUPL_FRAME_INFO frameInfo;
|
|
HRESULT hr;
|
|
hr = p_data->duplication->AcquireNextFrame(1000, &frameInfo, &resource);
|
|
if (FAILED(hr))
|
|
{
|
|
msg_Err(p_demux, "Failed to capture a frame. (hr=0x%lX)", hr);
|
|
goto error;
|
|
}
|
|
|
|
#if defined(SCREEN_SUBSCREEN) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
|
if( p_sys->b_follow_mouse )
|
|
{
|
|
POINT pos;
|
|
GetCursorPos( &pos );
|
|
pos.x -= p_data->screen_x;
|
|
pos.y -= p_data->screen_y;
|
|
FollowMouse( p_sys, pos.x, pos.y );
|
|
}
|
|
#endif // SCREEN_SUBSCREEN && WINAPI_PARTITION_DESKTOP
|
|
|
|
/* copy the texture into the block texture */
|
|
hr = resource.As(&d3d11res);
|
|
if (unlikely(FAILED(hr)))
|
|
{
|
|
msg_Err(p_demux, "Failed to get the texture. (hr=0x%lX)", hr);
|
|
goto error;
|
|
}
|
|
pic_sys = ActiveD3D11PictureSys(d3d11_block->d3d11_pic);
|
|
#ifdef SCREEN_SUBSCREEN
|
|
copyBox.left = p_sys->i_left;
|
|
copyBox.right = copyBox.left + p_sys->i_width;
|
|
copyBox.top = p_sys->i_top;
|
|
copyBox.bottom = copyBox.top + p_sys->i_height;
|
|
#else // !SCREEN_SUBSCREEN
|
|
copyBox.left = 0;
|
|
copyBox.right = p_sys->fmt.video.i_width;
|
|
copyBox.top = 0;
|
|
copyBox.bottom = p_sys->fmt.video.i_height;
|
|
#endif // !SCREEN_SUBSCREEN
|
|
copyBox.front = 0;
|
|
copyBox.back = 1;
|
|
d3d11_device_lock( &d3d_dev->d3d_dev );
|
|
d3d_dev->d3d_dev.d3dcontext->CopySubresourceRegion(
|
|
pic_sys->resource[KNOWN_DXGI_INDEX], 0,
|
|
0, 0, 0,
|
|
d3d11res.Get(), 0,
|
|
©Box);
|
|
p_data->duplication->ReleaseFrame();
|
|
|
|
// TODO mouse blending
|
|
|
|
d3d11_device_unlock( &d3d_dev->d3d_dev );
|
|
|
|
return &d3d11_block->self;
|
|
error:
|
|
if (d3d11_block->d3d11_pic)
|
|
picture_Release(d3d11_block->d3d11_pic);
|
|
delete d3d11_block;
|
|
return nullptr;
|
|
}
|
|
|
|
static void screen_CloseCapture(screen_data_t *p_data)
|
|
{
|
|
delete p_data;
|
|
}
|
|
|
|
int screen_InitCaptureDXGI(demux_t *p_demux)
|
|
{
|
|
demux_sys_t *p_sys = static_cast<demux_sys_t*>(p_demux->p_sys);
|
|
screen_data_t *p_data;
|
|
vlc_decoder_device *dec_dev;
|
|
HRESULT hr;
|
|
|
|
if (var_CreateGetInteger( p_demux, "screen-fragment-size" ) != 0)
|
|
{
|
|
msg_Dbg(p_demux, "screen-fragment-size not supported in DXGI");
|
|
return VLC_ENOTSUP;
|
|
}
|
|
|
|
char *mousefile = var_InheritString( p_demux, "screen-mouse-image" );
|
|
free( mousefile );
|
|
if (mousefile)
|
|
{
|
|
msg_Dbg(p_demux, "screen-mouse-image not supported in DXGI");
|
|
return VLC_ENOTSUP;
|
|
}
|
|
|
|
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
|
if (p_sys->b_follow_mouse)
|
|
{
|
|
msg_Dbg(p_demux, "screen-follow-mouse not supported in UWP DXGI");
|
|
return VLC_ENOTSUP;
|
|
}
|
|
#endif // !WINAPI_PARTITION_DESKTOP
|
|
|
|
p_data = new (std::nothrow) screen_data_t();
|
|
if (unlikely(p_data == nullptr))
|
|
return VLC_ENOMEM;
|
|
|
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
|
p_data->screen_x = GetSystemMetrics( SM_XVIRTUALSCREEN );
|
|
p_data->screen_y = GetSystemMetrics( SM_YVIRTUALSCREEN );
|
|
#endif
|
|
|
|
dec_dev = vlc_decoder_device_Create(VLC_OBJECT(p_demux), nullptr);
|
|
d3d11_decoder_device_t *d3d11_dev = GetD3D11OpaqueDevice(dec_dev);
|
|
if (d3d11_dev == nullptr)
|
|
{
|
|
msg_Err(p_demux, "incompatible decoder device.");
|
|
}
|
|
else
|
|
{
|
|
ComPtr<ID3D11Device1> d3d11VLC1;
|
|
hr = d3d11_dev->d3d_dev.d3ddevice->QueryInterface(IID_GRAPHICS_PPV_ARGS(&d3d11VLC1));
|
|
if (FAILED(hr)) {
|
|
msg_Warn(p_demux, "D3D11 device cannot share textures. (hr=0x%lX)", hr);
|
|
goto error;
|
|
}
|
|
|
|
ComPtr<IDXGIDevice> pDXGIDevice;
|
|
HRESULT hr = d3d11_dev->d3d_dev.d3ddevice->QueryInterface(IID_GRAPHICS_PPV_ARGS(&pDXGIDevice));
|
|
if (FAILED(hr)) {
|
|
goto error;
|
|
}
|
|
|
|
ComPtr<IDXGIAdapter> adapter;
|
|
hr = pDXGIDevice->GetAdapter(&adapter);
|
|
if (FAILED(hr)) {
|
|
goto error;
|
|
}
|
|
|
|
ComPtr<IDXGIOutput1> output1;
|
|
for (UINT j=0; output1.Get()==nullptr; j++)
|
|
{
|
|
ComPtr<IDXGIOutput> output;
|
|
hr = adapter->EnumOutputs(j, &output);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
hr = output.As(&output1);
|
|
if (FAILED(hr))
|
|
continue;
|
|
|
|
hr = output1->DuplicateOutput(d3d11_dev->d3d_dev.d3ddevice, &p_data->duplication);
|
|
if (FAILED(hr))
|
|
output1.Reset();
|
|
}
|
|
}
|
|
|
|
if (!p_data->duplication)
|
|
goto error;
|
|
|
|
DXGI_OUTDUPL_DESC outDesc;
|
|
p_data->duplication->GetDesc(&outDesc);
|
|
|
|
p_data->output_format = D3D11_RenderFormat(outDesc.ModeDesc.Format ,true);
|
|
if (unlikely(!p_data->output_format->name))
|
|
{
|
|
msg_Err(p_demux, "Unknown texture format %d", outDesc.ModeDesc.Format);
|
|
goto error;
|
|
}
|
|
|
|
p_data->vctx = D3D11CreateVideoContext(dec_dev, p_data->output_format->formatTexture);
|
|
vlc_decoder_device_Release(dec_dev);
|
|
dec_dev = nullptr;
|
|
if (unlikely(p_data->vctx == nullptr))
|
|
goto error;
|
|
|
|
es_format_Init( &p_sys->fmt, VIDEO_ES, p_data->output_format->fourcc );
|
|
p_sys->fmt.video.i_visible_width =
|
|
p_sys->fmt.video.i_width = outDesc.ModeDesc.Width;
|
|
p_sys->fmt.video.i_visible_height =
|
|
p_sys->fmt.video.i_height = outDesc.ModeDesc.Height;
|
|
p_sys->fmt.video.i_bits_per_pixel = 4 * p_data->output_format->bitsPerChannel; /* FIXME */
|
|
p_sys->fmt.video.i_sar_num = p_sys->fmt.video.i_sar_den = 1;
|
|
p_sys->fmt.video.i_chroma = p_sys->fmt.i_codec;
|
|
p_sys->fmt.video.color_range = COLOR_RANGE_FULL;
|
|
p_sys->fmt.video.i_frame_rate = outDesc.ModeDesc.RefreshRate.Numerator;
|
|
p_sys->fmt.video.i_frame_rate_base = outDesc.ModeDesc.RefreshRate.Denominator;
|
|
DxgiFormatMask( outDesc.ModeDesc.Format, &p_sys->fmt.video );
|
|
|
|
p_sys->p_data = p_data;
|
|
static const screen_capture_operations ops = {
|
|
screen_Capture, screen_CloseCapture,
|
|
};
|
|
p_sys->ops = &ops;
|
|
|
|
return VLC_SUCCESS;
|
|
error:
|
|
if (dec_dev)
|
|
vlc_decoder_device_Release(dec_dev);
|
|
delete p_data;
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
int screen_InitCapture(demux_t *p_demux)
|
|
{
|
|
int ret = screen_InitCaptureDXGI(p_demux);
|
|
if (ret == VLC_SUCCESS)
|
|
return VLC_SUCCESS;
|
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
|
return screen_InitCaptureGDI(p_demux);
|
|
#else
|
|
return ret;
|
|
#endif
|
|
}
|
|
|