WPF 实现调用 ffmpeg 实现屏幕录制
控件名:DesktopRecord
作 者:WPFDevelopersOrg - 驚鏵
原文链接[1]:https://github.com/yanjinhuagood/DesktopRecord
框架使用
.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;
}));
}
}
![fe3473d58c96598e65b0296d9e077b77.gif](https://i-blog.csdnimg.cn/blog_migrate/ee5e2cd907913c346d720e579fa89dd2.gif)
参考资料
[1]
原文链接: https://github.com/yanjinhuagood/DesktopRecord
[2]ffmpeg: http://www.ffmpeg.org/download.html
[3]ffmpeg.exe: https://github.com/yanjinhuagood/DesktopRecord/releases/expanded_assets/ffmepg.exe