使用 directshow 在桌面录制上翻转光标图标
Fliped cursor icon on desktop recording using directshow
使用Directshow.NET我开发了一个会记录桌面屏幕的应用程序,为了获得鼠标指针我们需要自己绘制鼠标指针。所以我在 BufferCB
中添加了 SampleGrabber
adn 我写了下面的代码:
public const Int32 CURSOR_SHOWING = 0x00000001;
[StructLayout(LayoutKind.Sequential)]
public struct ICONINFO
{
public bool fIcon;
public Int32 xHotspot;
public Int32 yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public Int32 x;
public Int32 y;
}
[StructLayout(LayoutKind.Sequential)]
public struct CURSORINFO
{
public Int32 cbSize;
public Int32 flags;
public IntPtr hCursor;
public POINT ptScreenPos;
}
[DllImport("user32.dll")]
public static extern bool GetCursorInfo(out CURSORINFO pci);
[DllImport("user32.dll")]
public static extern IntPtr CopyIcon(IntPtr hIcon);
[DllImport("user32.dll")]
public static extern bool DrawIcon(IntPtr hdc, int x, int y, IntPtr hIcon);
[DllImport("user32.dll")]
public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
{
Graphics g;
Bitmap v;
v = new Bitmap(m_videoWidth, m_videoHeight, m_stride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, pBuffer);
g = Graphics.FromImage(v);
CURSORINFO cursorInfo;
cursorInfo.cbSize = Marshal.SizeOf(typeof(CURSORINFO));
if (GetCursorInfo(out cursorInfo))
{
if (cursorInfo.flags == CURSOR_SHOWING)
{
var iconPointer = CopyIcon(cursorInfo.hCursor);
ICONINFO iconInfo;
int iconX, iconY;
if (GetIconInfo(iconPointer, out iconInfo))
{
iconX = cursorInfo.ptScreenPos.x - ((int)iconInfo.xHotspot);
iconY = cursorInfo.ptScreenPos.y - ((int)iconInfo.yHotspot);
DrawIcon(g.GetHdc(), iconX, iconY, cursorInfo.hCursor);
g.ReleaseHdc();
g.Dispose();
v.Dispose();
}
}
}
return 0;
}
此代码正在绘制鼠标光标,但光标在 Y 轴上翻转。
可能是因为在 BufferCB
中,如果我们在 Bitmap 中转换 pBuffer
,那么该帧将在 Y 轴上翻转。为了解决这个问题,我通过在 BufferCB
内添加 v.RotateFlip(RotateFlipType.RotateNoneFlipY);
来翻转 Y 轴上的当前帧,此更改后鼠标指针在桌面录制视频中不可见。
如何翻转鼠标指针?
更新#1
我使用 Icon.ToBitmap()
将图标指针转换为 Icon
然后转换为 Bitmap
然后在 Y 轴上翻转,这是代码(感谢@Roman R.):
...
iconX = cursorInfo.ptScreenPos.x - ((int)iconInfo.xHotspot);
iconY = cursorInfo.ptScreenPos.y - ((int)iconInfo.yHotspot);
Icon ic = Icon.FromHandle(iconPointer);
Bitmap icon = ic.ToBitmap();
icon.RotateFlip(RotateFlipType.RotateNoneFlipY);
g.DrawImage(icon, iconX, iconY);
g.Dispose();
v.Dispose();
icon.Dispose();
ic.Dispose();
...
我在上述修改中只遇到一个问题,有时我在第 Bitmap icon = ic.ToBitmap();
行得到 ArgumentException
System.ArgumentException occurred
HResult=-2147024809
Message=Parameter is not valid.
Source=System.Drawing
StackTrace:
at System.Drawing.Bitmap.FromHicon(IntPtr hicon)
InnerException:
我处理了所有使用的位图,但仍然出现此异常。
您还有另一种选择,即绘制正确的鼠标指针。只需将鼠标指针的 PNG 图片放入 BufferCB
:
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
{
Graphics g;
Bitmap v;
v = new Bitmap(m_videoWidth, m_videoHeight, m_stride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, pBuffer);
g = Graphics.FromImage(v);
CURSORINFO cursorInfo;
cursorInfo.cbSize = Marshal.SizeOf(typeof(CURSORINFO));
if (GetCursorInfo(out cursorInfo))
{
if (cursorInfo.flags == CURSOR_SHOWING)
{
var iconPointer = CopyIcon(cursorInfo.hCursor);
ICONINFO iconInfo;
int iconX, iconY;
if (GetIconInfo(iconPointer, out iconInfo))
{
iconX = cursorInfo.ptScreenPos.x - ((int)iconInfo.xHotspot);
iconY = cursorInfo.ptScreenPos.y - ((int)iconInfo.yHotspot);
//DRAW STATIC POINTER IMAGE
Bitmap pointerImage = new Bitmap('pointer.png');
g.DrawImage(pointerImage,iconX,iconY);
g.Dispose();
v.Dispose();
}
}
}
return 0;
}
这里的问题是您的 BufferCB
实施。您创建了一个临时的 Graphics
/Bitmap
对象,以便可以使用光标覆盖对其进行更新。您将此位图上下颠倒,并应用图标的正常绘制将其翻转。
您需要考虑到通过 BufferCB
回调获得的 24 位 RGB 缓冲区行的正常顺序是从下到上; Bitmap
constructor 期望的行顺序是相反的。您需要以相反的顺序将行转移到 Bitmap
中,然后将它们分别取回。我不确定负步幅是否有效,大概不会。或者简单地覆盖预先翻转的光标,以补偿翻转的背景和覆盖坐标。
使用Directshow.NET我开发了一个会记录桌面屏幕的应用程序,为了获得鼠标指针我们需要自己绘制鼠标指针。所以我在 BufferCB
中添加了 SampleGrabber
adn 我写了下面的代码:
public const Int32 CURSOR_SHOWING = 0x00000001;
[StructLayout(LayoutKind.Sequential)]
public struct ICONINFO
{
public bool fIcon;
public Int32 xHotspot;
public Int32 yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public Int32 x;
public Int32 y;
}
[StructLayout(LayoutKind.Sequential)]
public struct CURSORINFO
{
public Int32 cbSize;
public Int32 flags;
public IntPtr hCursor;
public POINT ptScreenPos;
}
[DllImport("user32.dll")]
public static extern bool GetCursorInfo(out CURSORINFO pci);
[DllImport("user32.dll")]
public static extern IntPtr CopyIcon(IntPtr hIcon);
[DllImport("user32.dll")]
public static extern bool DrawIcon(IntPtr hdc, int x, int y, IntPtr hIcon);
[DllImport("user32.dll")]
public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
{
Graphics g;
Bitmap v;
v = new Bitmap(m_videoWidth, m_videoHeight, m_stride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, pBuffer);
g = Graphics.FromImage(v);
CURSORINFO cursorInfo;
cursorInfo.cbSize = Marshal.SizeOf(typeof(CURSORINFO));
if (GetCursorInfo(out cursorInfo))
{
if (cursorInfo.flags == CURSOR_SHOWING)
{
var iconPointer = CopyIcon(cursorInfo.hCursor);
ICONINFO iconInfo;
int iconX, iconY;
if (GetIconInfo(iconPointer, out iconInfo))
{
iconX = cursorInfo.ptScreenPos.x - ((int)iconInfo.xHotspot);
iconY = cursorInfo.ptScreenPos.y - ((int)iconInfo.yHotspot);
DrawIcon(g.GetHdc(), iconX, iconY, cursorInfo.hCursor);
g.ReleaseHdc();
g.Dispose();
v.Dispose();
}
}
}
return 0;
}
此代码正在绘制鼠标光标,但光标在 Y 轴上翻转。
可能是因为在 BufferCB
中,如果我们在 Bitmap 中转换 pBuffer
,那么该帧将在 Y 轴上翻转。为了解决这个问题,我通过在 BufferCB
内添加 v.RotateFlip(RotateFlipType.RotateNoneFlipY);
来翻转 Y 轴上的当前帧,此更改后鼠标指针在桌面录制视频中不可见。
如何翻转鼠标指针?
更新#1
我使用 Icon.ToBitmap()
将图标指针转换为 Icon
然后转换为 Bitmap
然后在 Y 轴上翻转,这是代码(感谢@Roman R.):
...
iconX = cursorInfo.ptScreenPos.x - ((int)iconInfo.xHotspot);
iconY = cursorInfo.ptScreenPos.y - ((int)iconInfo.yHotspot);
Icon ic = Icon.FromHandle(iconPointer);
Bitmap icon = ic.ToBitmap();
icon.RotateFlip(RotateFlipType.RotateNoneFlipY);
g.DrawImage(icon, iconX, iconY);
g.Dispose();
v.Dispose();
icon.Dispose();
ic.Dispose();
...
我在上述修改中只遇到一个问题,有时我在第 Bitmap icon = ic.ToBitmap();
ArgumentException
System.ArgumentException occurred
HResult=-2147024809
Message=Parameter is not valid.
Source=System.Drawing
StackTrace:
at System.Drawing.Bitmap.FromHicon(IntPtr hicon)
InnerException:
我处理了所有使用的位图,但仍然出现此异常。
您还有另一种选择,即绘制正确的鼠标指针。只需将鼠标指针的 PNG 图片放入 BufferCB
:
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
{
Graphics g;
Bitmap v;
v = new Bitmap(m_videoWidth, m_videoHeight, m_stride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, pBuffer);
g = Graphics.FromImage(v);
CURSORINFO cursorInfo;
cursorInfo.cbSize = Marshal.SizeOf(typeof(CURSORINFO));
if (GetCursorInfo(out cursorInfo))
{
if (cursorInfo.flags == CURSOR_SHOWING)
{
var iconPointer = CopyIcon(cursorInfo.hCursor);
ICONINFO iconInfo;
int iconX, iconY;
if (GetIconInfo(iconPointer, out iconInfo))
{
iconX = cursorInfo.ptScreenPos.x - ((int)iconInfo.xHotspot);
iconY = cursorInfo.ptScreenPos.y - ((int)iconInfo.yHotspot);
//DRAW STATIC POINTER IMAGE
Bitmap pointerImage = new Bitmap('pointer.png');
g.DrawImage(pointerImage,iconX,iconY);
g.Dispose();
v.Dispose();
}
}
}
return 0;
}
这里的问题是您的 BufferCB
实施。您创建了一个临时的 Graphics
/Bitmap
对象,以便可以使用光标覆盖对其进行更新。您将此位图上下颠倒,并应用图标的正常绘制将其翻转。
您需要考虑到通过 BufferCB
回调获得的 24 位 RGB 缓冲区行的正常顺序是从下到上; Bitmap
constructor 期望的行顺序是相反的。您需要以相反的顺序将行转移到 Bitmap
中,然后将它们分别取回。我不确定负步幅是否有效,大概不会。或者简单地覆盖预先翻转的光标,以补偿翻转的背景和覆盖坐标。