WPF 实现调用 WindowsAPI 实现屏幕录制

该文章介绍了一个使用WPF和WindowsAPI编写的屏幕录制程序,不依赖ffmpeg,通过定时调用API截取屏幕并保存为jpg,然后组合成GIF动画。程序中遇到了内存占用高的问题,作者尝试了不同库来优化GIF生成过程。
摘要由CSDN通过智能技术生成

 WPF 实现调用 WindowsAPI 实现屏幕录制

控件名:DesktopRecord

作   者:WPFDevelopersOrg - 驚鏵

原文链接[1]:https://github.com/yanjinhuagood/DesktopRecord

  • 框架使用.NET4

  • Visual Studio 2022

  • 接着上一篇做一个不依赖 ffmpeg 实现屏幕录制

  • 1000 毫秒调用 WindowsAPI 进行截取屏幕获取图像,并保存 jpg 文件到指定路径(保存的文件从 0.jpgn.jpg )。

1)获取屏幕图片并保存为 jpg 代码如下:

private static BitmapSource CaptureScreen()
        {
            IntPtr desk = GetDesktopWindow();
            IntPtr dc = GetWindowDC(desk);

            IntPtr memdc = CreateCompatibleDC(dc);
            IntPtr bitmap = CreateCompatibleBitmap(dc, screenWidth, screenHeight);
            SelectObject(memdc, bitmap);
            BitBlt(memdc, 0, 0, screenWidth, screenHeight, dc, 0, 0, 0xCC0020);
            BitmapSource source = Imaging.CreateBitmapSourceFromHBitmap(bitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            ReleaseDC(desk, dc);
            return source;
        }
        
        Task.Factory.StartNew(() =>
            {
                while (IsRunning)
                {
                    Thread.Sleep(1000);
                    num += 1;
                    Application.Current.Dispatcher.Invoke(new Action(() =>
                    {
                        var drawingVisual = new DrawingVisual();
                        POINT mousePosition;
                        using (DrawingContext drawingContext = drawingVisual.RenderOpen())
                        {
                            drawingContext.DrawImage(CaptureScreen(),
                                new Rect(new Point(),
                                new Size(screenWidth, screenHeight)));

                            if (GetCursorPos(out mousePosition))
                            {
                                var cursorSize = 30;
                                var cursorHalfSize = cursorSize / 2;
                                var cursorCenterX = mousePosition.X - SystemParameters.VirtualScreenLeft;
                                var cursorCenterY = mousePosition.Y - SystemParameters.VirtualScreenTop;
                                drawingContext.DrawImage(GetCursorIcon(),
                                    new Rect(new Point(cursorCenterX, cursorCenterY),
                                    new Size(cursorSize, cursorSize)));

                            }
                        }
                        var png = Path.Combine(tempDir, $"{num}.jpg");
                        using (FileStream stream = new FileStream(png, FileMode.Create))
                        {
                            var bitmap = new RenderTargetBitmap((int)screenWidth, (int)screenHeight, 96, 96, PixelFormats.Pbgra32);
                            bitmap.Render(drawingVisual);
                            var bitmapEncoder = BitmapFrame.Create(bitmap);
                            bitmapEncoder.Freeze();
                            var encoder = new JpegBitmapEncoder();
                            encoder.QualityLevel = 50;
                            encoder.Frames.Add(bitmapEncoder);
                            encoder.Save(stream);
                            encoder.Frames.Clear();
                            GC.Collect();
                        }
                    }));
                }
            });
  • 当点击开始录制按钮时将窗体最小化,停止录制时通过循环之前保存的文件夹地址排序循环添加每一帧图像到 GifBitmapEncoder.Frames 中,但是在使用自带的 GifBitmapEncoder 发现内存占用很高,当使用完成后没有释放 GC ,所以放弃了使用它。哪位大佬有好的方式欢迎分享

  • 使用了GifEncoder 自己写入 GIF 文件。

  • 保存 gif 文件可以使用以下库

    • FreeImage.NET

    • WpfAnimatedGif

    • ImageTools

    • Magick.NET

    • GifRenderer

2) MainWindow.xaml 代码如下:

<wd:Window
    x:Class="DesktopRecord.View.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:vm="clr-namespace:DesktopRecord.ViewModel"
    xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"
    Title="屏幕录制"
    Width="525"
    Height="200"
    Icon="/screen.ico"
    ResizeMode="CanMinimize"
    WindowStartupLocation="CenterScreen"
    mc:Ignorable="d">
    <wd:Window.DataContext>
        <vm:MainVM />
    </wd:Window.DataContext>
    <Grid>
        <TabControl>
            <TabItem Header="ffmpeg 录制">
                <StackPanel
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Orientation="Horizontal">
                    <Button
                        Margin="0,0,5,0"
                        Command="{Binding MyStart}"
                        Content="{Binding MyTime}"
                        Style="{StaticResource WD.SuccessPrimaryButton}" />
                    <Button
                        Margin="5,0,0,0"
                        Command="{Binding MyStop}"
                        Content="停止录制"
                        Style="{StaticResource WD.DangerPrimaryButton}" />
                </StackPanel>
            </TabItem>
            <TabItem Header="WindowsAPI 录制">
                <StackPanel
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Orientation="Horizontal">
                    <Button
                        Margin="0,0,5,0"
                        Command="{Binding RecordCommand}"
                        Content="开始录制"
                        Style="{StaticResource WD.SuccessPrimaryButton}" />
                    <Button
                        Margin="5,0,0,0"
                        wd:Loading.Child="{x:Static wd:NormalLoading.Default}"
                        wd:Loading.IsShow="{Binding IsShow}"
                        Command="{Binding RecordStopCommand}"
                        Content="停止录制"
                        Style="{StaticResource WD.DangerPrimaryButton}" />
                </StackPanel>
            </TabItem>
        </TabControl>
    </Grid>
</wd:Window>

3)创建 MainVM.cs 代码如下:

using DesktopRecord.Helper;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Threading;
using WPFDevelopers.Controls;
using WPFDevelopers.Helpers;

namespace DesktopRecord.ViewModel
{
    public class MainVM : ViewModelBase
    {
        private DispatcherTimer tm = new DispatcherTimer();

        public int currentCount = 0;

        private string myTime = "开始录制";

        public string MyTime
        {
            get { return myTime; }
            set
            {
                myTime = value;
                NotifyPropertyChange("MyTime");
            }
        }


        private bool isStart = true;

        public bool IsStart
        {
            get { return isStart; }
            set
            {
                isStart = value;
                NotifyPropertyChange("IsStart");
            }
        }


        private bool _isShow;

        public bool IsShow
        {
            get { return _isShow; }
            set
            {
                _isShow = value;
                NotifyPropertyChange("IsShow");
            }
        }

        private ICommand myStart;

