我尝试以 60 fps 而不是 30 帧运行游戏。游戏锁定为 30 fps,因此我能达到 60 fps 的唯一方法是使用帧插值。
对于插值部分,我将使用 OpenCV,我发现这篇非常好的文章。
本游戏使用OpenGL。经过一番研究,我发现抓取屏幕的最好方法是hook SwapBuffer函数。因此,我选择挂钩游戏以抓取屏幕并将其发送到其他应用程序,该应用程序将实时传输游戏,并顺便添加插值帧以达到 60 FPS(如果您有更好的主意,我完全同意)打开!)
我开始编写我的新 dll。我正在使用 C# 进行编码,我选择 EasyHook 来注入我的 DLL。
Hooking 运行良好,现在非常酷 =D.
但是,我现在陷入困境,因为我完全不知道如何从游戏中抓取屏幕。
我尝试使用OpenGL.Net和SharpGL,但我不知道如何获取实际的OpenGL上下文来使用glReadPixel!
这是我的实际代码:
using System;
using System.Runtime.InteropServices;
namespace Hook
{
public class ServerInterface : MarshalByRefObject
{
public void IsInstalled(int clientPID)
{
Console.WriteLine("This programm has injected dll into process {0}.\r\n", clientPID);
}
public void ReportMessage(int clientPID, string message)
{
Console.WriteLine(message);
}
public void ReportException(Exception e)
{
Console.WriteLine("The target process has reported an error:\r\n" + e.ToString());
}
public void Ping()
{
Console.WriteLine("Ping !");
}
}
//Point d'entrée
public class InjectionEntryPoint : EasyHook.IEntryPoint
{
//Serveur :
ServerInterface server = null;
public InjectionEntryPoint(EasyHook.RemoteHooking.IContext context, string channelName)
{
//Constructeur
//Objectif : vérifier si la communication entre les deux programmes peut etre établit
server = EasyHook.RemoteHooking.IpcConnectClient<ServerInterface>(channelName);
server.Ping();
}
public void Run(EasyHook.RemoteHooking.IContext context, string channelName)
{
try
{
var SwapBuffersHook = EasyHook.LocalHook.Create(EasyHook.LocalHook.GetProcAddress("opengl32.dll", "wglSwapBuffers"), new SwapBuffers_Delegate(SwapBuffers_Hook), this);
SwapBuffersHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
server.ReportMessage(EasyHook.RemoteHooking.GetCurrentProcessId(), "SwapBuffers Hooked");
}
catch (Exception ExtInfo)
{
server.ReportException(ExtInfo);
return;
}
server.IsInstalled(EasyHook.RemoteHooking.GetCurrentProcessId());
EasyHook.RemoteHooking.WakeUpProcess();
//Waiting years
while( true)
{
System.Threading.Thread.Sleep(10000);
}
// Finalise cleanup of hooks
EasyHook.LocalHook.Release();
}
//Deleguate
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
delegate IntPtr SwapBuffers_Delegate(IntPtr hdc);
//Original
[DllImport("opengl32.dll", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
static extern IntPtr wglSwapBuffers(IntPtr hdc);
//Hook function
public IntPtr SwapBuffers_Hook(IntPtr hdc)
{
server.ReportMessage(EasyHook.RemoteHooking.GetCurrentProcessId(), "I'm in SwapBuffers =DDDD");
//Here I need to grab frames
//Return
return wglSwapBuffers(hdc);
}
}
}
我怀疑你能否提高游戏的FPS。顺便说一句,有一些可能性。
你正在做的事情是正确的。您只是错过了创建要显示的中间帧的 GL 代码(我建议使用当前的 GL 上下文在进程中创建它)。
问题在于执行
SwapBuffers
命令时会显示视频帧。这是您挂钩的,但问问自己和程序:为什么 SwapBuffers
每秒被调用 30 次?
如果答案是“因为应用程序使用计时器暂停执行”,那么您无法轻松解决它:您的钩子必须与应用程序一起工作,因此您需要了解应用程序如何休眠。
如果答案是“因为应用程序正在利用 WGL_EXT_swap_control”,那么您有一种可能性:
如何创建插值帧?如果通过 CPU 进行操作,请使用 glReadPixels 和后台缓冲区 (GL_BACK) 作为读取帧缓冲区(使用 glDrawBuffers)。这将下载帧缓冲区绑定的内容。然后使用混合方程获得插值像素。