2025-01-05 12:53:03 +08:00
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using Fischless.GameCapture.Graphics.Helpers;
|
2023-10-07 23:26:56 +08:00
|
|
|
|
using SharpDX.Direct3D11;
|
2023-10-02 18:00:14 +08:00
|
|
|
|
using Vanara.PInvoke;
|
2023-10-10 22:33:58 +08:00
|
|
|
|
using Windows.Foundation.Metadata;
|
2023-10-02 18:00:14 +08:00
|
|
|
|
using Windows.Graphics.Capture;
|
|
|
|
|
using Windows.Graphics.DirectX;
|
|
|
|
|
|
2023-10-07 23:26:56 +08:00
|
|
|
|
namespace Fischless.GameCapture.Graphics;
|
2023-10-02 18:00:14 +08:00
|
|
|
|
|
2023-10-07 23:26:56 +08:00
|
|
|
|
public class GraphicsCapture : IGameCapture
|
2023-10-02 18:00:14 +08:00
|
|
|
|
{
|
|
|
|
|
private nint _hWnd;
|
|
|
|
|
|
|
|
|
|
private Direct3D11CaptureFramePool _captureFramePool = null!;
|
|
|
|
|
private GraphicsCaptureItem _captureItem = null!;
|
|
|
|
|
private GraphicsCaptureSession _captureSession = null!;
|
|
|
|
|
|
2023-10-22 15:28:38 +08:00
|
|
|
|
public CaptureModes Mode => CaptureModes.WindowsGraphicsCapture;
|
2023-10-02 18:00:14 +08:00
|
|
|
|
public bool IsCapturing { get; private set; }
|
|
|
|
|
|
2023-10-08 00:22:01 +08:00
|
|
|
|
private ResourceRegion? _region;
|
2023-10-07 19:29:24 +08:00
|
|
|
|
|
2024-02-23 22:55:31 +08:00
|
|
|
|
private bool _useBitmapCache = false;
|
2024-02-07 22:29:20 +08:00
|
|
|
|
private Bitmap? _currentBitmap;
|
2024-02-07 22:10:43 +08:00
|
|
|
|
|
2023-10-07 19:29:24 +08:00
|
|
|
|
public void Dispose() => Stop();
|
2023-10-02 18:00:14 +08:00
|
|
|
|
|
2024-02-23 22:55:31 +08:00
|
|
|
|
public void Start(nint hWnd, Dictionary<string, object>? settings = null)
|
2023-10-02 18:00:14 +08:00
|
|
|
|
{
|
|
|
|
|
_hWnd = hWnd;
|
2023-10-07 19:29:24 +08:00
|
|
|
|
|
2023-10-07 23:26:56 +08:00
|
|
|
|
_region = GetGameScreenRegion(hWnd);
|
2023-10-07 19:29:24 +08:00
|
|
|
|
|
2023-10-02 18:00:14 +08:00
|
|
|
|
IsCapturing = true;
|
|
|
|
|
|
|
|
|
|
_captureItem = CaptureHelper.CreateItemForWindow(_hWnd);
|
|
|
|
|
|
|
|
|
|
if (_captureItem == null)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException("Failed to create capture item.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_captureItem.Closed += CaptureItemOnClosed;
|
|
|
|
|
|
|
|
|
|
var device = Direct3D11Helper.CreateDevice();
|
|
|
|
|
|
|
|
|
|
_captureFramePool = Direct3D11CaptureFramePool.Create(device, DirectXPixelFormat.B8G8R8A8UIntNormalized, 2,
|
|
|
|
|
_captureItem.Size);
|
2024-02-23 22:55:31 +08:00
|
|
|
|
|
|
|
|
|
if (settings != null && settings.TryGetValue("useBitmapCache", out object? value) && (bool)value)
|
|
|
|
|
{
|
|
|
|
|
_useBitmapCache = true;
|
|
|
|
|
_captureFramePool.FrameArrived += OnFrameArrived;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 18:00:14 +08:00
|
|
|
|
_captureSession = _captureFramePool.CreateCaptureSession(_captureItem);
|
2025-01-05 12:53:03 +08:00
|
|
|
|
if (ApiInformation.IsPropertyPresent("Windows.Graphics.Capture.GraphicsCaptureSession", "IsCursorCaptureEnabled"))
|
|
|
|
|
{
|
|
|
|
|
_captureSession.IsCursorCaptureEnabled = false;
|
|
|
|
|
}
|
2023-10-10 22:33:58 +08:00
|
|
|
|
if (ApiInformation.IsWriteablePropertyPresent("Windows.Graphics.Capture.GraphicsCaptureSession", "IsBorderRequired"))
|
|
|
|
|
{
|
|
|
|
|
_captureSession.IsBorderRequired = false;
|
|
|
|
|
}
|
2024-02-07 22:10:43 +08:00
|
|
|
|
|
2023-10-02 18:00:14 +08:00
|
|
|
|
_captureSession.StartCapture();
|
|
|
|
|
IsCapturing = true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-07 23:26:56 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 从 DwmGetWindowAttribute 的矩形 截取出 GetClientRect的矩形(游戏区域)
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="hWnd"></param>
|
|
|
|
|
/// <returns></returns>
|
2023-10-08 00:22:01 +08:00
|
|
|
|
private ResourceRegion? GetGameScreenRegion(nint hWnd)
|
2023-10-07 19:29:24 +08:00
|
|
|
|
{
|
2023-10-08 00:22:01 +08:00
|
|
|
|
var exStyle = User32.GetWindowLong(hWnd, User32.WindowLongFlags.GWL_EXSTYLE);
|
|
|
|
|
if ((exStyle & (int)User32.WindowStylesEx.WS_EX_TOPMOST) != 0)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-10-07 19:29:24 +08:00
|
|
|
|
ResourceRegion region = new();
|
|
|
|
|
DwmApi.DwmGetWindowAttribute<RECT>(hWnd, DwmApi.DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS, out var windowRect);
|
|
|
|
|
User32.GetClientRect(_hWnd, out var clientRect);
|
|
|
|
|
//POINT point = default; // 这个点和 DwmGetWindowAttribute 结果差1
|
|
|
|
|
//User32.ClientToScreen(hWnd, ref point);
|
|
|
|
|
|
|
|
|
|
region.Left = 0;
|
|
|
|
|
region.Top = windowRect.Height - clientRect.Height;
|
|
|
|
|
region.Right = clientRect.Width;
|
|
|
|
|
region.Bottom = windowRect.Height;
|
|
|
|
|
region.Front = 0;
|
|
|
|
|
region.Back = 1;
|
|
|
|
|
|
|
|
|
|
return region;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-07 22:10:43 +08:00
|
|
|
|
private void OnFrameArrived(Direct3D11CaptureFramePool sender, object args)
|
2023-10-02 18:00:14 +08:00
|
|
|
|
{
|
2024-02-07 22:10:43 +08:00
|
|
|
|
using var frame = _captureFramePool?.TryGetNextFrame();
|
2023-10-02 18:00:14 +08:00
|
|
|
|
|
2024-02-07 22:10:43 +08:00
|
|
|
|
if (frame != null)
|
2023-10-02 18:00:14 +08:00
|
|
|
|
{
|
2024-02-07 22:10:43 +08:00
|
|
|
|
var b = frame.ToBitmap(_region);
|
|
|
|
|
if (b != null)
|
2023-10-02 18:00:14 +08:00
|
|
|
|
{
|
2024-02-07 22:10:43 +08:00
|
|
|
|
_currentBitmap = b;
|
2023-10-02 18:00:14 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-07 22:10:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Bitmap? Capture()
|
|
|
|
|
{
|
|
|
|
|
if (_hWnd == IntPtr.Zero)
|
2023-10-02 18:00:14 +08:00
|
|
|
|
{
|
2024-02-07 22:10:43 +08:00
|
|
|
|
return null;
|
2023-10-02 18:00:14 +08:00
|
|
|
|
}
|
2023-10-07 19:29:24 +08:00
|
|
|
|
|
2024-02-23 22:55:31 +08:00
|
|
|
|
if (!_useBitmapCache)
|
2024-02-07 22:29:20 +08:00
|
|
|
|
{
|
2024-02-23 22:55:31 +08:00
|
|
|
|
try
|
2024-02-14 15:46:48 +08:00
|
|
|
|
{
|
2024-02-23 22:55:31 +08:00
|
|
|
|
using var frame = _captureFramePool?.TryGetNextFrame();
|
|
|
|
|
|
|
|
|
|
if (frame == null)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return frame.ToBitmap(_region);
|
2024-02-14 15:46:48 +08:00
|
|
|
|
}
|
2024-02-23 22:55:31 +08:00
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
Debug.WriteLine(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
2024-02-14 15:46:48 +08:00
|
|
|
|
}
|
2024-02-23 22:55:31 +08:00
|
|
|
|
else
|
2024-02-14 15:46:48 +08:00
|
|
|
|
{
|
2024-02-23 22:55:31 +08:00
|
|
|
|
if (_currentBitmap == null)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (Bitmap)_currentBitmap.Clone();
|
2024-02-07 22:29:20 +08:00
|
|
|
|
}
|
2023-10-02 18:00:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Stop()
|
|
|
|
|
{
|
|
|
|
|
_captureSession?.Dispose();
|
|
|
|
|
_captureFramePool?.Dispose();
|
|
|
|
|
_captureSession = null!;
|
|
|
|
|
_captureFramePool = null!;
|
|
|
|
|
_captureItem = null!;
|
|
|
|
|
|
|
|
|
|
_hWnd = IntPtr.Zero;
|
|
|
|
|
IsCapturing = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CaptureItemOnClosed(GraphicsCaptureItem sender, object args)
|
|
|
|
|
{
|
|
|
|
|
Stop();
|
|
|
|
|
}
|
2023-10-07 19:29:24 +08:00
|
|
|
|
}
|