| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- using System.Drawing;
- using System.Drawing.Imaging;
- using System.Runtime.InteropServices;
- using SharpAvi;
- using SharpAvi.Output;
- using SharpAvi.Codecs;
- namespace ScreenRecorder;
- public class CaptureService : IDisposable
- {
- private AviWriter? _writer;
- private IAviVideoStream? _videoStream;
- private Thread? _captureThread;
- private volatile bool _isRecording;
- private readonly int _fps;
- private readonly int _quality;
- private readonly bool _hideCursor;
- private readonly Rectangle _captureArea;
- private string? _outputPath;
- public CaptureService(int fps = 30, int quality = 100, bool hideCursor = false)
- {
- _fps = fps;
- _quality = quality;
- _hideCursor = hideCursor;
- // 录制主屏幕
- var screen = System.Windows.Forms.Screen.PrimaryScreen!;
- _captureArea = screen.Bounds;
- }
- public void Start(string outputPath)
- {
- _outputPath = outputPath;
- _isRecording = true;
- _writer = new AviWriter(outputPath)
- {
- FramesPerSecond = _fps,
- EmitIndex1 = true
- };
- if (_quality == 101) // 无损模式:无压缩 RGB
- {
- var rawStream = _writer.AddVideoStream(
- _captureArea.Width,
- _captureArea.Height,
- BitsPerPixel.Bpp32
- );
- rawStream.Codec = KnownFourCCs.Codecs.Uncompressed;
- _videoStream = rawStream;
- }
- else // MJPEG 有损模式
- {
- _videoStream = _writer.AddMotionJpegVideoStream(
- _captureArea.Width,
- _captureArea.Height,
- quality: _quality
- );
- }
- _captureThread = new Thread(CaptureLoop) { IsBackground = true };
- _captureThread.Start();
- }
- public void Stop()
- {
- _isRecording = false;
- _captureThread?.Join(3000);
- _writer?.Close();
- _writer = null;
- }
- private void CaptureLoop()
- {
- var interval = TimeSpan.FromMilliseconds(1000.0 / _fps);
- int w = _captureArea.Width, h = _captureArea.Height;
- bool lossless = _quality == 101;
- using var bitmap = new Bitmap(w, h, PixelFormat.Format32bppRgb);
- using var graphics = Graphics.FromImage(bitmap);
- int stride = w * 4;
- var frameData = new byte[stride * h];
- while (_isRecording)
- {
- var start = DateTime.UtcNow;
- graphics.CopyFromScreen(_captureArea.Location, Point.Empty, _captureArea.Size);
- if (!_hideCursor) DrawCursor(graphics);
- var bits = bitmap.LockBits(
- new Rectangle(0, 0, w, h),
- ImageLockMode.ReadOnly,
- PixelFormat.Format32bppRgb
- );
- if (lossless)
- {
- // 无压缩 AVI 需要 BGR 从下到上(行翻转)
- for (int row = 0; row < h; row++)
- {
- IntPtr src = bits.Scan0 + row * bits.Stride;
- int dstOffset = (h - 1 - row) * stride;
- Marshal.Copy(src, frameData, dstOffset, stride);
- }
- }
- else
- {
- Marshal.Copy(bits.Scan0, frameData, 0, frameData.Length);
- }
- bitmap.UnlockBits(bits);
- _videoStream!.WriteFrame(true, frameData, 0, frameData.Length);
- var elapsed = DateTime.UtcNow - start;
- var remaining = interval - elapsed;
- if (remaining > TimeSpan.Zero)
- Thread.Sleep(remaining);
- }
- }
- public void Dispose() => Stop();
- // Win32 API:获取鼠标位置和光标图标
- [StructLayout(LayoutKind.Sequential)]
- private struct CURSORINFO
- {
- public int cbSize;
- public int flags;
- public IntPtr hCursor;
- public POINT ptScreenPos;
- }
- [StructLayout(LayoutKind.Sequential)]
- private struct POINT { public int x, y; }
- [DllImport("user32.dll")] static extern bool GetCursorInfo(ref CURSORINFO ci);
- [DllImport("user32.dll")] static extern bool DrawIconEx(IntPtr hdc, int x, int y, IntPtr hIcon, int w, int h, int step, IntPtr brush, int flags);
- private const int CURSOR_SHOWING = 1;
- private const int DI_NORMAL = 3;
- private void DrawCursor(Graphics g)
- {
- var ci = new CURSORINFO { cbSize = Marshal.SizeOf<CURSORINFO>() };
- if (!GetCursorInfo(ref ci) || ci.flags != CURSOR_SHOWING) return;
- int x = ci.ptScreenPos.x - _captureArea.X;
- int y = ci.ptScreenPos.y - _captureArea.Y;
- var hdc = g.GetHdc();
- DrawIconEx(hdc, x, y, ci.hCursor, 0, 0, 0, IntPtr.Zero, DI_NORMAL);
- g.ReleaseHdc(hdc);
- }
- }
|