WPF调用ffmpeg实现屏幕录制
作者:WPF开发者 发布时间:2023-04-23 13:57:00
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;
}));
}
}
效果图
来源:https://mp.weixin.qq.com/s/tPj0fUmCT_UiDYu2_35UkA


猜你喜欢
- 在Android中偶尔会用到开关,Switch就是一个简单易使用的不错的控件。首先,在布局中添加上Switch控件:<Switch &
- 背景 最近在工作中遇到一个需求,需要在接收到推送的时候将推送获得的数据存起来,以供app启动时使用。我们会认为这不是So eas
- 环境:springcloud Hoxton.SR11本节主要了解系统中的谓词与配置的路由信息是如何进行初始化关联生成路由对象的。每个谓词工厂
- GridView基础新建一个HelloGridView的工程修改main.xml代码如下:<?xml version="1.
- 环境配置:jdk1.8mybatis3.4.1springboot2.0起始原因:编写mybatis的Demo程序时,mapper传递多参数
- 这篇文章主要介绍了spring cloud gateway网关路由分配代码实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有
- 前言在使用maven配置Mybatis generator插件时报以下错误,generator插件一直无法使用,查询资料说和eclipse版
- A-PC端:1-页面--multiple是控制单张还是多张图片上传<input id="BusRoute" typ
- 引言Spring Boot的一个便捷功能是外部化配置,可以轻松访问属性文件中定义的属性。本文将详细介绍@ConfigurationPrope
- 在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,这样一方面是线程的创建更加规范,可以合理
- 智能终端设备的多点触控操作为我们带来了种种炫酷体验,这也使得很多A
- 公司的以前的项目,看到使用了这个Android自带的倒计时控件Chronometer,现在整合了一下先看看效果:<Chronomete
- 一、Spring Boot任务线程池线程池的作用防止资源占用无限的扩张调用过程省去资源的创建和销毁所占用的时间在高并发环境下,不断的分配新资
- java 实现文件夹的拷贝实例代码 这里就直接上代码,
- 缓冲区的建立目的就是增加传输性能,使我们传输数据更加快速缓冲区的内部其实很简单 就是在内部封装了一个数组 用数组来存储数据 对外提供一些方法
- 这篇文章主要介绍了springboot配置aop切面日志打印过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习
- 小伙伴们在使用ICP提供的各种能力进行集成开发时常常会遇到一些技术上的困扰,例如ICP中很多接口是通过OCX控件的方式提供的,如何调用这些接
- 本文实例讲述了Android实现的仿淘宝购物车。分享给大家供大家参考,具体如下:夏的热情渐渐退去,秋如期而至,丰收的季节,小编继续着实习之路
- 本文实例讲述了C#图像伪彩色处理方法。分享给大家供大家参考。具体如下://灰度图转伪彩色图像函数public Bitmap PGrayToC
- 一、 springBoot + Mybatis配置完成后,访问数据库遇到的问题首先出现这个问题,肯定是xml文件与mapper接口没有匹配上