        public ICommand MyStart
        {
            get
            {
                return myStart ?? (myStart = new RelayCommand(p =>
                {
                    App.Current.MainWindow.WindowState = System.Windows.WindowState.Minimized;
                    if (!FFmpegHelper.Start())
                    {
                        App.Current.MainWindow.WindowState = System.Windows.WindowState.Normal;
                        MessageBox.Show("未找到 【ffmpeg.exe】,请下载", "错误", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
                        return;
                    }
                    tm.Tick += tm_Tick;
                    tm.Interval = TimeSpan.FromSeconds(1);
                    tm.Start();
                    IsStart = false;
               }, a =>
                {
                return IsStart;
                }));
            }
        }
        private void tm_Tick(object sender, EventArgs e)
        {
            currentCount++;
            MyTime = "录制中(" + currentCount + "s)";
        }
        /// <summary>
        /// 获取或设置
        /// </summary>
        private ICommand myStop;
        /// <summary>
        /// 获取或设置
        /// </summary>
        public ICommand MyStop
        {
            get
            {
                return myStop ?? (myStop = new RelayCommand(p =>
                           {
                               var task = new Task(() =>
                               {
                                   FFmpegHelper.Stop();
                                   MyTime = "开始录制";
                                   tm.Stop();
                                   currentCount = 0;
                                   IsShow = true;
                               });
                               task.ContinueWith(previousTask =>
                               {
                                   IsShow = false;
                                   IsStart = true;
                                   Process.Start(AppDomain.CurrentDomain.BaseDirectory);
                               }, TaskScheduler.FromCurrentSynchronizationContext());
                               task.Start();
                           }, a =>
            {
                return !IsStart;
            }));
            }
        }
        public ICommand RecordCommand { get; }
        public ICommand RecordStopCommand { get; }
        public MainVM()
        {
            RecordCommand = new RelayCommand(Record, CanExecuteRecordCommand);
            RecordStopCommand = new RelayCommand(RecordStop);
        }
        void Record(object parameter)
        {
            App.Current.MainWindow.WindowState = System.Windows.WindowState.Minimized;
            Win32.Start();
            IsStart = false;
        }

        private bool CanExecuteRecordCommand(object parameter)
        {
            return IsStart;
        }
        void RecordStop(object parameter)
        {
            var task = new Task(() =>
            {
                Win32.Stop();
                IsShow = true;
                Win32.Save($"DesktopRecord_{DateTime.Now.ToString("yyyyMMddHHmmss")}.gif");
            });
            task.ContinueWith(previousTask =>
            {
                IsShow = false;
                IsStart = true;
                Process.Start(AppDomain.CurrentDomain.BaseDirectory);
            }, TaskScheduler.FromCurrentSynchronizationContext());
            task.Start();
        }

    }
}

4)创建 Win32.cs 代码如下:

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace DesktopRecord.Helper
{
    public class Win32
    {
        [DllImport("user32.dll")]
        public static extern IntPtr GetDesktopWindow();

        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowDC(IntPtr hwnd);

        [DllImport("user32.dll")]
        public static extern IntPtr ReleaseDC(IntPtr hwnd, IntPtr hdc);

        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);

        [DllImport("gdi32.dll")]
        public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

        [DllImport("gdi32.dll")]
        public static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, System.Int32 dwRop);

        [DllImport("user32.dll")]
        private static extern bool GetCursorInfo(out CURSORINFO pci);

        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int X;
            public int 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 GetCursorPos(out POINT lpPoint);
        [DllImport("user32.dll")]
        public static extern bool DestroyIcon(IntPtr handle);

        private static string basePath = AppDomain.CurrentDomain.BaseDirectory;


        private static string tempDir = Path.Combine(Path.GetTempPath(), "DesktopRecord");
        private static Thread _thread = null;

        public static bool IsRunning = false;
        static int screenWidth = Convert.ToInt32(SystemParameters.PrimaryScreenWidth);
        static int screenHeight = Convert.ToInt32(SystemParameters.PrimaryScreenHeight);

        private static BitmapSource GetCursorIcon()
        {
            var cursorInfo = new CURSORINFO { cbSize = Marshal.SizeOf(typeof(CURSORINFO)) };
            if (GetCursorInfo(out cursorInfo) && cursorInfo.hCursor != IntPtr.Zero)
            {
                try
                {
                    return Imaging.CreateBitmapSourceFromHIcon(cursorInfo.hCursor, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                }
                finally
                {
                    DestroyIcon(cursorInfo.hCursor);
                }
            }

            return null;
        }
        private static BitmapSource CaptureScreen()
        {
            IntPtr desk = GetDesktopWindow();
            IntPtr dc = GetWindowDC(desk);

            IntPtr memdc = CreateCompatibleDC(dc);
            IntPtr bitmap = CreateCompatibleBitmap(dc, screenWidth, screenHeight);
            SelectObject(memdc, bitmap);
            BitBlt(memdc, 0, 0, screenWidth, screenHeight, dc, 0, 0, 0xCC0020);
            BitmapSource source = Imaging.CreateBitmapSourceFromHBitmap(bitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            ReleaseDC(desk, dc);
            return source;
        }

        public static void Start()
        {
            if (_thread == null)
            {
                IsRunning = true;
                _thread = new Thread(Record);
                _thread.Start();
            }
        }
        public static void Stop()
        {
            if (_thread != null)
            {
                IsRunning = false;
                _thread = null;
            }
        }
        private static void Record()
        {
            if (!Directory.Exists(tempDir))
                Directory.CreateDirectory(tempDir);
            else
            {
                foreach (string file in Directory.GetFiles(tempDir))
                    File.Delete(file);
            }
            int num = 0;
            Task.Factory.StartNew(() => 
            {
                while (IsRunning)
                {
                    Thread.Sleep(20);
                    num += 1;
                    Application.Current.Dispatcher.Invoke(new Action(() =>
                    {
                        var drawingVisual = new DrawingVisual();
                        POINT mousePosition;
                        using (DrawingContext drawingContext = drawingVisual.RenderOpen())
                        {
                            drawingContext.DrawImage(CaptureScreen(),
                                new Rect(new Point(),
                                new Size(screenWidth, screenHeight)));

                            if (GetCursorPos(out mousePosition))
                            {
                                var cursorSize = 30;
                                var cursorHalfSize = cursorSize / 2;
                                var cursorCenterX = mousePosition.X - SystemParameters.VirtualScreenLeft;
                                var cursorCenterY = mousePosition.Y - SystemParameters.VirtualScreenTop;
                                drawingContext.DrawImage(GetCursorIcon(),
                                    new Rect(new Point(cursorCenterX, cursorCenterY),
                                    new Size(cursorSize, cursorSize)));

                            }
                        }

                        var png = Path.Combine(tempDir, $"{num}.jpg");
                        using (FileStream stream = new FileStream(png, FileMode.Create))
                        {
                            var bitmap = new RenderTargetBitmap((int)screenWidth, (int)screenHeight, 96, 96, PixelFormats.Pbgra32);
                            bitmap.Render(drawingVisual);
                            var bitmapEncoder = BitmapFrame.Create(bitmap);
                            bitmapEncoder.Freeze();
                            var encoder = new JpegBitmapEncoder();
                            encoder.QualityLevel = 50;
                            encoder.Frames.Add(bitmapEncoder);
                            encoder.Save(stream);
                            encoder.Frames.Clear();
                            GC.Collect();
                        }
                    }));
                }
            });
            
            
        }

        public static void ClearRecording()
        {
            if (Directory.Exists(tempDir))
                Directory.Delete(tempDir, true);
            Directory.CreateDirectory(tempDir);
        }
       
        public static void Save(string output)
        {
            try
            {
                output = Path.Combine(basePath, output);
                var imagePaths = Directory.GetFiles(tempDir, "*.jpg", SearchOption.TopDirectoryOnly);
                if (imagePaths.Length == 0) return;

                #region GC不释放,暂时弃用

                //using (var gifFileStream = new FileStream(Output, FileMode.Create))
                //{
                //    var gifBitmapEncoder = new GifBitmapEncoder();
                //    var jpgs = Directory.GetFiles(tempDir, "*.jpg", SearchOption.TopDirectoryOnly);
                //    if (jpgs.Length == 0) return;
                //    foreach (string file in jpgs)
                //    {
                //        using (var stream = new FileStream(file, FileMode.Open, FileAccess.Read))
                //        {
                //            var bitmapDecoder = new JpegBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
                //            var bitmapFrame = bitmapDecoder.Frames[0];
                //            bitmapDecoder.Frames[0].Freeze();
                //            gifBitmapEncoder.Frames.Add(bitmapFrame);
                //            bitmapFrame = null;
                //            bitmapDecoder = null;
                //            GC.Collect();
                //            stream.Dispose();
                //        }
                //    }
                //    gifBitmapEncoder.Save(gifFileStream);
                //    gifBitmapEncoder.Frames.Clear();
                //    gifBitmapEncoder = null;
                //    GC.Collect();
                //    GC.WaitForPendingFinalizers();
                //} 
                #endregion


                var bitmapFrames = new List<BitmapFrame>();
                foreach (string imagePath in imagePaths)
                {
                    var frame = BitmapFrame.Create(new Uri(imagePath, UriKind.RelativeOrAbsolute));
                    bitmapFrames.Add(frame);
                }
                using (var gifStream = new MemoryStream())
                {
                    using (var encoder = new GifEncoder(gifStream))
                    {
                        foreach (var imagePath in imagePaths)
                        {
                            var image = System.Drawing.Image.FromFile(imagePath);
                            encoder.AddFrame(image, 0, 0, TimeSpan.FromSeconds(0));
                        }
                    }
                    gifStream.Position = 0;
                    using (var fileStream = new FileStream(output, FileMode.Create))
                    {
                        fileStream.Write(gifStream.ToArray(), 0, gifStream.ToArray().Length);
                    }
                }


            }
            catch
            {
                throw;
            }

        }

    }
}

5)创建 GifEncoder.cs 代码如下:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;

namespace DesktopRecord.Helper
{
    public class GifEncoder : IDisposable
    {
        #region Header Constants
        private const string FileType = "GIF";
        private const string FileVersion = "89a";
        private const byte FileTrailer = 0x3b;

        private const int ApplicationExtensionBlockIdentifier = 0xff21;
        private const byte ApplicationBlockSize = 0x0b;
        private const string ApplicationIdentification = "NETSCAPE2.0";

        private const int GraphicControlExtensionBlockIdentifier = 0xf921;
        private const byte GraphicControlExtensionBlockSize = 0x04;

        private const long SourceGlobalColorInfoPosition = 10;
        private const long SourceGraphicControlExtensionPosition = 781;
        private const long SourceGraphicControlExtensionLength = 8;
        private const long SourceImageBlockPosition = 789;
        private const long SourceImageBlockHeaderLength = 11;
        private const long SourceColorBlockPosition = 13;
        private const long SourceColorBlockLength = 768;
        #endregion

        private bool _isFirstImage = true;
        private int? _width;
        private int? _height;
        private int? _repeatCount;
        private readonly Stream _stream;

        public TimeSpan FrameDelay { get; set; }

        /// <summary>
        /// Encodes multiple images as an animated gif to a stream. <br />
        /// ALWAYS ALWAYS ALWAYS wire this in a using block <br />
        /// Disposing the encoder will complete the file. <br />
        /// Uses default .net GIF encoding and adds animation headers.
        /// </summary>
        /// <param name="stream">The stream that will be written to.</param>
        /// <param name="width">Sets the width for this gif or null to use the first frame's width.</param>
        /// <param name="height">Sets the height for this gif or null to use the first frame's height.</param>
        public GifEncoder(Stream stream, int? width = null, int? height = null, int? repeatCount = null)
        {
            _stream = stream;
            _width = width;
            _height = height;
            _repeatCount = repeatCount;
        }

        /// <summary>
        /// Adds a frame to this animation.
        /// </summary>
        /// <param name="img">The image to add</param>
        /// <param name="x">The positioning x offset this image should be displayed at.</param>
        /// <param name="y">The positioning y offset this image should be displayed at.</param>
        public void AddFrame(Image img, int x = 0, int y = 0, TimeSpan? frameDelay = null)
        {
            using (var gifStream = new MemoryStream())
            {
                img.Save(gifStream, ImageFormat.Gif);
                if (_isFirstImage) // Steal the global color table info
                {
                    InitHeader(gifStream, img.Width, img.Height);
                }
                WriteGraphicControlBlock(gifStream, frameDelay.GetValueOrDefault(FrameDelay));
                WriteImageBlock(gifStream, !_isFirstImage, x, y, img.Width, img.Height);
            }
            _isFirstImage = false;
        }

        private void InitHeader(Stream sourceGif, int w, int h)
        {
            // File Header
            WriteString(FileType);
            WriteString(FileVersion);
            WriteShort(_width.GetValueOrDefault(w)); // Initial Logical Width
            WriteShort(_height.GetValueOrDefault(h)); // Initial Logical Height
            sourceGif.Position = SourceGlobalColorInfoPosition;
            WriteByte(sourceGif.ReadByte()); // Global Color Table Info
            WriteByte(0); // Background Color Index
            WriteByte(0); // Pixel aspect ratio
            WriteColorTable(sourceGif);

            // App Extension Header
            WriteShort(ApplicationExtensionBlockIdentifier);
            WriteByte(ApplicationBlockSize);
            WriteString(ApplicationIdentification);
            WriteByte(3); // Application block length
            WriteByte(1);
            WriteShort(_repeatCount.GetValueOrDefault(0)); // Repeat count for images.
            WriteByte(0); // terminator
        }

