+++ /dev/null
-/* GStreamer
- * Copyright (C) 2019 OKADA Jun-ichi <okada@abt.jp>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-/* This code captures the screen using "Desktop Duplication API".
- * For more information
- * https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/desktop-dup-api */
-
-#include "dxgicapture.h"
-
-#include <d3dcompiler.h>
-#include <gmodule.h>
-
-GST_DEBUG_CATEGORY_EXTERN (gst_dxgi_screen_cap_src_debug);
-#define GST_CAT_DEFAULT gst_dxgi_screen_cap_src_debug
-
-#define PTR_RELEASE(p) {if(NULL!=(p)){IUnknown_Release((IUnknown *)(p)); (p) = NULL;}}
-#define BYTE_PER_PIXEL (4)
-
-/* vertex structures */
-typedef struct _vector3d
-{
- float x;
- float y;
- float z;
-} vector3d;
-
-typedef struct _vector2d
-{
- float x;
- float y;
-} vector2d;
-
-typedef struct _vertex
-{
- vector3d pos;
- vector2d texcoord;
-} vertex;
-#define VERTEX_NUM (6);
-
-typedef struct _DxgiCapture
-{
- GstDXGIScreenCapSrc *src;
-
- /*Direct3D pointers */
- ID3D11Device *d3d11_device;
- ID3D11DeviceContext *d3d11_context;
- IDXGIOutput1 *dxgi_output1;
- IDXGIOutputDuplication *dxgi_dupl;
-
- /* Texture that has been rotated and combined fragments. */
- ID3D11Texture2D *work_texture;
- D3D11_TEXTURE2D_DESC work_texture_desc;
- D3D11_VIEWPORT view_port;
- /* Textures that can be read by the CPU.
- * CPU-accessible textures are required separately from work_texture
- * because shaders cannot be executed. */
- ID3D11Texture2D *readable_texture;
- ID3D11VertexShader *vertex_shader;
- ID3D11PixelShader *pixel_shader;
- ID3D11SamplerState *sampler_state;
- ID3D11RenderTargetView *target_view;
- /* Screen output dimensions and rotation status.
- * The texture acquired by AcquireNextFrame has a non-rotated region. */
- DXGI_OUTDUPL_DESC dupl_desc;
-
- /* mouse pointer image */
- guint8 *pointer_buffer;
- gsize pointer_buffer_capacity;
-
- /* The movement rectangular regions and the movement
- * destination position from the previous frame. */
- DXGI_OUTDUPL_MOVE_RECT *move_rects;
- gsize move_rects_capacity;
-
- /* Array of dirty rectangular region for the desktop frame. */
- RECT *dirty_rects;
- gsize dirty_rects_capacity;
-
- /* Vertex buffer created from array of dirty rectangular region. */
- vertex *dirty_verteces;
- gsize verteces_capacity;
-
- /* Array of rectangular region to copy to readable_texture. */
- RECT *copy_rects;
- gsize copy_rects_capacity;
-
- /* latest mouse pointer info */
- DXGI_OUTDUPL_POINTER_SHAPE_INFO pointer_shape_info;
- DXGI_OUTDUPL_POINTER_POSITION last_pointer_position;
-
-} DxgiCapture;
-
-/* Vertex shader for texture rotation by HLSL. */
-static const char STR_VERTEX_SHADER[] =
- "struct vs_input { float4 pos : POSITION; float2 tex : TEXCOORD; }; "
- "struct vs_output { float4 pos : SV_POSITION; float2 tex : TEXCOORD; }; "
- "vs_output vs_main(vs_input input){return input;}";
-
-/* Pixel shader for texture rotation by HLSL. */
-static const char STR_PIXEL_SHADER[] =
- "Texture2D tx : register( t0 ); "
- "SamplerState samp : register( s0 ); "
- "struct ps_input { float4 pos : SV_POSITION; float2 tex : TEXCOORD;}; "
- "float4 ps_main(ps_input input) : "
- "SV_Target{ return tx.Sample( samp, input.tex ); }";
-
-/* initial buffer size */
-const int INITIAL_POINTER_BUFFER_CAPACITY = 64 * 64 * BYTE_PER_PIXEL;
-const int INITIAL_MOVE_RECTS_CAPACITY = 100;
-const int INITIAL_DIRTY_RECTS_CAPACITY = 100;
-const int INITIAL_VERTICES_CAPACITY = 100 * VERTEX_NUM;
-const int INITIAL_COPY_RECTS_CAPACITY = 100;
-
-static D3D_FEATURE_LEVEL feature_levels[] = {
- D3D_FEATURE_LEVEL_11_0,
- D3D_FEATURE_LEVEL_10_1,
- D3D_FEATURE_LEVEL_10_0,
- D3D_FEATURE_LEVEL_9_3,
- D3D_FEATURE_LEVEL_9_2,
- D3D_FEATURE_LEVEL_9_1,
-};
-
-static D3D11_INPUT_ELEMENT_DESC vertex_layout[] = {
- {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
- D3D11_INPUT_PER_VERTEX_DATA, 0},
- {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA,
- 0}
-};
-
-static void _draw_pointer (DxgiCapture * self, LPBYTE buffer, LPRECT dst_rect,
- int stride);
-static ID3D11Texture2D *_create_texture (DxgiCapture * self,
- enum D3D11_USAGE usage, UINT bindFlags, UINT cpuAccessFlags);
-
-static gboolean _setup_texture (DxgiCapture * self);
-
-static HRESULT _update_work_texture (DxgiCapture * self,
- IDXGIResource * desktop_resource);
-
-static HRESULT _copy_dirty_fragment (DxgiCapture * self,
- ID3D11Texture2D * src_texture, const D3D11_TEXTURE2D_DESC * src_desc,
- guint move_count, guint dirty_count, RECT ** dst_rect);
-
-static void _set_verteces (DxgiCapture * self, vertex * verteces,
- RECT * dest_rect, const D3D11_TEXTURE2D_DESC * dst_desc, RECT * rect,
- const D3D11_TEXTURE2D_DESC * src_desc);
-
-static GModule *d3d_compiler_module = NULL;
-static pD3DCompile GstD3DCompileFunc = NULL;
-
-gboolean
-gst_dxgicap_shader_init (void)
-{
- static gsize _init = 0;
- static const gchar *d3d_compiler_names[] = {
- "d3dcompiler_47.dll",
- "d3dcompiler_46.dll",
- "d3dcompiler_45.dll",
- "d3dcompiler_44.dll",
- "d3dcompiler_43.dll",
- };
-
- if (g_once_init_enter (&_init)) {
- gint i;
- for (i = 0; i < G_N_ELEMENTS (d3d_compiler_names); i++) {
- d3d_compiler_module =
- g_module_open (d3d_compiler_names[i], G_MODULE_BIND_LAZY);
-
- if (d3d_compiler_module) {
- GST_INFO ("D3D compiler %s is available", d3d_compiler_names[i]);
- if (!g_module_symbol (d3d_compiler_module, "D3DCompile",
- (gpointer *) & GstD3DCompileFunc)) {
- GST_ERROR ("Cannot load D3DCompile symbol from %s",
- d3d_compiler_names[i]);
- g_module_close (d3d_compiler_module);
- d3d_compiler_module = NULL;
- GstD3DCompileFunc = NULL;
- } else {
- break;
- }
- }
- }
-
- if (!GstD3DCompileFunc)
- GST_WARNING ("D3D11 compiler library is unavailable");
-
- g_once_init_leave (&_init, 1);
- }
-
- return ! !GstD3DCompileFunc;
-}
-
-static GstFlowReturn
-initialize_output_duplication (DxgiCapture * self)
-{
- HDESK hdesk;
- HRESULT hr;
- DXGI_OUTDUPL_DESC old_dupl_desc;
- GstDXGIScreenCapSrc *src = self->src;
-
- PTR_RELEASE (self->dxgi_dupl);
-
- hdesk = OpenInputDesktop (0, FALSE, GENERIC_ALL);
- if (hdesk) {
- if (!SetThreadDesktop (hdesk)) {
- GST_WARNING_OBJECT (src, "SetThreadDesktop() failed. Error code: %lu",
- GetLastError ());
- }
-
- CloseDesktop (hdesk);
- } else {
- GST_WARNING_OBJECT (src, "OpenInputDesktop() failed. Error code: %lu",
- GetLastError ());
- }
-
- hr = IDXGIOutput1_DuplicateOutput (self->dxgi_output1,
- (IUnknown *) (self->d3d11_device), &self->dxgi_dupl);
- if (hr != S_OK) {
- gchar *msg = get_hresult_to_string (hr);
- GST_WARNING_OBJECT (src, "IDXGIOutput1::DuplicateOutput() failed (%x): %s",
- (guint) hr, msg);
- g_free (msg);
- if (hr == E_ACCESSDENIED) {
- /* Happens temporarily during resolution changes. */
- return GST_FLOW_OK;
- }
- return GST_FLOW_ERROR;
- }
-
- old_dupl_desc = self->dupl_desc;
- IDXGIOutputDuplication_GetDesc (self->dxgi_dupl, &self->dupl_desc);
-
- if (self->readable_texture &&
- (self->dupl_desc.ModeDesc.Width != old_dupl_desc.ModeDesc.Width ||
- self->dupl_desc.ModeDesc.Height != old_dupl_desc.ModeDesc.Height ||
- self->dupl_desc.Rotation != old_dupl_desc.Rotation)) {
- PTR_RELEASE (self->readable_texture);
- PTR_RELEASE (self->work_texture);
-
- _setup_texture (self);
-
- return GST_DXGICAP_FLOW_RESOLUTION_CHANGE;
- }
-
- return GST_FLOW_OK;
-}
-
-DxgiCapture *
-dxgicap_new (HMONITOR monitor, GstDXGIScreenCapSrc * src)
-{
- int i, j;
- HRESULT hr;
- IDXGIFactory1 *dxgi_factory1 = NULL;
- IDXGIAdapter1 *dxgi_adapter1 = NULL;
- ID3D11InputLayout *vertex_input_layout = NULL;
- ID3DBlob *vertex_shader_blob = NULL;
- ID3DBlob *pixel_shader_blob = NULL;
- D3D11_SAMPLER_DESC sampler_desc;
-
- DxgiCapture *self = g_new0 (DxgiCapture, 1);
- if (NULL == self) {
- return NULL;
- }
-
- self->src = src;
- hr = CreateDXGIFactory1 (&IID_IDXGIFactory1, (void **) &dxgi_factory1);
- HR_FAILED_GOTO (hr, CreateDXGIFactory1, new_error);
-
- for (i = 0;
- IDXGIFactory1_EnumAdapters1 (dxgi_factory1, i,
- &dxgi_adapter1) != DXGI_ERROR_NOT_FOUND; ++i) {
- IDXGIOutput *dxgi_output = NULL;
- D3D_FEATURE_LEVEL feature_level;
-
- hr = D3D11CreateDevice ((IDXGIAdapter *) dxgi_adapter1,
- D3D_DRIVER_TYPE_UNKNOWN, NULL, 0,
- feature_levels, G_N_ELEMENTS (feature_levels),
- D3D11_SDK_VERSION, &self->d3d11_device, &feature_level,
- &self->d3d11_context);
- if (FAILED (hr)) {
- HR_FAILED_INFO (hr, D3D11CreateDevice);
- PTR_RELEASE (dxgi_adapter1);
- continue;
- }
-
- for (j = 0; IDXGIAdapter1_EnumOutputs (dxgi_adapter1, j, &dxgi_output) !=
- DXGI_ERROR_NOT_FOUND; ++j) {
- DXGI_OUTPUT_DESC output_desc;
- hr = IDXGIOutput_QueryInterface (dxgi_output, &IID_IDXGIOutput1,
- (void **) &self->dxgi_output1);
- PTR_RELEASE (dxgi_output);
- HR_FAILED_GOTO (hr, IDXGIOutput::QueryInterface, new_error);
-
- hr = IDXGIOutput1_GetDesc (self->dxgi_output1, &output_desc);
- HR_FAILED_GOTO (hr, IDXGIOutput1::GetDesc, new_error);
-
- if (output_desc.Monitor == monitor) {
- GST_DEBUG_OBJECT (src, "found monitor");
- break;
- }
-
- PTR_RELEASE (self->dxgi_output1);
- }
-
- PTR_RELEASE (dxgi_adapter1);
-
- if (NULL != self->dxgi_output1) {
- break;
- }
-
- PTR_RELEASE (self->d3d11_device);
- PTR_RELEASE (self->d3d11_context);
- }
-
- if (NULL == self->dxgi_output1) {
- goto new_error;
- }
-
- PTR_RELEASE (dxgi_factory1);
-
- if (initialize_output_duplication (self) == GST_FLOW_ERROR) {
- goto new_error;
- }
-
- self->pointer_buffer_capacity = INITIAL_POINTER_BUFFER_CAPACITY;
- self->pointer_buffer = g_malloc (self->pointer_buffer_capacity);
- if (NULL == self->pointer_buffer) {
- goto new_error;
- }
-
- self->move_rects_capacity = INITIAL_MOVE_RECTS_CAPACITY;
- self->move_rects = g_new0 (DXGI_OUTDUPL_MOVE_RECT, self->move_rects_capacity);
- if (NULL == self->move_rects) {
- goto new_error;
- }
-
- self->dirty_rects_capacity = INITIAL_DIRTY_RECTS_CAPACITY;
- self->dirty_rects = g_new0 (RECT, self->dirty_rects_capacity);
- if (NULL == self->dirty_rects) {
- goto new_error;
- }
-
- self->verteces_capacity = INITIAL_VERTICES_CAPACITY;
- self->dirty_verteces = g_new0 (vertex, self->verteces_capacity);
- if (NULL == self->dirty_verteces) {
- goto new_error;
- }
-
- self->copy_rects_capacity = INITIAL_COPY_RECTS_CAPACITY;
- self->copy_rects = g_new0 (RECT, self->copy_rects_capacity);
- if (NULL == self->copy_rects) {
- goto new_error;
- }
-
- if (DXGI_MODE_ROTATION_IDENTITY != self->dupl_desc.Rotation) {
- g_assert (GstD3DCompileFunc);
-
- /* For a rotated display, create a shader. */
- hr = GstD3DCompileFunc (STR_VERTEX_SHADER, sizeof (STR_VERTEX_SHADER),
- NULL, NULL, NULL, "vs_main", "vs_4_0_level_9_1",
- 0, 0, &vertex_shader_blob, NULL);
- HR_FAILED_GOTO (hr, D3DCompile, new_error);
-
- hr = GstD3DCompileFunc (STR_PIXEL_SHADER, sizeof (STR_PIXEL_SHADER),
- NULL, NULL, NULL, "ps_main", "ps_4_0_level_9_1",
- 0, 0, &pixel_shader_blob, NULL);
- HR_FAILED_GOTO (hr, D3DCompile, new_error);
-
- hr = ID3D11Device_CreateVertexShader (self->d3d11_device,
- ID3D10Blob_GetBufferPointer (vertex_shader_blob),
- ID3D10Blob_GetBufferSize (vertex_shader_blob), NULL,
- &self->vertex_shader);
- HR_FAILED_GOTO (hr, ID3D11Device::CreateVertexShader, new_error);
-
- hr = ID3D11Device_CreateInputLayout (self->d3d11_device, vertex_layout,
- G_N_ELEMENTS (vertex_layout),
- ID3D10Blob_GetBufferPointer (vertex_shader_blob),
- ID3D10Blob_GetBufferSize (vertex_shader_blob), &vertex_input_layout);
- PTR_RELEASE (vertex_shader_blob)
- HR_FAILED_GOTO (hr, ID3D11Device::CreateInputLayout, new_error);
-
- ID3D11DeviceContext_IASetInputLayout (self->d3d11_context,
- vertex_input_layout);
- PTR_RELEASE (vertex_input_layout);
-
- hr = ID3D11Device_CreatePixelShader (self->d3d11_device,
- ID3D10Blob_GetBufferPointer (pixel_shader_blob),
- ID3D10Blob_GetBufferSize (pixel_shader_blob), NULL,
- &self->pixel_shader);
- PTR_RELEASE (pixel_shader_blob);
- HR_FAILED_GOTO (hr, ID3D11Device::CreatePixelShader, new_error);
-
- memset (&sampler_desc, 0, sizeof (sampler_desc));
- sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
- sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
- sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
- sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
- sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
- sampler_desc.MinLOD = 0;
- sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
-
- hr = ID3D11Device_CreateSamplerState (self->d3d11_device, &sampler_desc,
- &self->sampler_state);
- HR_FAILED_GOTO (hr, ID3D11Device::CreateSamplerState, new_error);
- }
-
- return self;
-
-new_error:
- PTR_RELEASE (vertex_input_layout);
- PTR_RELEASE (vertex_shader_blob);
- PTR_RELEASE (pixel_shader_blob);
-
- dxgicap_destory (self);
- return NULL;
-}
-
-void
-dxgicap_destory (DxgiCapture * self)
-{
- if (!self)
- return;
- PTR_RELEASE (self->target_view);
- PTR_RELEASE (self->readable_texture);
- PTR_RELEASE (self->work_texture);
- PTR_RELEASE (self->dxgi_output1);
- PTR_RELEASE (self->dxgi_dupl);
- PTR_RELEASE (self->d3d11_context);
- PTR_RELEASE (self->d3d11_device);
- PTR_RELEASE (self->vertex_shader);
- PTR_RELEASE (self->pixel_shader);
- PTR_RELEASE (self->sampler_state);
-
- g_free (self->pointer_buffer);
- g_free (self->move_rects);
- g_free (self->dirty_rects);
- g_free (self->dirty_verteces);
- g_free (self->copy_rects);
-
- g_free (self);
-}
-
-gboolean
-dxgicap_start (DxgiCapture * self)
-{
- return _setup_texture (self);
-}
-
-void
-dxgicap_stop (DxgiCapture * self)
-{
- PTR_RELEASE (self->target_view);
- PTR_RELEASE (self->readable_texture);
- PTR_RELEASE (self->work_texture);
-}
-
-GstFlowReturn
-dxgicap_acquire_next_frame (DxgiCapture * self, gboolean show_cursor,
- guint timeout)
-{
- GstFlowReturn ret = GST_FLOW_ERROR;
- HRESULT hr;
- GstDXGIScreenCapSrc *src = self->src;
-
- DXGI_OUTDUPL_FRAME_INFO frame_info;
- IDXGIResource *desktop_resource = NULL;
-
- if (!self->dxgi_dupl) {
- /* Desktop duplication interface became invalid due to desktop switch,
- * UAC prompt popping up, or similar event. Try to reinitialize. */
- ret = initialize_output_duplication (self);
- goto end;
- }
-
- /* Get the latest desktop frames. */
- hr = IDXGIOutputDuplication_AcquireNextFrame (self->dxgi_dupl,
- timeout, &frame_info, &desktop_resource);
- if (hr == DXGI_ERROR_WAIT_TIMEOUT) {
- /* In case of DXGI_ERROR_WAIT_TIMEOUT,
- * it has not changed from the last time. */
- GST_LOG_OBJECT (src, "DXGI_ERROR_WAIT_TIMEOUT");
- ret = GST_FLOW_OK;
- goto end;
- } else if (hr == DXGI_ERROR_ACCESS_LOST) {
- GST_LOG_OBJECT (src, "DXGI_ERROR_ACCESS_LOST; reinitializing output "
- "duplication...");
- PTR_RELEASE (self->dxgi_dupl);
- ret = GST_FLOW_OK;
- goto end;
- }
- HR_FAILED_GOTO (hr, IDXGIOutputDuplication::AcquireNextFrame, end);
-
- if (0 != frame_info.LastPresentTime.QuadPart) {
- /* The desktop frame has changed since last time. */
- hr = _update_work_texture (self, desktop_resource);
- if (FAILED (hr)) {
- GST_DEBUG_OBJECT (src, "failed to _update_work_texture");
- goto end;
- }
- }
-
- if (show_cursor && 0 != frame_info.LastMouseUpdateTime.QuadPart) {
- /* The mouse pointer has changed since last time. */
- self->last_pointer_position = frame_info.PointerPosition;
-
- if (0 < frame_info.PointerShapeBufferSize) {
- /* A valid mouse cursor shape exists. */
- DXGI_OUTDUPL_POINTER_SHAPE_INFO pointer_shape_info;
- guint pointer_shape_size_required;
- /* Get the mouse cursor shape. */
- hr = IDXGIOutputDuplication_GetFramePointerShape (self->dxgi_dupl,
- self->pointer_buffer_capacity,
- self->pointer_buffer,
- &pointer_shape_size_required, &pointer_shape_info);
- if (DXGI_ERROR_MORE_DATA == hr) {
- /* not enough buffers */
- self->pointer_buffer_capacity = pointer_shape_size_required * 2;
- self->pointer_buffer =
- g_realloc (self->pointer_buffer, self->pointer_buffer_capacity);
-
- hr = IDXGIOutputDuplication_GetFramePointerShape (self->dxgi_dupl,
- self->pointer_buffer_capacity,
- self->pointer_buffer,
- &pointer_shape_size_required, &pointer_shape_info);
- }
- HR_FAILED_GOTO (hr, IDXGIOutputDuplication::GetFramePointerShape, end);
- self->pointer_shape_info = pointer_shape_info;
- ret = GST_FLOW_OK;
- } else {
- ret = GST_FLOW_OK;
- }
- } else {
- ret = GST_FLOW_OK;
- }
-end:
- if (self->dxgi_dupl) {
- IDXGIOutputDuplication_ReleaseFrame (self->dxgi_dupl);
- }
- PTR_RELEASE (desktop_resource);
- return ret;
-}
-
-gboolean
-dxgicap_copy_buffer (DxgiCapture * self, gboolean show_cursor, LPRECT dst_rect,
- GstVideoInfo * video_info, GstBuffer * buf)
-{
- HRESULT hr;
- int i;
- GstDXGIScreenCapSrc *src = self->src;
- D3D11_MAPPED_SUBRESOURCE readable_map;
- GstVideoFrame vframe;
- gint height = RECT_HEIGHT ((*dst_rect));
- gint width = RECT_WIDTH ((*dst_rect));
-
- if (NULL == self->readable_texture) {
- GST_DEBUG_OBJECT (src, "readable_texture is null");
- goto flow_error;
- }
-
- hr = ID3D11DeviceContext_Map (self->d3d11_context,
- (ID3D11Resource *) self->readable_texture, 0,
- D3D11_MAP_READ, 0, &readable_map);
- HR_FAILED_GOTO (hr, IDXGISurface1::Map, flow_error);
- GST_DEBUG_OBJECT (src, "copy size width:%d height:%d", width, height);
-
- /* Copy from readable_texture to GstVideFrame. */
- if (gst_video_frame_map (&vframe, video_info, buf, GST_MAP_WRITE)) {
- gint line_size;
- gint stride_dst;
- PBYTE frame_buffer;
- PBYTE p_dst;
- PBYTE p_src;
-
- frame_buffer = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0);
- p_src = (PBYTE) readable_map.pData +
- (dst_rect->top * readable_map.RowPitch) +
- (dst_rect->left * BYTE_PER_PIXEL);
- p_dst = frame_buffer;
-
- line_size = width * BYTE_PER_PIXEL;
- stride_dst = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
-
- if (line_size > stride_dst) {
- GST_ERROR_OBJECT (src, "not enough stride in video frame");
- ID3D11DeviceContext_Unmap (self->d3d11_context,
- (ID3D11Resource *) self->readable_texture, 0);
- gst_video_frame_unmap (&vframe);
- goto flow_error;
- }
-
- for (i = 0; i < height; ++i) {
- memcpy (p_dst, p_src, line_size);
- p_dst += stride_dst;
- p_src += readable_map.RowPitch;
- }
- ID3D11DeviceContext_Unmap (self->d3d11_context,
- (ID3D11Resource *) self->readable_texture, 0);
- HR_FAILED_GOTO (hr, IDXGISurface1::Unmap, flow_error);
-
- if (show_cursor && self->last_pointer_position.Visible) {
- _draw_pointer (self, frame_buffer, dst_rect, stride_dst);
- }
- gst_video_frame_unmap (&vframe);
- return TRUE;
- }
-
-flow_error:
- return FALSE;
-}
-
-static void
-_draw_pointer (DxgiCapture * self, PBYTE buffer, LPRECT dst_rect, int stride)
-{
- RECT pointer_rect;
- RECT clip_pointer_rect;
- int offset_x;
- int offset_y;
- PBYTE p_dst;
- /* For DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME, halve the height. */
- int pointer_height =
- (DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME ==
- self->pointer_shape_info.Type)
- ? self->pointer_shape_info.Height / 2 : self->pointer_shape_info.Height;
-
- /* A rectangular area containing the mouse pointer shape */
- SetRect (&pointer_rect,
- self->last_pointer_position.Position.x,
- self->last_pointer_position.Position.y,
- self->last_pointer_position.Position.x +
- self->pointer_shape_info.Width,
- self->last_pointer_position.Position.y + pointer_height);
-
- if (!IntersectRect (&clip_pointer_rect, dst_rect, &pointer_rect)) {
- return;
- }
-
- /* Draw a pointer if it overlaps the destination rectangle range.
- * There are three ways to draw the mouse cursor.
- * see https://docs.microsoft.com/ja-jp/windows/win32/api/dxgi1_2/ne-dxgi1_2-dxgi_outdupl_pointer_shape_type */
- offset_x = clip_pointer_rect.left - pointer_rect.left;
- offset_y = clip_pointer_rect.top - pointer_rect.top;
- p_dst =
- ((PBYTE) buffer) + ((clip_pointer_rect.top -
- dst_rect->top) * stride) +
- ((clip_pointer_rect.left - dst_rect->left) * BYTE_PER_PIXEL);
-
- if (DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR ==
- self->pointer_shape_info.Type
- || DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR ==
- self->pointer_shape_info.Type) {
- gboolean mask_mode =
- DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR ==
- self->pointer_shape_info.Type;
- PBYTE p_src =
- (PBYTE) self->pointer_buffer +
- (offset_y * self->pointer_shape_info.Pitch) +
- (offset_x * BYTE_PER_PIXEL);
-
- int y, x;
- for (y = 0; y < RECT_HEIGHT (clip_pointer_rect); ++y) {
- for (x = 0; x < RECT_WIDTH (clip_pointer_rect); ++x) {
- PBYTE p1 = p_dst + (x * BYTE_PER_PIXEL);
- PBYTE p2 = p_src + (x * BYTE_PER_PIXEL);
- int alpha = *(p2 + 3);
- int i;
- for (i = 0; i < 3; ++i) {
- if (mask_mode) {
- /* case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
- * If the alpha channel of a pixel in the mouse image is 0, copy it.
- * Otherwise, xor each pixel. */
- if (0 == alpha) {
- *p1 = *p2;
- } else {
- *p1 = *p2 ^ *p1;
- }
- } else {
- /* case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
- * Copies the mouse cursor image with alpha channel composition. */
- *p1 = min (255, max (0, *p1 + ((*p2 - *p1) * alpha / 255)));
- }
- ++p1;
- ++p2;
- }
- }
- p_dst += stride;
- p_src += self->pointer_shape_info.Pitch;
- }
- } else if (DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME ==
- self->pointer_shape_info.Type) {
- guint mask_bit = 0x80;
- /* AND MASK pointer
- * It is stored in 1 bit per pixel from the beginning. */
- PBYTE p_src_and =
- (PBYTE) self->pointer_buffer +
- (offset_y * self->pointer_shape_info.Pitch);
- /* XOR MASK pointer
- * The XOR MASK is stored after the AND mask. */
- PBYTE p_src_xor =
- (PBYTE) self->pointer_buffer +
- ((offset_y + pointer_height) * self->pointer_shape_info.Pitch);
-
- int y, x;
- for (y = 0; y < RECT_HEIGHT (clip_pointer_rect); ++y) {
- guint32 *p_dst_32 = ((guint32 *) (p_dst));
- for (x = offset_x; x < RECT_WIDTH (clip_pointer_rect); ++x) {
- int bit_pos = x % 8;
- gboolean and_bit =
- 0 != (*(p_src_and + (x / 8)) & (mask_bit >> bit_pos));
- gboolean xor_bit =
- 0 != (*(p_src_xor + (x / 8)) & (mask_bit >> bit_pos));
-
- if (and_bit) {
- if (xor_bit) {
- *p_dst_32 = *p_dst_32 ^ 0x00ffffff;
- }
- } else {
- if (xor_bit) {
- *p_dst_32 = 0xffffffff;
- } else {
- *p_dst_32 = 0xff000000;
- }
- }
- ++p_dst_32;
- }
- p_dst += stride;
- p_src_and += self->pointer_shape_info.Pitch;
- p_src_xor += self->pointer_shape_info.Pitch;
- }
- }
-}
-
-static ID3D11Texture2D *
-_create_texture (DxgiCapture * self,
- enum D3D11_USAGE usage, UINT bindFlags, UINT cpuAccessFlags)
-{
- HRESULT hr;
- GstDXGIScreenCapSrc *src = self->src;
- D3D11_TEXTURE2D_DESC new_desc;
- ID3D11Texture2D *new_texture = NULL;
-
- ZeroMemory (&new_desc, sizeof (new_desc));
- new_desc.Width = self->dupl_desc.ModeDesc.Width;
- new_desc.Height = self->dupl_desc.ModeDesc.Height;
- new_desc.MipLevels = 1;
- new_desc.ArraySize = 1;
- new_desc.SampleDesc.Count = 1;
- new_desc.SampleDesc.Quality = 0;
- new_desc.Usage = usage;
- new_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
- new_desc.BindFlags = bindFlags;
- new_desc.CPUAccessFlags = cpuAccessFlags;
- new_desc.MiscFlags = 0;
-
- hr = ID3D11Device_CreateTexture2D (self->d3d11_device, &new_desc, NULL,
- &new_texture);
- HR_FAILED_RET (hr, ID3D11Device::CreateTexture2D, NULL);
-
- return new_texture;
-}
-
-static gboolean
-_setup_texture (DxgiCapture * self)
-{
- HRESULT hr;
- ID3D11Texture2D *new_texture = NULL;
- GstDXGIScreenCapSrc *src = self->src;
-
- if (NULL == self->readable_texture) {
- new_texture = _create_texture (self, D3D11_USAGE_STAGING, 0,
- D3D11_CPU_ACCESS_READ);
- if (NULL == new_texture) {
- return FALSE;
- }
- self->readable_texture = new_texture;
- }
-
- if (DXGI_MODE_ROTATION_IDENTITY != self->dupl_desc.Rotation) {
- /* For rotated displays, create work_texture. */
- if (NULL == self->work_texture) {
- new_texture =
- _create_texture (self, D3D11_USAGE_DEFAULT,
- D3D11_BIND_RENDER_TARGET, 0);
- if (NULL == new_texture) {
- return FALSE;
- }
-
- self->work_texture = new_texture;
- ID3D11Texture2D_GetDesc (self->work_texture, &self->work_texture_desc);
- hr = ID3D11Device_CreateRenderTargetView (self->d3d11_device,
- (ID3D11Resource *) self->work_texture, NULL, &self->target_view);
- HR_FAILED_RET (hr, ID3D11Device::CreateRenderTargetView, FALSE);
-
- self->view_port.Width = (float) self->work_texture_desc.Width;
- self->view_port.Height = (float) self->work_texture_desc.Height;
- self->view_port.MinDepth = 0.0f;
- self->view_port.MaxDepth = 1.0f;
- self->view_port.TopLeftX = 0.0f;
- self->view_port.TopLeftY = 0.0f;
- }
- }
-
- return TRUE;
-}
-
-/* Update work_texture to the latest desktop frame from the update information
- * that can be obtained from IDXGIOutputDuplication.
- * Then copy to readable_texture.
- */
-static HRESULT
-_update_work_texture (DxgiCapture * self, IDXGIResource * desktop_resource)
-{
- HRESULT hr = S_OK;
- GstDXGIScreenCapSrc *src = self->src;
- int i;
- ID3D11Texture2D *desktop_texture = NULL;
- guint required_size;
- guint move_count;
- guint dirty_rects_capacity_size;
- guint dirty_count;
- guint copy_count;
- D3D11_TEXTURE2D_DESC src_desc;
- RECT *dst_rect;
- ID3D11Texture2D *work_src;
- guint move_rects_capacity_size =
- sizeof (DXGI_OUTDUPL_MOVE_RECT) * self->move_rects_capacity;
-
- hr = IDXGIResource_QueryInterface (desktop_resource, &IID_ID3D11Texture2D,
- (void **) &desktop_texture);
- HR_FAILED_GOTO (hr, IDXGIResource::QueryInterface, end);
-
- /* Get the rectangular regions that was moved from the last time.
- * However, I have never obtained a valid value in GetFrameMoveRects.
- * It seems to depend on the implementation of the GPU driver.
- * see https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_2/nf-dxgi1_2-idxgioutputduplication-getframemoverects
- */
- hr = IDXGIOutputDuplication_GetFrameMoveRects (self->dxgi_dupl,
- move_rects_capacity_size, self->move_rects, &required_size);
- if (DXGI_ERROR_MORE_DATA == hr) {
- /* not enough buffers */
- self->move_rects_capacity =
- (required_size / sizeof (DXGI_OUTDUPL_MOVE_RECT)) * 2;
- self->move_rects =
- g_renew (DXGI_OUTDUPL_MOVE_RECT, self->move_rects,
- self->move_rects_capacity);
-
- hr = IDXGIOutputDuplication_GetFrameMoveRects (self->dxgi_dupl,
- required_size, self->move_rects, &required_size);
- }
- HR_FAILED_GOTO (hr, IDXGIOutputDuplication::GetFrameMoveRects, end);
- move_count = required_size / sizeof (DXGI_OUTDUPL_MOVE_RECT);
-
- dirty_rects_capacity_size = sizeof (RECT) * self->dirty_rects_capacity;
- /* Gets the rectangular regions that has changed since the last time.
- see https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_2/nf-dxgi1_2-idxgioutputduplication-getframedirtyrects
- */
- hr = IDXGIOutputDuplication_GetFrameDirtyRects (self->dxgi_dupl,
- dirty_rects_capacity_size, self->dirty_rects, &required_size);
-
- if (DXGI_ERROR_MORE_DATA == hr) {
- /* not enough buffers */
- self->dirty_rects_capacity = (required_size / sizeof (RECT)) * 2;
- self->dirty_rects =
- g_renew (RECT, self->dirty_rects, self->dirty_rects_capacity);
-
- hr = IDXGIOutputDuplication_GetFrameDirtyRects (self->dxgi_dupl,
- required_size, self->dirty_rects, &required_size);
- }
- HR_FAILED_GOTO (hr, IDXGIOutputDuplication::GetFrameDirtyRects, end);
-
- dirty_count = required_size / sizeof (RECT);
-
- /* The number of rectangular regions to copy to the readable_texture. */
- copy_count = move_count + dirty_count;
-
- if (self->copy_rects_capacity < copy_count) {
- /* not enough buffers */
- self->copy_rects_capacity = copy_count * 2;
- self->copy_rects =
- g_renew (RECT, self->copy_rects, self->copy_rects_capacity);
- }
-
- if (DXGI_MODE_ROTATION_IDENTITY == self->dupl_desc.Rotation) {
- /* For a non-rotating display, copy it directly into readable_texture. */
- RECT *p = self->copy_rects;
- for (i = 0; i < move_count; ++i) {
- *p = self->move_rects[i].DestinationRect;
- ++p;
- }
- for (i = 0; i < dirty_count; ++i) {
- *p = self->dirty_rects[i];
- ++p;
- }
- work_src = desktop_texture;
- } else {
- /* For rotated displays, rotate to work_texture and copy. */
- ID3D11Texture2D_GetDesc (desktop_texture, &src_desc);
- dst_rect = self->copy_rects;
- /* Copy the dirty rectangular and moved rectangular regions from desktop frame to work_texture. */
- hr = _copy_dirty_fragment (self, desktop_texture, &src_desc, move_count,
- dirty_count, &dst_rect);
- work_src = self->work_texture;
- if (FAILED (hr)) {
- goto end;
- }
- }
-
- /* Copy the updated rectangular regions to readable_texture. */
- for (i = 0; i < copy_count; ++i) {
- RECT *p = (self->copy_rects + i);
- D3D11_BOX box;
- box.left = p->left;
- box.top = p->top;
- box.front = 0;
- box.right = p->right;
- box.bottom = p->bottom;
- box.back = 1;
-
- ID3D11DeviceContext_CopySubresourceRegion (self->d3d11_context,
- (ID3D11Resource *) self->readable_texture,
- 0, p->left, p->top, 0, (ID3D11Resource *) work_src, 0, &box);
- }
-
-end:
- PTR_RELEASE (desktop_texture);
- return hr;
-}
-
-static void
-_rotate_rect (DXGI_MODE_ROTATION rotation, RECT * dst, const RECT * src,
- gint dst_width, gint dst_height)
-{
- switch (rotation) {
- case DXGI_MODE_ROTATION_ROTATE90:
- dst->left = dst_width - src->bottom;
- dst->top = src->left;
- dst->right = dst_width - src->top;
- dst->bottom = src->right;
- break;
- case DXGI_MODE_ROTATION_ROTATE180:
- dst->left = dst_width - src->right;
- dst->top = dst_height - src->bottom;
- dst->right = dst_width - src->left;
- dst->bottom = dst_height - src->top;
- break;
- case DXGI_MODE_ROTATION_ROTATE270:
- dst->left = src->top;
- dst->top = dst_height - src->right;
- dst->right = src->bottom;
- dst->bottom = dst_height - src->left;
- break;
- default:
- *dst = *src;
- break;
- }
-}
-
-/* Copy the rectangular area specified by dirty_rects and move_rects from src_texture to work_texture. */
-static HRESULT
-_copy_dirty_fragment (DxgiCapture * self, ID3D11Texture2D * src_texture,
- const D3D11_TEXTURE2D_DESC * src_desc, guint move_count, guint dirty_count,
- RECT ** dst_rect)
-{
- HRESULT hr = S_OK;
- GstDXGIScreenCapSrc *src = self->src;
- int i;
- RECT *dst_rect_p;
- vertex *vp;
- UINT stride;
- UINT offset;
- guint verteces_count;
- ID3D11Buffer *verteces_buffer = NULL;
- ID3D11ShaderResourceView *shader_resource = NULL;
- D3D11_SUBRESOURCE_DATA subresource_data;
- D3D11_BUFFER_DESC buffer_desc;
- D3D11_SHADER_RESOURCE_VIEW_DESC shader_desc;
-
- shader_desc.Format = src_desc->Format;
- shader_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
- shader_desc.Texture2D.MostDetailedMip = src_desc->MipLevels - 1;
- shader_desc.Texture2D.MipLevels = src_desc->MipLevels;
- hr = ID3D11Device_CreateShaderResourceView (self->d3d11_device,
- (ID3D11Resource *) src_texture, &shader_desc, &shader_resource);
- HR_FAILED_GOTO (hr, ID3D11Device::CreateShaderResourceView, end);
-
- ID3D11DeviceContext_OMSetRenderTargets (self->d3d11_context, 1,
- &self->target_view, NULL);
-
- ID3D11DeviceContext_VSSetShader (self->d3d11_context, self->vertex_shader,
- NULL, 0);
-
- ID3D11DeviceContext_PSSetShader (self->d3d11_context, self->pixel_shader,
- NULL, 0);
-
- ID3D11DeviceContext_PSSetShaderResources (self->d3d11_context, 0, 1,
- &shader_resource);
-
- ID3D11DeviceContext_PSSetSamplers (self->d3d11_context, 0, 1,
- &self->sampler_state);
-
- ID3D11DeviceContext_IASetPrimitiveTopology (self->d3d11_context,
- D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
-
- verteces_count = (move_count + dirty_count) * VERTEX_NUM;
- if (verteces_count > self->verteces_capacity) {
- /* not enough buffers */
- self->verteces_capacity = verteces_count * 2;
- self->dirty_verteces =
- g_renew (vertex, self->dirty_verteces, self->verteces_capacity);
- if (NULL == self->dirty_verteces) {
- hr = S_FALSE;
- goto end;
- }
- }
-
- dst_rect_p = *dst_rect;
- vp = self->dirty_verteces;
- /* Create a vertex buffer to move and rotate from the move_rects.
- * And set the rectangular region to be copied to readable_texture. */
- for (i = 0; i < move_count; ++i) {
- /* Copy the area to be moved.
- * The source of the move is included in dirty_rects. */
- _set_verteces (self, vp, dst_rect_p, &self->work_texture_desc,
- &(self->move_rects[i].DestinationRect), src_desc);
- vp += VERTEX_NUM;
- ++dst_rect_p;
- }
- /* Create a vertex buffer to move and rotate from the dirty_rects.
- * And set the rectangular region to be copied to readable_texture. */
- for (i = 0; i < dirty_count; ++i) {
- _set_verteces (self, vp, dst_rect_p, &self->work_texture_desc,
- &(self->dirty_rects[i]), src_desc);
- vp += VERTEX_NUM;
- ++dst_rect_p;
- }
- *dst_rect = dst_rect_p;
-
- memset (&buffer_desc, 0, sizeof (buffer_desc));
- buffer_desc.Usage = D3D11_USAGE_IMMUTABLE;
- buffer_desc.ByteWidth = verteces_count * sizeof (vertex);
- buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
- buffer_desc.CPUAccessFlags = 0;
-
- memset (&subresource_data, 0, sizeof (subresource_data));
- subresource_data.pSysMem = self->dirty_verteces;
-
- hr = ID3D11Device_CreateBuffer (self->d3d11_device, &buffer_desc,
- &subresource_data, &verteces_buffer);
- HR_FAILED_GOTO (hr, ID3D11Device::CreateBuffer, end);
-
- stride = sizeof (vertex);
- offset = 0;
- ID3D11DeviceContext_IASetVertexBuffers (self->d3d11_context, 0, 1,
- &verteces_buffer, &stride, &offset);
-
- ID3D11DeviceContext_RSSetViewports (self->d3d11_context, 1, &self->view_port);
-
- /* Copy the rectangular region indicated by dirty_rects from the desktop frame to work_texture. */
- ID3D11DeviceContext_Draw (self->d3d11_context, verteces_count, 0);
-
-end:
- PTR_RELEASE (verteces_buffer);
- PTR_RELEASE (shader_resource);
-
- return hr;
-}
-
-static void
-_set_verteces (DxgiCapture * self, vertex * verteces, RECT * dst_rect,
- const D3D11_TEXTURE2D_DESC * dst_desc, RECT * rect,
- const D3D11_TEXTURE2D_DESC * src_desc)
-{
- int center_x;
- int center_y;
-
- /* Rectangular area is moved according to the rotation of the display. */
- _rotate_rect (self->dupl_desc.Rotation, dst_rect, rect, dst_desc->Width,
- dst_desc->Height);
-
- /* Set the vertex buffer from the rotation of the display. */
- switch (self->dupl_desc.Rotation) {
- case DXGI_MODE_ROTATION_ROTATE90:
- verteces[0].texcoord = (vector2d) {
- (float) rect->right / (float) src_desc->Width,
- (float) rect->bottom / (float) src_desc->Height};
- verteces[1].texcoord = (vector2d) {
- (float) rect->left / (float) src_desc->Width,
- (float) rect->bottom / (float) src_desc->Height};
- verteces[2].texcoord = (vector2d) {
- (float) rect->right / (float) src_desc->Width,
- (float) rect->top / (float) src_desc->Height};
- verteces[5].texcoord = (vector2d) {
- (float) rect->left / (float) src_desc->Width,
- (float) rect->top / (float) src_desc->Height};
- break;
- case DXGI_MODE_ROTATION_ROTATE180:
- verteces[0].texcoord = (vector2d) {
- (float) rect->right / (float) src_desc->Width,
- (float) rect->top / (float) src_desc->Height};
- verteces[1].texcoord = (vector2d) {
- (float) rect->right / (float) src_desc->Width,
- (float) rect->bottom / (float) src_desc->Height};
- verteces[2].texcoord = (vector2d) {
- (float) rect->left / (float) src_desc->Width,
- (float) rect->top / (float) src_desc->Height};
- verteces[5].texcoord = (vector2d) {
- (float) rect->left / (float) src_desc->Width,
- (float) rect->bottom / (float) src_desc->Height};
- break;
- case DXGI_MODE_ROTATION_ROTATE270:
- verteces[0].texcoord = (vector2d) {
- (float) rect->left / (float) src_desc->Width,
- (float) rect->top / (float) src_desc->Height};
- verteces[1].texcoord = (vector2d) {
- (float) rect->right / (float) src_desc->Width,
- (float) rect->top / (float) src_desc->Height};
- verteces[2].texcoord = (vector2d) {
- (float) rect->left / (float) src_desc->Width,
- (float) rect->bottom / (float) src_desc->Height};
- verteces[5].texcoord = (vector2d) {
- (float) rect->right / (float) src_desc->Width,
- (float) rect->bottom / (float) src_desc->Height};
- break;
- default:
- verteces[0].texcoord = (vector2d) {
- (float) rect->left / (float) src_desc->Width,
- (float) rect->bottom / (float) src_desc->Height};
- verteces[1].texcoord = (vector2d) {
- (float) rect->left / (float) src_desc->Width,
- (float) rect->top / (float) src_desc->Height};
- verteces[2].texcoord = (vector2d) {
- (float) rect->right / (float) src_desc->Width,
- (float) rect->bottom / (float) src_desc->Height};
- verteces[5].texcoord = (vector2d) {
- (float) rect->right / (float) src_desc->Width,
- (float) rect->top / (float) src_desc->Height};
- break;
- }
- verteces[3].texcoord = verteces[2].texcoord;
- verteces[4].texcoord = verteces[1].texcoord;
-
- center_x = (int) dst_desc->Width / 2;
- center_y = (int) dst_desc->Height / 2;
-
- verteces[0].pos = (vector3d) {
- (float) (dst_rect->left - center_x) / (float) center_x,
- (float) (dst_rect->bottom - center_y) / (float) center_y *-1.0f, 0.0f};
- verteces[1].pos = (vector3d) {
- (float) (dst_rect->left - center_x) / (float) center_x,
- (float) (dst_rect->top - center_y) / (float) center_y *-1.0f, 0.0f};
- verteces[2].pos = (vector3d) {
- (float) (dst_rect->right - center_x) / (float) center_x,
- (float) (dst_rect->bottom - center_y) / (float) center_y *-1.0f, 0.0f};
- verteces[3].pos = verteces[2].pos;
- verteces[4].pos = verteces[1].pos;
- verteces[5].pos = (vector3d) {
- (float) (dst_rect->right - center_x) / (float) center_x,
- (float) (dst_rect->top - center_y) / (float) center_y *-1.0f, 0.0f};
-}
-
-typedef struct _monitor_param_by_name
-{
- const gchar *device_name;
- HMONITOR hmonitor;
-} monitor_param_by_name;
-
-static BOOL CALLBACK
-monitor_enum_proc_by_name (HMONITOR hmonitor, HDC hdc, LPRECT rect,
- LPARAM lparam)
-{
- MONITORINFOEXA monitor_info;
- monitor_param_by_name *param = (monitor_param_by_name *) lparam;
-
- monitor_info.cbSize = sizeof (monitor_info);
- if (GetMonitorInfoA (hmonitor, (MONITORINFO *) & monitor_info)) {
- if (0 == g_strcmp0 (monitor_info.szDevice, param->device_name)) {
- param->hmonitor = hmonitor;
- return FALSE;
- }
- }
- return TRUE;
-}
-
-HMONITOR
-get_hmonitor_by_device_name (const gchar * device_name)
-{
- monitor_param_by_name monitor = { device_name, NULL, };
- EnumDisplayMonitors (NULL, NULL, monitor_enum_proc_by_name,
- (LPARAM) & monitor);
- return monitor.hmonitor;
-}
-
-static BOOL CALLBACK
-monitor_enum_proc_primary (HMONITOR hmonitor, HDC hdc, LPRECT rect,
- LPARAM lparam)
-{
- MONITORINFOEXA monitor_info;
- monitor_param_by_name *param = (monitor_param_by_name *) lparam;
-
- monitor_info.cbSize = sizeof (monitor_info);
- if (GetMonitorInfoA (hmonitor, (MONITORINFO *) & monitor_info)) {
- if (MONITORINFOF_PRIMARY == monitor_info.dwFlags) {
- param->hmonitor = hmonitor;
- return FALSE;
- }
- }
- return TRUE;
-}
-
-HMONITOR
-get_hmonitor_primary (void)
-{
- monitor_param_by_name monitor = { NULL, NULL, };
- EnumDisplayMonitors (NULL, NULL, monitor_enum_proc_primary,
- (LPARAM) & monitor);
- return monitor.hmonitor;
-}
-
-typedef struct _monitor_param_by_index
-{
- int target;
- int counter;
- HMONITOR hmonitor;
-} monitor_param_by_index;
-
-static BOOL CALLBACK
-monitor_enum_proc_by_index (HMONITOR hmonitor, HDC hdc, LPRECT rect,
- LPARAM lparam)
-{
- MONITORINFOEXA monitor_info;
- monitor_param_by_index *param = (monitor_param_by_index *) lparam;
-
- monitor_info.cbSize = sizeof (monitor_info);
- if (GetMonitorInfoA (hmonitor, (MONITORINFO *) & monitor_info)) {
- if (param->target == param->counter) {
- param->hmonitor = hmonitor;
- return FALSE;
- }
- }
- ++param->counter;
- return TRUE;
-}
-
-HMONITOR
-get_hmonitor_by_index (int index)
-{
- monitor_param_by_index monitor = { index, 0, NULL, };
- EnumDisplayMonitors (NULL, NULL, monitor_enum_proc_by_index,
- (LPARAM) & monitor);
- return monitor.hmonitor;
-}
-
-
-gboolean
-get_monitor_physical_size (HMONITOR hmonitor, LPRECT rect)
-{
- MONITORINFOEXW monitor_info;
- DEVMODEW dev_mode;
-
- monitor_info.cbSize = sizeof (monitor_info);
- if (!GetMonitorInfoW (hmonitor, (LPMONITORINFO) & monitor_info)) {
- return FALSE;
- }
-
- dev_mode.dmSize = sizeof (dev_mode);
- dev_mode.dmDriverExtra = sizeof (POINTL);
- dev_mode.dmFields = DM_POSITION;
- if (!EnumDisplaySettingsW
- (monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode)) {
- return FALSE;
- }
-
- SetRect (rect, 0, 0, dev_mode.dmPelsWidth, dev_mode.dmPelsHeight);
- return TRUE;
-}
-
-static const gchar *
-_hresult_to_string_fallback (HRESULT hr)
-{
- const gchar *s = "unknown error";
-
- switch (hr) {
- case DXGI_ERROR_ACCESS_DENIED:
- s = "DXGI_ERROR_ACCESS_DENIED";
- break;
- case DXGI_ERROR_ACCESS_LOST:
- s = "DXGI_ERROR_ACCESS_LOST";
- break;
- case DXGI_ERROR_CANNOT_PROTECT_CONTENT:
- s = "DXGI_ERROR_CANNOT_PROTECT_CONTENT";
- break;
- case DXGI_ERROR_DEVICE_HUNG:
- s = "DXGI_ERROR_DEVICE_HUNG";
- break;
- case DXGI_ERROR_DEVICE_REMOVED:
- s = "DXGI_ERROR_DEVICE_REMOVED";
- break;
- case DXGI_ERROR_DEVICE_RESET:
- s = "DXGI_ERROR_DEVICE_RESET";
- break;
- case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
- s = "DXGI_ERROR_DRIVER_INTERNAL_ERROR";
- break;
- case DXGI_ERROR_FRAME_STATISTICS_DISJOINT:
- s = "DXGI_ERROR_FRAME_STATISTICS_DISJOINT";
- break;
- case DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE:
- s = "DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE";
- break;
- case DXGI_ERROR_INVALID_CALL:
- s = "DXGI_ERROR_INVALID_CALL";
- break;
- case DXGI_ERROR_MORE_DATA:
- s = "DXGI_ERROR_MORE_DATA";
- break;
- case DXGI_ERROR_NAME_ALREADY_EXISTS:
- s = "DXGI_ERROR_NAME_ALREADY_EXISTS";
- break;
- case DXGI_ERROR_NONEXCLUSIVE:
- s = "DXGI_ERROR_NONEXCLUSIVE";
- break;
- case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:
- s = "DXGI_ERROR_NOT_CURRENTLY_AVAILABLE";
- break;
- case DXGI_ERROR_NOT_FOUND:
- s = "DXGI_ERROR_NOT_FOUND";
- break;
- case DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED:
- s = "DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED";
- break;
- case DXGI_ERROR_REMOTE_OUTOFMEMORY:
- s = "DXGI_ERROR_REMOTE_OUTOFMEMORY";
- break;
- case DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE:
- s = "DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE";
- break;
- case DXGI_ERROR_SDK_COMPONENT_MISSING:
- s = "DXGI_ERROR_SDK_COMPONENT_MISSING";
- break;
- case DXGI_ERROR_SESSION_DISCONNECTED:
- s = "DXGI_ERROR_SESSION_DISCONNECTED";
- break;
- case DXGI_ERROR_UNSUPPORTED:
- s = "DXGI_ERROR_UNSUPPORTED";
- break;
- case DXGI_ERROR_WAIT_TIMEOUT:
- s = "DXGI_ERROR_WAIT_TIMEOUT";
- break;
- case DXGI_ERROR_WAS_STILL_DRAWING:
- s = "DXGI_ERROR_WAS_STILL_DRAWING";
- break;
- case E_FAIL:
- s = "E_FAIL";
- break;
- case E_OUTOFMEMORY:
- s = "E_OUTOFMEMORY";
- break;
- case E_NOTIMPL:
- s = "E_NOTIMPL";
- break;
- case E_ACCESSDENIED:
- s = "E_ACCESSDENIED";
- break;
- case E_POINTER:
- s = "E_POINTER";
- break;
- case E_INVALIDARG:
- s = "E_INVALIDARG";
- break;
-#if defined(_MSC_VER) && (_MSC_VER >= 1800)
- case DXGI_ERROR_ALREADY_EXISTS:
- s = "DXGI_ERROR_ALREADY_EXISTS";
- break;
- case D3D11_ERROR_FILE_NOT_FOUND:
- s = "D3D11_ERROR_FILE_NOT_FOUND";
- break;
- case D3D11_ERROR_TOO_MANY_UNIQUE_STATE_OBJECTS:
- s = "D3D11_ERROR_TOO_MANY_UNIQUE_STATE_OBJECTS";
- break;
- case D3D11_ERROR_TOO_MANY_UNIQUE_VIEW_OBJECTS:
- s = "D3D11_ERROR_TOO_MANY_UNIQUE_VIEW_OBJECTS";
- break;
- case D3D11_ERROR_DEFERRED_CONTEXT_MAP_WITHOUT_INITIAL_DISCARD:
- s = "D3D11_ERROR_DEFERRED_CONTEXT_MAP_WITHOUT_INITIAL_DISCARD";
- break;
-#endif
- }
- return s;
-}
-
-gchar *
-get_hresult_to_string (HRESULT hr)
-{
- gchar *error_text = NULL;
-
- error_text = g_win32_error_message ((gint) hr);
- /* g_win32_error_message() doesn't cover all HERESULT return code,
- * so it could be empty string, or null if there was an error
- * in g_utf16_to_utf8() */
- if (!error_text || strlen (error_text) == 0) {
- g_free (error_text);
- error_text = g_strdup (_hresult_to_string_fallback (hr));
- }
-
- return error_text;
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) 2019 OKADA Jun-ichi <okada@abt.jp>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-/**
- * SECTION:element-dxgiscreencapsrc
- * @title: dxgiscreencapsrc
- *
- * This element uses DXGI Desktop Duplication API.
- * The default is capturing the whole desktop, but #GstDXGIScreenCapSrc:x,
- * #GstDXGIScreenCapSrc:y, #GstDXGIScreenCapSrc:width and
- * #GstDXGIScreenCapSrc:height can be used to select a particular region.
- * Use #GstDXGIScreenCapSrc:monitor for changing which monitor to capture
- * from.
- *
- * ## Example pipelines
- * |[
- * gst-launch-1.0 dxgiscreencapsrc ! videoconvert ! dshowvideosink
- * ]| Capture the desktop and display it.
- * |[
- * gst-launch-1.0 dxgiscreencapsrc x=100 y=100 width=320 height=240 !
- * videoconvert ! dshowvideosink
- * ]| Capture a portion of the desktop and display it.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <windows.h>
-#include <versionhelpers.h>
-#include <gst/video/video.h>
-#include "gstdxgiscreencapsrc.h"
-#include "dxgicapture.h"
-
-GST_DEBUG_CATEGORY_EXTERN (gst_dxgi_screen_cap_src_debug);
-#define GST_CAT_DEFAULT gst_dxgi_screen_cap_src_debug
-
-struct _GstDXGIScreenCapSrc
-{
- /* Parent */
- GstPushSrc src;
-
- /* Properties */
- gint capture_x;
- gint capture_y;
- gint capture_w;
- gint capture_h;
- guint monitor;
- gchar *device_name;
- gboolean show_cursor;
-
- /* Source pad frame rate */
- gint rate_numerator;
- gint rate_denominator;
-
- /* Runtime variables */
- RECT screen_rect;
- RECT src_rect;
- guint64 frame_number;
- GstClockID clock_id;
- GstVideoInfo video_info;
-
- /*DXGI capture */
- DxgiCapture *dxgi_capture;
-};
-
-static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("BGRA")));
-
-#define gst_dxgi_screen_cap_src_parent_class parent_class
-G_DEFINE_TYPE (GstDXGIScreenCapSrc, gst_dxgi_screen_cap_src, GST_TYPE_PUSH_SRC);
-
-#define DEFAULT_MONITOR (-1)
-#define DEFAULT_DEVICE_NAME (NULL)
-#define DEFAULT_SHOW_CURSOR (FALSE)
-#define DEFAULT_X_POS (0)
-#define DEFAULT_Y_POS (0)
-#define DEFAULT_WIDTH (0)
-#define DEFAULT_HEIGHT (0)
-
-enum
-{
- PROP_0,
- PROP_MONITOR,
- PROP_DEVICE_NAME,
- PROP_SHOW_CURSOR,
- PROP_X_POS,
- PROP_Y_POS,
- PROP_WIDTH,
- PROP_HEIGHT
-};
-
-static void gst_dxgi_screen_cap_src_dispose (GObject * object);
-static void gst_dxgi_screen_cap_src_set_property (GObject * object,
- guint prop_id, const GValue * value, GParamSpec * pspec);
-static void gst_dxgi_screen_cap_src_get_property (GObject * object,
- guint prop_id, GValue * value, GParamSpec * pspec);
-
-static GstCaps *gst_dxgi_screen_cap_src_fixate (GstBaseSrc * bsrc,
- GstCaps * caps);
-static gboolean gst_dxgi_screen_cap_src_set_caps (GstBaseSrc * bsrc,
- GstCaps * caps);
-static GstCaps *gst_dxgi_screen_cap_src_get_caps (GstBaseSrc * bsrc,
- GstCaps * filter);
-static gboolean gst_dxgi_screen_cap_src_start (GstBaseSrc * bsrc);
-static gboolean gst_dxgi_screen_cap_src_stop (GstBaseSrc * bsrc);
-
-static gboolean gst_dxgi_screen_cap_src_unlock (GstBaseSrc * bsrc);
-
-static GstFlowReturn gst_dxgi_screen_cap_src_create (GstBaseSrc * pushsrc,
- guint64 offset, guint length, GstBuffer ** buffer);
-
-static HMONITOR _get_hmonitor (GstDXGIScreenCapSrc * src);
-
-/* Implementation. */
-static void
-gst_dxgi_screen_cap_src_class_init (GstDXGIScreenCapSrcClass * klass)
-{
- GObjectClass *go_class;
- GstElementClass *e_class;
- GstBaseSrcClass *bs_class;
-
- go_class = G_OBJECT_CLASS (klass);
- e_class = GST_ELEMENT_CLASS (klass);
- bs_class = GST_BASE_SRC_CLASS (klass);
-
- go_class->set_property = gst_dxgi_screen_cap_src_set_property;
- go_class->get_property = gst_dxgi_screen_cap_src_get_property;
-
- go_class->dispose = GST_DEBUG_FUNCPTR (gst_dxgi_screen_cap_src_dispose);
- bs_class->get_caps = GST_DEBUG_FUNCPTR (gst_dxgi_screen_cap_src_get_caps);
- bs_class->set_caps = GST_DEBUG_FUNCPTR (gst_dxgi_screen_cap_src_set_caps);
- bs_class->start = GST_DEBUG_FUNCPTR (gst_dxgi_screen_cap_src_start);
- bs_class->stop = GST_DEBUG_FUNCPTR (gst_dxgi_screen_cap_src_stop);
- bs_class->unlock = GST_DEBUG_FUNCPTR (gst_dxgi_screen_cap_src_unlock);
- bs_class->fixate = GST_DEBUG_FUNCPTR (gst_dxgi_screen_cap_src_fixate);
- bs_class->create = GST_DEBUG_FUNCPTR (gst_dxgi_screen_cap_src_create);
-
- g_object_class_install_property (go_class, PROP_MONITOR,
- g_param_spec_int ("monitor", "Monitor",
- "Which monitor to use (-1 = primary monitor and default)",
- DEFAULT_MONITOR, G_MAXINT, DEFAULT_MONITOR, G_PARAM_READWRITE));
- g_object_class_install_property (go_class, PROP_DEVICE_NAME,
- g_param_spec_string ("device-name", "Monitor device name",
- "Which monitor to use by device name (e.g. \"\\\\\\\\.\\\\DISPLAY1\")",
- DEFAULT_DEVICE_NAME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (go_class, PROP_SHOW_CURSOR,
- g_param_spec_boolean ("cursor",
- "Show mouse cursor",
- "Whether to show mouse cursor (default off)",
- DEFAULT_SHOW_CURSOR, G_PARAM_READWRITE));
- g_object_class_install_property (go_class, PROP_X_POS,
- g_param_spec_int ("x", "X",
- "Horizontal coordinate of top left corner for the screen capture "
- "area", 0, G_MAXINT, DEFAULT_X_POS, G_PARAM_READWRITE));
- g_object_class_install_property (go_class, PROP_Y_POS,
- g_param_spec_int ("y", "Y",
- "Vertical coordinate of top left corner for the screen capture "
- "area", 0, G_MAXINT, DEFAULT_Y_POS, G_PARAM_READWRITE));
- g_object_class_install_property (go_class, PROP_WIDTH,
- g_param_spec_int ("width", "Width",
- "Width of screen capture area (0 = maximum)",
- 0, G_MAXINT, DEFAULT_WIDTH, G_PARAM_READWRITE));
- g_object_class_install_property (go_class, PROP_HEIGHT,
- g_param_spec_int ("height", "Height",
- "Height of screen capture area (0 = maximum)",
- 0, G_MAXINT, DEFAULT_HEIGHT, G_PARAM_READWRITE));
-
- gst_element_class_add_static_pad_template (e_class, &src_template);
-
- gst_element_class_set_static_metadata (e_class,
- "DirectX DXGI screen capture source",
- "Source/Video", "Captures screen", "OKADA Jun-ichi <okada@abt.jp>");
-}
-
-static void
-gst_dxgi_screen_cap_src_init (GstDXGIScreenCapSrc * src)
-{
- /* Set src element inital values... */
- src->capture_x = DEFAULT_X_POS;
- src->capture_y = DEFAULT_Y_POS;
- src->capture_w = DEFAULT_WIDTH;
- src->capture_h = DEFAULT_HEIGHT;
-
- src->monitor = DEFAULT_MONITOR;
- src->device_name = DEFAULT_DEVICE_NAME;
- src->show_cursor = DEFAULT_SHOW_CURSOR;
-
- src->dxgi_capture = NULL;
-
- gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
- gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
-}
-
-static void
-gst_dxgi_screen_cap_src_dispose (GObject * object)
-{
- GstDXGIScreenCapSrc *src = GST_DXGI_SCREEN_CAP_SRC (object);
-
- g_free (src->device_name);
- src->device_name = NULL;
-
- dxgicap_destory (src->dxgi_capture);
- src->dxgi_capture = NULL;
-
- G_OBJECT_CLASS (parent_class)->dispose (object);
-}
-
-static void
-gst_dxgi_screen_cap_src_set_property (GObject * object,
- guint prop_id, const GValue * value, GParamSpec * pspec)
-{
- GstDXGIScreenCapSrc *src = GST_DXGI_SCREEN_CAP_SRC (object);
-
- switch (prop_id) {
- case PROP_MONITOR:
- src->monitor = g_value_get_int (value);
- break;
- case PROP_DEVICE_NAME:
- g_free (src->device_name);
- src->device_name = g_value_dup_string (value);
- break;
- case PROP_SHOW_CURSOR:
- src->show_cursor = g_value_get_boolean (value);
- break;
- case PROP_X_POS:
- src->capture_x = g_value_get_int (value);
- break;
- case PROP_Y_POS:
- src->capture_y = g_value_get_int (value);
- break;
- case PROP_WIDTH:
- src->capture_w = g_value_get_int (value);
- break;
- case PROP_HEIGHT:
- src->capture_h = g_value_get_int (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- };
-}
-
-static void
-gst_dxgi_screen_cap_src_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec)
-{
- GstDXGIScreenCapSrc *src = GST_DXGI_SCREEN_CAP_SRC (object);
-
- switch (prop_id) {
- case PROP_MONITOR:
- g_value_set_int (value, src->monitor);
- break;
- case PROP_DEVICE_NAME:
- g_value_set_string (value, src->device_name);
- break;
- case PROP_SHOW_CURSOR:
- g_value_set_boolean (value, src->show_cursor);
- break;
- case PROP_X_POS:
- g_value_set_int (value, src->capture_x);
- break;
- case PROP_Y_POS:
- g_value_set_int (value, src->capture_y);
- break;
- case PROP_WIDTH:
- g_value_set_int (value, src->capture_w);
- break;
- case PROP_HEIGHT:
- g_value_set_int (value, src->capture_h);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- };
-}
-
-static GstCaps *
-gst_dxgi_screen_cap_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
-{
- GstStructure *structure;
-
- caps = gst_caps_make_writable (caps);
-
- structure = gst_caps_get_structure (caps, 0);
-
- gst_structure_fixate_field_nearest_int (structure, "width", 640);
- gst_structure_fixate_field_nearest_int (structure, "height", 480);
- gst_structure_fixate_field_nearest_fraction (structure, "framerate", 30, 1);
-
- caps = GST_BASE_SRC_CLASS (parent_class)->fixate (bsrc, caps);
-
- return caps;
-}
-
-static gboolean
-gst_dxgi_screen_cap_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
-{
- GstDXGIScreenCapSrc *src = GST_DXGI_SCREEN_CAP_SRC (bsrc);
- GstStructure *structure;
-
- structure = gst_caps_get_structure (caps, 0);
-
- src->src_rect = src->screen_rect;
- if (src->capture_w && src->capture_h) {
- src->src_rect.left += src->capture_x;
- src->src_rect.top += src->capture_y;
- src->src_rect.right = src->src_rect.left + src->capture_w;
- src->src_rect.bottom = src->src_rect.top + src->capture_h;
- }
-
- gst_structure_get_fraction (structure, "framerate",
- &src->rate_numerator, &src->rate_denominator);
-
- GST_DEBUG_OBJECT (src, "set_caps size %dx%d, %d/%d fps",
- (gint) RECT_WIDTH (src->src_rect),
- (gint) RECT_HEIGHT (src->src_rect),
- src->rate_numerator, src->rate_denominator);
-
- gst_video_info_from_caps (&src->video_info, caps);
- gst_base_src_set_blocksize (bsrc, GST_VIDEO_INFO_SIZE (&src->video_info));
- return TRUE;
-}
-
-static GstCaps *
-gst_dxgi_screen_cap_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
-{
- GstDXGIScreenCapSrc *src = GST_DXGI_SCREEN_CAP_SRC (bsrc);
- RECT rect_dst;
- GstCaps *caps = NULL;
-
- HMONITOR hmonitor = _get_hmonitor (src);
- if (!get_monitor_physical_size (hmonitor, &rect_dst)) {
- GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
- ("Specified monitor with index %d not found", src->monitor), (NULL));
- return NULL;
- }
-
- src->screen_rect = rect_dst;
- if (src->capture_w && src->capture_h &&
- src->capture_x + src->capture_w <= RECT_WIDTH (rect_dst) &&
- src->capture_y + src->capture_h <= RECT_HEIGHT (rect_dst)) {
- rect_dst.left = src->capture_x;
- rect_dst.top = src->capture_y;
- rect_dst.right = src->capture_x + src->capture_w;
- rect_dst.bottom = src->capture_y + src->capture_h;
- } else {
- /* Default values */
- src->capture_x = src->capture_y = 0;
- src->capture_w = src->capture_h = 0;
- }
-
- /* The desktop image is always in the DXGI_FORMAT_B8G8R8A8_UNORM format. */
- GST_DEBUG_OBJECT (src, "get_cap rect: %ld, %ld, %ld, %ld", rect_dst.left,
- rect_dst.top, rect_dst.right, rect_dst.bottom);
-
- caps =
- gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, "BGRA",
- "width", G_TYPE_INT, RECT_WIDTH (rect_dst),
- "height", G_TYPE_INT, RECT_HEIGHT (rect_dst),
- "framerate", GST_TYPE_FRACTION_RANGE, 1, 1, G_MAXINT,
- 1, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL);
-
- if (filter) {
- GstCaps *tmp =
- gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
- gst_caps_unref (caps);
- caps = tmp;
- }
- return caps;
-}
-
-static gboolean
-gst_dxgi_screen_cap_src_start (GstBaseSrc * bsrc)
-{
- GstDXGIScreenCapSrc *src = GST_DXGI_SCREEN_CAP_SRC (bsrc);
- HMONITOR hmonitor = _get_hmonitor (src);
- if (NULL == hmonitor) {
- GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
- ("Specified monitor with index %d not found", src->monitor), (NULL));
- return FALSE;
- }
- src->dxgi_capture = dxgicap_new (hmonitor, src);
-
- if (NULL == src->dxgi_capture) {
- GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
- ("Specified monitor with index %d not found", src->monitor), (NULL));
- return FALSE;
- }
- dxgicap_start (src->dxgi_capture);
-
- src->frame_number = -1;
- return TRUE;
-}
-
-static gboolean
-gst_dxgi_screen_cap_src_stop (GstBaseSrc * bsrc)
-{
- GstDXGIScreenCapSrc *src = GST_DXGI_SCREEN_CAP_SRC (bsrc);
- dxgicap_stop (src->dxgi_capture);
- dxgicap_destory (src->dxgi_capture);
- src->dxgi_capture = NULL;
-
- return TRUE;
-}
-
-static gboolean
-gst_dxgi_screen_cap_src_unlock (GstBaseSrc * bsrc)
-{
- GstDXGIScreenCapSrc *src = GST_DXGI_SCREEN_CAP_SRC (bsrc);
-
- GST_OBJECT_LOCK (src);
- if (src->clock_id) {
- GST_DEBUG_OBJECT (src, "Waking up waiting clock");
- gst_clock_id_unschedule (src->clock_id);
- }
- GST_OBJECT_UNLOCK (src);
-
- return TRUE;
-}
-
-static GstFlowReturn
-gst_dxgi_screen_cap_src_create (GstBaseSrc * base_src, guint64 offset,
- guint length, GstBuffer ** buf)
-{
- GstDXGIScreenCapSrc *src = GST_DXGI_SCREEN_CAP_SRC (base_src);
- GstClock *clock;
- GstClockTime buf_time, buf_dur;
- guint64 frame_number;
- GstFlowReturn ret;
- GstBuffer *buffer = NULL;
-
- if (G_UNLIKELY (!src->dxgi_capture)) {
- GST_DEBUG_OBJECT (src, "format wasn't negotiated before create function");
- return GST_FLOW_NOT_NEGOTIATED;
- }
-
- clock = gst_element_get_clock (GST_ELEMENT (src));
- if (clock != NULL) {
- GstClockTime time, base_time;
-
- /* Calculate sync time. */
-
- time = gst_clock_get_time (clock);
- base_time = gst_element_get_base_time (GST_ELEMENT (src));
- buf_time = time - base_time;
-
- if (src->rate_numerator) {
- frame_number = gst_util_uint64_scale (buf_time,
- src->rate_numerator, GST_SECOND * src->rate_denominator);
- } else {
- frame_number = -1;
- }
- } else {
- buf_time = GST_CLOCK_TIME_NONE;
- frame_number = -1;
- }
-
- if (frame_number != -1 && frame_number == src->frame_number) {
- GstClockID id;
- GstClockReturn ret;
-
- /* Need to wait for the next frame */
- frame_number += 1;
-
- /* Figure out what the next frame time is */
- buf_time = gst_util_uint64_scale (frame_number,
- src->rate_denominator * GST_SECOND, src->rate_numerator);
-
- id = gst_clock_new_single_shot_id (clock,
- buf_time + gst_element_get_base_time (GST_ELEMENT (src)));
- GST_OBJECT_LOCK (src);
- src->clock_id = id;
- GST_OBJECT_UNLOCK (src);
-
- GST_DEBUG_OBJECT (src, "Waiting for next frame time %" G_GUINT64_FORMAT,
- buf_time);
- ret = gst_clock_id_wait (id, NULL);
-
- GST_OBJECT_LOCK (src);
- gst_clock_id_unref (id);
- src->clock_id = NULL;
- GST_OBJECT_UNLOCK (src);
-
- if (ret == GST_CLOCK_UNSCHEDULED) {
- /* Got woken up by the unlock function */
- if (clock) {
- gst_object_unref (clock);
- }
- return GST_FLOW_FLUSHING;
- }
-
- /* Duration is a complete 1/fps frame duration */
- buf_dur =
- gst_util_uint64_scale_int (GST_SECOND, src->rate_denominator,
- src->rate_numerator);
- } else if (frame_number != -1) {
- GstClockTime next_buf_time;
-
- GST_DEBUG_OBJECT (src, "No need to wait for next frame time %"
- G_GUINT64_FORMAT " next frame = %" G_GINT64_FORMAT
- " prev = %" G_GINT64_FORMAT, buf_time, frame_number, src->frame_number);
- next_buf_time =
- gst_util_uint64_scale (frame_number + 1,
- src->rate_denominator * GST_SECOND, src->rate_numerator);
- /* Frame duration is from now until the next expected capture time */
- buf_dur = next_buf_time - buf_time;
- } else {
- buf_dur = GST_CLOCK_TIME_NONE;
- }
- src->frame_number = frame_number;
-
- if (clock) {
- gst_object_unref (clock);
- }
-
- /* Get the latest desktop frame. */
- do {
- ret = dxgicap_acquire_next_frame (src->dxgi_capture, src->show_cursor, 0);
- if (ret == GST_DXGICAP_FLOW_RESOLUTION_CHANGE) {
- GST_DEBUG_OBJECT (src, "Resolution change detected.");
-
- if (!gst_base_src_negotiate (GST_BASE_SRC (src))) {
- return GST_FLOW_NOT_NEGOTIATED;
- }
- }
- } while (ret == GST_DXGICAP_FLOW_RESOLUTION_CHANGE);
-
- if (ret != GST_FLOW_OK) {
- return ret;
- }
-
- ret =
- GST_BASE_SRC_CLASS (g_type_class_peek_parent (parent_class))->alloc
- (base_src, offset, length, &buffer);
- if (ret != GST_FLOW_OK) {
- return ret;
- }
-
- /* Copy the latest desktop frame to the video frame. */
- if (dxgicap_copy_buffer (src->dxgi_capture, src->show_cursor,
- &src->src_rect, &src->video_info, buffer)) {
- GST_BUFFER_TIMESTAMP (buffer) = buf_time;
- GST_BUFFER_DURATION (buffer) = buf_dur;
- *buf = buffer;
-
- return GST_FLOW_OK;
- }
-
- gst_clear_buffer (&buffer);
-
- return GST_FLOW_ERROR;
-}
-
-static HMONITOR
-_get_hmonitor (GstDXGIScreenCapSrc * src)
-{
- HMONITOR hmonitor = NULL;
- GST_DEBUG_OBJECT (src, "device_name:%s", GST_STR_NULL (src->device_name));
- if (NULL != src->device_name) {
- hmonitor = get_hmonitor_by_device_name (src->device_name);
- }
- if (NULL == hmonitor && DEFAULT_MONITOR != src->monitor) {
- hmonitor = get_hmonitor_by_index (src->monitor);
- }
- if (NULL == hmonitor) {
- hmonitor = get_hmonitor_primary ();
- }
- return hmonitor;
-}
-
-void
-gst_dxgi_screen_cap_src_register (GstPlugin * plugin, GstRank rank)
-{
- if (!IsWindows8OrGreater ()) {
- GST_WARNING ("OS version is too old");
- return;
- }
-
- if (!gst_dxgicap_shader_init ()) {
- GST_WARNING ("Couldn't load HLS compiler");
- return;
- }
-
- /**
- * element-dxgiscreencapsrc:
- *
- * Since: 1.18
- */
- gst_element_register (plugin, "dxgiscreencapsrc",
- rank, GST_TYPE_DXGI_SCREEN_CAP_SRC);
-}