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

  • 框架使用.NET4
  • Visual Studio 2022
  • 需要去 ffmpeg[2] 官网下载 Windows 解压进入 ffmpeg-4.1.1-win32-static\bin\ 或者 下载 ffmpeg.exe[3] 拷贝到运行目录下的ffmpeg 文件夹下 DesktopRecord.exe 就可以进行录屏,结束录屏后视频保存在运行程序根目录下。.
  • 使用参数命令进行录屏 "-f gdigrab -framerate 30 -offset_x 0 -offset_y 0 -video_size 1920x1080 -i desktop -c:v libx264 -preset ultrafast -crf 0 " + DateTime.Now.ToString("yyyyMMddHHmmss") + "_DesktopRecord.mp4"
    • -f gdigrab: 设定视频输入来源为 Windows 桌面画面捕获;
    • -framerate 30: 设置帧率为 30fps
    • -offset_x 0 -offset_y 0: 设置捕获起始坐标为 (0, 0)
    • -video_size 1920x1080: 设置视频分辨率为 1920x1080
    • -i desktop: 指示从桌面捕获视频流;
    • -c:v libx264: 使用 libx264 编码器进行视频压缩;
    • -preset ultrafast: 设定视频压缩速度为最快;
    • -crf 0: 设置视频压缩质量无限制(CRF 为 0 表示最高质量);
    • " + DateTime.Now.ToString("yyyyMMddHHmmss") + "_DesktopRecord.mp4": 指定视频输出文件名为 yyyyMMddHHmmss_DesktopRecord.mp4

1)创建 FFmpegHelper.cs 代码如下:

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace DesktopRecord.Helper
{
    public class FFmpegHelper
    {
        #region 模拟控制台信号需要使用的api

        [DllImport("kernel32.dll")]
        static extern bool GenerateConsoleCtrlEvent(int dwCtrlEvent, int dwProcessGroupId);

        [DllImport("kernel32.dll")]
        static extern bool SetConsoleCtrlHandler(IntPtr handlerRoutine, bool add);

        [DllImport("kernel32.dll")]
        static extern bool AttachConsole(int dwProcessId);

        [DllImport("kernel32.dll")]
        static extern bool FreeConsole();

        #endregion
        // ffmpeg进程
        static Process _process;

        // ffmpeg.exe实体文件路径
        static string ffmpegPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ffmpeg.exe");

        /// <summary>
        /// 功能: 开始录制
        /// </summary>
        public static bool Start()
        {
            if(!File.Exists(ffmpegPath))
                return false;
            var processInfo = new ProcessStartInfo
            {
                FileName = ffmpegPath,
                Arguments = "-f gdigrab -framerate 30 -offset_x 0 -offset_y 0 -video_size 1920x1080 -i desktop -c:v libx264 -preset ultrafast -crf 0 " + DateTime.Now.ToString("yyyyMMddHHmmss") + "_DesktopRecord.mp4",
                UseShellExecute = false,
                RedirectStandardInput = true,
                RedirectStandardOutput = true,
                CreateNoWindow = true
            };
            _process = new Process { StartInfo = processInfo };
            _process.Start();
            return true;
        }

        /// <summary>
        /// 功能: 停止录制
        /// </summary>
        public static void Stop()
        {
            if (_process == null) return;
            AttachConsole(_process.Id);
            SetConsoleCtrlHandler(IntPtr.Zero, true);
            GenerateConsoleCtrlEvent(0, 0);
            FreeConsole();
            _process.StandardInput.Write("q");
            if (!_process.WaitForExit(10000))
            {
                _process.Kill();
            }
        }
    }
}

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>
        </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;
            }));
            }
        }

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