        private void WriteColorTable(Stream sourceGif)
        {
            sourceGif.Position = SourceColorBlockPosition; // Locating the image color table
            var colorTable = new byte[SourceColorBlockLength];
            sourceGif.Read(colorTable, 0, colorTable.Length);
            _stream.Write(colorTable, 0, colorTable.Length);
        }

        private void WriteGraphicControlBlock(Stream sourceGif, TimeSpan frameDelay)
        {
            sourceGif.Position = SourceGraphicControlExtensionPosition; // Locating the source GCE
            var blockhead = new byte[SourceGraphicControlExtensionLength];
            sourceGif.Read(blockhead, 0, blockhead.Length); // Reading source GCE

            WriteShort(GraphicControlExtensionBlockIdentifier); // Identifier
            WriteByte(GraphicControlExtensionBlockSize); // Block Size
            WriteByte(blockhead[3] & 0xf7 | 0x08); // Setting disposal flag
            WriteShort(Convert.ToInt32(frameDelay.TotalMilliseconds / 10)); // Setting frame delay
            WriteByte(blockhead[6]); // Transparent color index
            WriteByte(0); // Terminator
        }

        private void WriteImageBlock(Stream sourceGif, bool includeColorTable, int x, int y, int h, int w)
        {
            sourceGif.Position = SourceImageBlockPosition; // Locating the image block
            var header = new byte[SourceImageBlockHeaderLength];
            sourceGif.Read(header, 0, header.Length);
            WriteByte(header[0]); // Separator
            WriteShort(x); // Position X
            WriteShort(y); // Position Y
            WriteShort(h); // Height
            WriteShort(w); // Width

            if (includeColorTable) // If first frame, use global color table - else use local
            {
                sourceGif.Position = SourceGlobalColorInfoPosition;
                WriteByte(sourceGif.ReadByte() & 0x3f | 0x80); // Enabling local color table
                WriteColorTable(sourceGif);
            }
            else
            {
                WriteByte(header[9] & 0x07 | 0x07); // Disabling local color table
            }

            WriteByte(header[10]); // LZW Min Code Size

            // Read/Write image data
            sourceGif.Position = SourceImageBlockPosition + SourceImageBlockHeaderLength;

            var dataLength = sourceGif.ReadByte();
            while (dataLength > 0)
            {
                var imgData = new byte[dataLength];
                sourceGif.Read(imgData, 0, dataLength);

                _stream.WriteByte(Convert.ToByte(dataLength));
                _stream.Write(imgData, 0, dataLength);
                dataLength = sourceGif.ReadByte();
            }

            _stream.WriteByte(0); // Terminator

        }

        private void WriteByte(int value)
        {
            _stream.WriteByte(Convert.ToByte(value));
        }

        private void WriteShort(int value)
        {
            _stream.WriteByte(Convert.ToByte(value & 0xff));
            _stream.WriteByte(Convert.ToByte((value >> 8) & 0xff));
        }

        private void WriteString(string value)
        {
            _stream.Write(value.ToArray().Select(c => (byte)c).ToArray(), 0, value.Length);
        }

        public void Dispose()
        {
            // Complete File
            WriteByte(FileTrailer);

            // Pushing data
            _stream.Flush();
        }
    }
}

参考资料

[1]

原文链接: https://github.com/yanjinhuagood/DesktopRecord

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值