• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

C#使用SharpAVI进行屏幕录制

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

再 nuget 中 搜索 shapAvi 并添加引用

github 地址:https://github.com/baSSiLL/SharpAvi

 

using SharpAvi;
using SharpAvi.Codecs;
using SharpAvi.Output;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BankAutoTransfer.Common
{
    public class Recorder
    {

        private readonly int zoomWidth;
        private readonly int zoomHeight;
        private readonly AviWriter writer;
        private readonly IAviVideoStream videoStream;
        private readonly Thread screenThread;

        /// <summary>
        /// 上一次图片
        /// </summary>
        Bitmap lastBitmap = new Bitmap(10, 10);

        bool stop = false;

        /// <summary>
        /// 缩放
        /// </summary>
        float zoom = 1;
        /// <summary>
        /// 鼠标是否点击
        /// </summary>
        bool mouseclick = false;
        /// <summary>
        /// 钩子句柄
        /// </summary>
        int hook = 0;
        /// <summary>
        /// 录制屏幕
        /// </summary>
        /// <param name="fileName">要保存的文件名</param>
        /// <param name="codec">编码</param>
        /// <param name="quality">录制质量</param>
        /// <param name="zoom">缩放</param>
        public Recorder(string fileName, FourCC codec, int quality = 70, float zoom = 1.0F)
        {
            //设置缩放 宽高
            zoomHeight = (int)Math.Floor(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height * zoom);
            zoomWidth = (int)Math.Floor(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width * zoom);

            this.zoom = zoom;
            //创建视频
            writer = new AviWriter(fileName)
            {
                FramesPerSecond = 10,
                EmitIndex1 = true,
            };

            //创建视频流
            videoStream = CreateVideoStream(codec, quality);
            videoStream.Name = "Screencast";
            //开启一个线程录制屏幕
            screenThread = new Thread(RecordScreen)
            {
                Name = typeof(Recorder).Name + ".RecordScreen",
                IsBackground = true
            };
            //钩子函数用于监控是否点击了鼠标
            hook = WinHook.SetWindowsHookEx(HookType.WH_MOUSE_LL, WinHook.hookProc += MouseHook, Win32Api.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0);

            screenThread.Start();
        }

        private IAviVideoStream CreateVideoStream(FourCC codec, int quality)
        {
            // Select encoder type based on FOURCC of codec
            if (codec == KnownFourCCs.Codecs.Uncompressed)
            {
                return writer.AddUncompressedVideoStream(zoomWidth, zoomHeight);
            }
            else if (codec == KnownFourCCs.Codecs.MotionJpeg)
            {
                return writer.AddMotionJpegVideoStream(zoomWidth, zoomHeight, quality);
            }
            else
            {
                return writer.AddMpeg4VideoStream(zoomWidth, zoomHeight, (double)writer.FramesPerSecond,
                    // It seems that all tested MPEG-4 VfW codecs ignore the quality affecting parameters passed through VfW API
                    // They only respect the settings from their own configuration dialogs, and Mpeg4VideoEncoder currently has no support for this
                    quality: quality,
                    codec: codec,
                    // Most of VfW codecs expect single-threaded use, so we wrap this encoder to special wrapper
                    // Thus all calls to the encoder (including its instantiation) will be invoked on a single thread although encoding (and writing) is performed asynchronously
                    forceSingleThreadedAccess: true);
            }
        }
        /// <summary>
        /// 停止录制
        /// </summary>
        public void Stop()
        {
            WinHook.UnhookWindowsHookEx(hook);
            lastBitmap.Dispose();
            stop = true;
        }

        private void RecordScreen()
        {
            while (!stop)
            {
                var buffer = GetScreenshot();
                // 把图片写入视频流
                videoStream.WriteFrameAsync(true, buffer, 0, buffer.Length).Wait();
            }
            writer.Close();
        }

        private byte[] GetScreenshot()
        {
            using (Bitmap avibitmap = GetScreen())
            {
                Point mouseXY = new Point();
                Win32Api.GetCursorPos(ref mouseXY);
                using (Graphics mouseGraphics = Graphics.FromImage(avibitmap))
                {
                    //绘制鼠标位置
                    if (mouseclick)
                        mouseGraphics.DrawEllipse(new Pen(new SolidBrush(Color.Red), 10), new Rectangle(mouseXY.X - 10, mouseXY.Y - 10, 20, 20));
                    else
                        mouseGraphics.DrawEllipse(new Pen(new SolidBrush(Color.Black), 5), new Rectangle(mouseXY.X - 10, mouseXY.Y - 10, 20, 20));
                    if (zoom != 1)
                    {
                        //缩放
                        using (var copy = new Bitmap(this.zoomWidth, this.zoomHeight))
                        {
                            var buffer = new byte[copy.Width * copy.Height * 4];
                            var gcopy = Graphics.FromImage(copy);
                            gcopy.DrawImage(avibitmap, new Rectangle(new Point(0, 0), copy.Size), 0, 0, avibitmap.Width, avibitmap.Height, GraphicsUnit.Pixel);
                            gcopy.Dispose();
                            var bits = copy.LockBits(new Rectangle(0, 0, copy.Width, copy.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
                            Marshal.Copy(bits.Scan0, buffer, 0, buffer.Length);
                            copy.UnlockBits(bits);
                            return buffer;
                        }
                    }
                    else
                    {
                        var buffer = new byte[avibitmap.Width * avibitmap.Height * 4];
                        var bits = avibitmap.LockBits(new Rectangle(0, 0, avibitmap.Width, avibitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
                        Marshal.Copy(bits.Scan0, buffer, 0, buffer.Length);
                        avibitmap.UnlockBits(bits);
                        return buffer;
                    }
                }
            }

        }

        private Bitmap GetScreen()
        {
            try
            {
                var bitmap = new Bitmap(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height);
                var graphics = Graphics.FromImage(bitmap);
                graphics.CopyFromScreen(0, 0, 0, 0, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Size);
                graphics.Dispose();
                lastBitmap.Dispose();
                lastBitmap = bitmap;
            }
            catch
            {
            }
            return (Bitmap)lastBitmap.Clone();
        }

        private int MouseHook(int nCode, int wParam, IntPtr lParam)
        {
            if (wParam == CommonConst.WM_LBUTTONDOWN)
                mouseclick = true;
            else if (wParam == CommonConst.WM_LBUTTONUP)
                mouseclick = false;
            return WinHook.CallNextHookEx(hook, nCode, wParam, lParam);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace BankAutoTransfer.Common
{
    public class WinHook
    {
        [DllImport("user32.dll")]
        public static extern int SetWindowsHookEx(
           HookType idHook,
           HookProc lpfn,
           IntPtr hInstance,
           int threadId
           );

        public delegate int HookProc(int nCode, int wParam, IntPtr lParam);

        public static HookProc hookProc;

        [DllImport("user32.dll")]
        public static extern int CallNextHookEx(
            int hhk,      //handle to current hook  
            int nCode,      //hook code passed to hook procedure  
            int wParam,      //value passed to hook procedure  
            IntPtr lParam       //value passed to hook procedure  
            );

        [DllImport("user32.dll")]
        public static extern bool UnhookWindowsHookEx(int hook);
    }

    /// <summary>
    /// 设置的钩子类型
    /// </summary>
    public enum HookType : int
    {
        /// <summary>
        /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动 
        ///条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。 
        ///WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通 
        ///过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook 
        ///监视所有应用程序消息。 
        /// 
        ///WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间 
        ///过滤消息,这等价于在主消息循环中过滤消息。 
        ///    
        ///通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这 
        ///个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循 
        ///环里一样
        /// </summary>
        WH_MSGFILTER = -1,
        /// <summary>
        /// WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的,可以使用这 
        ///个Hook记录连续的鼠标和键盘事件,然后通过使用WH_JOURNALPLAYBACK Hook 
        ///来回放。WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook一样 
        ///使用。WH_JOURNALRECORD是system-wide local hooks,它们不会被注射到任何行 
        ///程地址空间
        /// </summary>
        WH_JOURNALRECORD = 0,
        /// <summary>
        /// WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可 
        ///以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠 
        ///标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装,正常的鼠标和键盘 
        ///事件就是无效的。WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定 
        ///Hook一样使用。WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处 
        ///理来自回放Hook当前消息之前需要等待多长时间(毫秒)。这就使Hook可以控制实 
        ///时事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,它们不会被 
        ///注射到任何行程地址空间
        /// </summary>
        WH_JOURNALPLAYBACK = 1,
        /// <summary>
        /// 在应用程序中,WH_KEYBOARD Hook用来监视WM_KEYDOWN and  
        ///WM_KEYUP消息,这些消息通过GetMessage or PeekMessage function返回。可以使 
        ///用这个Hook来监视输入到消息队列中的键盘消息
        /// </summary>
        WH_KEYBOARD = 2,
        /// <summary>
        /// 应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函 
        ///数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入,以及 
        ///其它发送到消息队列中的消息
        /// </summary>
        WH_GETMESSAGE = 3,
        /// <summary>
        /// 监视发送到窗口过程的消息,系统在消息发送到接收窗口过程之前调用
        /// </summary>
        WH_CALLWNDPROC = 4,
        /// <summary>
        /// 在以下事件之前,系统都会调用WH_CBT Hook子过程,这些事件包括: 
        ///1. 激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件; 
        ///2. 完成系统指令; 
        ///3. 来自系统消息队列中的移动鼠标,键盘事件; 
        ///4. 设置输入焦点事件; 
        ///5. 同步系统消息队列事件。
        ///Hook子过程的返回值确定系统是否允许或者防止这些操作中的一个
        /// </summary>
        WH_CBT = 5,
        /// <summary>
        /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动 
        ///条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。 
        ///WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通 
        ///过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook 
        ///监视所有应用程序消息。 
        /// 
        ///WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间 
        ///过滤消息,这等价于在主消息循环中过滤消息。 
        ///    
        ///通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这 
        ///个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循 
        ///环里一样
        /// </summary>
        WH_SYSMSGFILTER = 6,
        /// <summary>
        /// WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。 
        ///使用这个Hook监视输入到消息队列中的鼠标消息
        /// </summary>
        WH_MOUSE = 7,
        /// <summary>
        /// 当调用GetMessage 或 PeekMessage 来从消息队列种查询非鼠标、键盘消息时
        /// </summary>
        WH_HARDWARE = 8,
        /// <summary>
        /// 在系统调用系统中与其它Hook关联的Hook子过程之前,系统会调用 
        ///WH_DEBUG Hook子过程。你可以使用这个Hook来决定是否允许系统调用与其它 
        ///Hook关联的Hook子过程
        /// </summary>
        WH_DEBUG = 9,
        /// <summary>
        /// 外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是 
        ///激活的并且当顶层窗口建立或者销毁时,系统调用WH_SHELL Hook子过程。 
        ///WH_SHELL 共有5钟情况: 
        ///1. 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁; 
        ///2. 当Taskbar需要重画某个按钮; 
        ///3. 当系统需要显示关于Taskbar的一个程序的最小化形式; 
        ///4. 当目前的键盘布局状态改变; 
        ///5. 当使用者按Ctrl+Esc去执行Task Manager(或相同级别的程序)。 
        ///
        ///按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接 
        ///收WH_SHELL消息之前,应用程序必须调用SystemParametersInfo function注册它自 
        ////// </summary>
        WH_SHELL = 10,
        /// <summary>
        /// 当应用程序的前台线程处于空闲状态时,可以使用WH_FOREGROUNDIDLE  
        ///Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就 
        ///会调用WH_FOREGROUNDIDLE Hook子过程
        /// </summary>
        WH_FOREGROUNDIDLE = 11,
        /// <summary>
        /// 监视发送到窗口过程的消息,系统在消息发送到接收窗口过程之后调用
        /// </summary>
        WH_CALLWNDPROCRET = 12,
        /// <summary>
        /// 监视输入到线程消息队列中的键盘消息
        /// </summary>
        WH_KEYBOARD_LL = 13,
        /// <summary>
        /// 监视输入到线程消息队列中的鼠标消息
        /// </summary>
        WH_MOUSE_LL = 14
    }

}

 


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
在C#程序中实现插件架构发布时间:2022-07-13
下一篇:
C# 如何获取项目的根目录发布时间:2022-07-13
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap