WPF录屏应用开发:如何将截屏帧合成为视频文件?
WPF截屏帧合成视频解决方案
你的核心需求是把截屏得到的帧合成视频,WPF本身没有原生视频编码API,推荐使用FFmpeg.AutoGen(开源免费、功能全面)来实现,以下是具体步骤和代码示例:
一、优化截屏代码
当前你返回的BitmapImage是用于WPF控件显示的,编码视频时直接用System.Drawing.Bitmap效率更高,修改CaptureScreen方法:
private System.Drawing.Bitmap CaptureScreen() { var screenSize = System.Windows.Forms.Screen.PrimaryScreen.Bounds; var target = new System.Drawing.Bitmap(screenSize.Width, screenSize.Height); using (var g = System.Drawing.Graphics.FromImage(target)) { g.CopyFromScreen(0, 0, 0, 0, screenSize.Size); } return target; }
二、集成FFmpeg.AutoGen实现视频编码
1. 安装依赖
通过NuGet安装FFmpeg.AutoGen,同时需要下载对应平台的FFmpeg原生库(比如Windows下的avcodec-58.dll、avformat-58.dll等),将这些dll放到项目输出目录,并设置“复制到输出目录”为“始终复制”。
2. 视频编码核心实现
创建一个视频编码器类,负责初始化、帧编码、资源清理:
using FFmpeg.AutoGen; using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; public class ScreenRecorder { private AVFormatContext* _formatContext; private AVStream* _videoStream; private AVCodecContext* _codecContext; private SwsContext* _swsContext; private AVPacket _packet; private AVFrame* _frame; private int _frameIndex; private readonly int _width; private readonly int _height; private readonly int _fps; public ScreenRecorder(string outputPath, int width, int height, int fps = 30) { _width = width; _height = height; _fps = fps; InitializeFFmpeg(); InitializeOutputContext(outputPath); InitializeVideoStream(); InitializeFrameAndConverter(); } private void InitializeFFmpeg() { ffmpeg.av_register_all(); ffmpeg.avformat_network_init(); } private void InitializeOutputContext(string outputPath) { ffmpeg.avformat_alloc_output_context2(&_formatContext, null, null, outputPath); if (_formatContext == null) { throw new InvalidOperationException("Failed to create output context"); } } private void InitializeVideoStream() { var codec = ffmpeg.avcodec_find_encoder(AVCodecID.AV_CODEC_ID_H264); if (codec == null) { throw new InvalidOperationException("H.264 encoder not found"); } _videoStream = ffmpeg.avformat_new_stream(_formatContext, codec); _codecContext = ffmpeg.avcodec_alloc_context3(codec); _codecContext->codec_id = AVCodecID.AV_CODEC_ID_H264; _codecContext->codec_type = AVMediaType.AVMEDIA_TYPE_VIDEO; _codecContext->pix_fmt = AVPixelFormat.AV_PIX_FMT_YUV420P; _codecContext->width = _width; _codecContext->height = _height; _codecContext->time_base = new AVRational { num = 1, den = _fps }; _codecContext->framerate = new AVRational { num = _fps, den = 1 }; _codecContext->bit_rate = 4000000; // 4Mbps码率,可根据需求调整 if (_formatContext->oformat->flags.HasFlag(AVFMT_GLOBALHEADER)) { _codecContext->flags |= ffmpeg.AV_CODEC_FLAG_GLOBAL_HEADER; } if (ffmpeg.avcodec_open2(_codecContext, codec, null) < 0) { throw new InvalidOperationException("Failed to open codec"); } ffmpeg.avcodec_parameters_from_context(_videoStream->codecpar, _codecContext); } private void InitializeFrameAndConverter() { _frame = ffmpeg.av_frame_alloc(); _frame->format = (int)AVPixelFormat.AV_PIX_FMT_YUV420P; _frame->width = _width; _frame->height = _height; ffmpeg.av_frame_get_buffer(_frame, 0); _swsContext = ffmpeg.sws_getContext( _width, _height, AVPixelFormat.AV_PIX_FMT_BGR24, _width, _height, AVPixelFormat.AV_PIX_FMT_YUV420P, ffmpeg.SWS_BILINEAR, null, null, null); _packet = new AVPacket(); ffmpeg.av_init_packet(&_packet); } public void EncodeFrame(Bitmap bitmap) { // 将Bitmap转换为BGR24格式的像素数据 var bitmapData = bitmap.LockBits(new Rectangle(0, 0, _width, _height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); try { var srcLinesize = new int[] { bitmapData.Stride }; var srcData = new IntPtr[] { bitmapData.Scan0 }; ffmpeg.sws_scale( _swsContext, srcData, srcLinesize, 0, _height, _frame->data, _frame->linesize); } finally { bitmap.UnlockBits(bitmapData); } _frame->pts = _frameIndex++; // 编码帧 if (ffmpeg.avcodec_send_frame(_codecContext, _frame) >= 0) { while (ffmpeg.avcodec_receive_packet(_codecContext, &_packet) >= 0) { ffmpeg.av_packet_rescale_ts(&_packet, _codecContext->time_base, _videoStream->time_base); ffmpeg.av_interleaved_write_frame(_formatContext, &_packet); ffmpeg.av_packet_unref(&_packet); } } } public void FinishRecording() { // 发送空帧,处理剩余编码数据 ffmpeg.avcodec_send_frame(_codecContext, null); while (ffmpeg.avcodec_receive_packet(_codecContext, &_packet) >= 0) { ffmpeg.av_packet_rescale_ts(&_packet, _codecContext->time_base, _videoStream->time_base); ffmpeg.av_interleaved_write_frame(_formatContext, &_packet); ffmpeg.av_packet_unref(&_packet); } // 写入文件尾 ffmpeg.av_write_trailer(_formatContext); // 清理资源 ffmpeg.avcodec_close(_codecContext); ffmpeg.av_frame_free(&_frame); ffmpeg.sws_freeContext(_swsContext); ffmpeg.avformat_free_context(_formatContext); } }
3. 修改录屏逻辑
在你的WPF窗口类中,集成上述编码器,调整DispatcherTimer的逻辑:
private ScreenRecorder _recorder; private DispatcherTimer _dispatcherTimer; // 开始录屏方法 private void StartRecording() { var screenSize = System.Windows.Forms.Screen.PrimaryScreen.Bounds; _recorder = new ScreenRecorder("output.mp4", screenSize.Width, screenSize.Height, 30); _dispatcherTimer = new DispatcherTimer(); _dispatcherTimer.Interval = TimeSpan.FromMilliseconds(1000 / 30); // 30帧每秒 _dispatcherTimer.Tick += DispatcherTimer_Tick; _dispatcherTimer.Start(); } private void DispatcherTimer_Tick(object sender, EventArgs e) { using (var bitmap = CaptureScreen()) { ScreenImage.Source = BitmapToBitmapImage(bitmap); // 继续显示到WPF控件 _recorder.EncodeFrame(bitmap); } } // 结束录屏方法 private void StopRecording() { _dispatcherTimer.Stop(); _recorder.FinishRecording(); } // 辅助方法:Bitmap转BitmapImage用于WPF显示 private BitmapImage BitmapToBitmapImage(Bitmap bitmap) { using (var memory = new System.IO.MemoryStream()) { bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Png); memory.Position = 0; var bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.StreamSource = memory; bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.EndInit(); bitmapImage.Freeze(); // 冻结避免跨线程问题 return bitmapImage; } }
三、注意事项
- 性能优化:录屏时尽量避免UI卡顿,可将编码逻辑放到后台线程(比如用
Task.Run包裹编码步骤);调整帧率和码率平衡画质与文件大小。 - FFmpeg库兼容:确保下载的FFmpeg库版本与FFmpeg.AutoGen的版本匹配,避免运行时错误。
- 内存管理:所有
Bitmap和FFmpeg资源要及时释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Sarper Makas
相关产品推荐
相关产品推荐

