
简介这是一套面向C#开发者与多媒体应用学习者的屏幕与摄像头同步录制工具源码工程解决桌面端音视频多源采集、合成与保存的实际开发需求适用于远程教学、会议记录、软件演示等场景。资源共87个文件包含14个核心C#源码如MainWindow.xaml.cs、SettingWindow.xaml.cs、25个关键DLL含AForge.Video.DirectShow、NAudio、FFmpeg封装库、8个XAML界面文件及4个可执行程序整体压缩包仅22.79MB结构清晰、模块解耦明确便于快速理解录屏菜单、屏幕绘画、音视频组合设置等核心功能实现逻辑。已有870人学习下载读者可直接运行调试掌握基于AForge的视频捕获、NAudio的音频采集、FFmpeg音视频合成等关键技术链并复用HotKey、DrawerWindow、CameraShowWindow等已封装UI组件与设置管理模块显著降低同类项目开发门槛。1. 项目概述与核心价值最近在整理硬盘时翻到了一个尘封已久的项目文件夹里面躺着一个名为“VideoRecSoft”的C#工程。这个项目是我几年前为了解决一个特定需求而开发的核心功能如其名一个集屏幕录像、摄像头录像以及屏幕和摄像头画面同时录制于一体的桌面软件。当时市面上虽然有不少录屏工具但要么功能臃肿附带广告要么在同时录制屏幕和摄像头画面时要么性能开销巨大导致卡顿要么画面和音频同步做得稀烂。于是本着“自己动手丰衣足食”的原则我决定用C#和几个经典的音视频处理库从头搭建一个轻量、高效且可控的录制工具。这个项目麻雀虽小五脏俱全。它不仅仅是一个简单的调用系统API的封装更涉及到了多路视频流的捕获、实时编码、音画同步、内存管理以及一个简洁的WPF用户界面。对于正在学习C#、对多媒体处理、Windows桌面开发特别是对DirectShow、AForge.NET等框架感兴趣的朋友来说这个项目的源码是一个非常好的学习样本。你可以看到如何从零开始组织一个功能完整的桌面应用如何处理复杂的多线程数据流以及如何优化性能以避免录制高清视频时的卡顿和内存泄漏。无论你是想开发自己的录课软件、游戏直播工具还是仅仅想深入理解Windows下的视频捕获原理这个项目都能给你带来不少启发。2. 项目整体架构与技术选型解析2.1 核心功能模块拆解VideoRecSoft的核心功能可以清晰地划分为三个独立又相互关联的模块屏幕捕获模块负责抓取整个屏幕或指定区域的画面。这里没有采用简单的截屏循环因为那样效率低下且帧率不稳定。项目核心使用了基于DirectX的屏幕捕获技术通过DesktopDuplication APIWindows 8来高效获取显卡输出到桌面的帧缓冲区数据。这种方式能获得极高的性能尤其是在录制全屏游戏或应用时几乎不影响前端应用的运行效率。摄像头捕获模块负责从USB摄像头或电脑内置摄像头获取视频流。这里主要依赖DirectShow框架和AForge.NET库。DirectShow是Windows平台处理多媒体流的基石提供了强大的滤镜图Filter Graph模型来管理音视频流。AForge.NET则在其基础上进行了封装提供了更易用的C#接口来枚举摄像头、设置分辨率帧率、以及获取每一帧的图像数据。编码与混合模块这是项目的“心脏”。捕获到的原始图像数据通常是Bitmap或字节数组和音频数据需要被实时压缩并写入文件。项目使用了FFmpeg库通过FFmpeg.AutoGen这样的C#封装进行编码。将屏幕流和摄像头流混合的关键在于构建一个复杂的滤镜图Filter Graph将两个视频流进行缩放、叠加比如画中画效果再与音频流混合最终由编码器输出为MP4等格式。2.2 关键技术栈深度剖析为什么选择这些技术这背后是性能、可控性和开发效率的综合考量。C#与WPF作为Windows桌面开发的主力军C#和WPF的组合能快速构建出美观、响应迅速的UI。WPF的数据绑定和命令模式非常适合用来控制录制的开始、暂停、停止并实时更新状态信息如录制时长、文件大小。DirectShow与AForge.NETDirectShow它是Windows平台处理多媒体捕获和播放的事实标准。虽然API是COM-based略显古老和复杂但其稳定性和功能完整性无可替代。我们通过Interop服务调用它来构建从摄像头采集到预览的完整流水线。AForge.NET一个优秀的计算机视觉和人工智能库。在本项目中我们主要用到它的Video命名空间它提供了VideoCaptureDevice类极大地简化了摄像头操作。你不用再手动构建复杂的Filter Graph几行代码就能启动摄像头并订阅新帧事件。但这里有一个关键点AForge.NET内部依然基于DirectShow它是对底层复杂操作的一个高级抽象。FFmpeg音视频处理的“瑞士军刀”。使用其C#封装库我们可以在内存中直接传递图像数据给FFmpeg进行编码避免了将每一帧都先保存为图片再组装的巨大开销。我们通过调用libx264视频和aac音频编码器实现高质量的实时压缩。选择FFmpeg的另一个巨大优势是格式支持的广泛性轻松输出MP4、AVI、MKV等格式。桌面复制API (Desktop Duplication API)这是实现高效屏幕录制的关键。相较于传统的GDI抓取Graphics.CopyFromScreen它直接访问GPU的桌面纹理延迟极低CPU占用小并且能捕获到由GPU加速渲染的内容如DirectX/OpenGL应用。这是实现流畅录制游戏或视频播放画面的技术保障。注意技术选型的权衡。为什么不直接用OBS Studio的开源方案OBS固然强大但其代码库庞大复杂对于学习和定制特定功能来说门槛较高。VideoRecSoft项目的目的之一是教学和原理展示因此选择了更底层、模块更清晰的技术组合让你能看清每一路数据流是如何产生、处理和汇合的。3. 核心模块实现细节与实操要点3.1 屏幕捕获的实现与优化屏幕捕获模块的核心是一个循环它不断地从Desktop Duplication API获取最新的桌面纹理。实现步骤初始化使用SharpDX.DXGI库创建与主显示器关联的Output1对象然后调用DuplicateOutput方法获取OutputDuplication接口。这是访问桌面纹理的句柄。捕获循环在一个独立的后台线程中通常是Task或Thread循环执行以下操作TryAcquireNextFrame尝试获取下一帧。该方法会等待直到有新的帧可用或超时。GetFrameDirtyRects/GetFrameMoveRects获取屏幕上发生变化的区域脏矩形。对于静态画面这能极大减少需要处理的数据量是重要的性能优化点。CopyResource将桌面纹理复制到一个我们可访问的Texture2D资源中。Map/Unmap将纹理数据映射到内存获取包含像素数据的指针。ReleaseFrame必须释放当前帧才能获取下一帧。数据转换获取到的像素数据通常是DXGI_FORMAT_B8G8R8A8_UNORM格式。我们需要将其转换为FFmpeg编码器所需的格式如AV_PIX_FMT_BGR0或AV_PIX_FMT_RGB24。这个转换过程可以在CPU上进行也可以尝试使用GPU通过Compute Shader来加速但对于大多数场景CPU转换已足够高效。实操心得与坑点线程管理捕获循环必须运行在独立的、高优先级的线程中避免被UI线程阻塞导致丢帧。异常处理TryAcquireNextFrame可能因为显示器分辨率改变、休眠等原因失败。代码必须健壮地处理这些异常并尝试重新初始化捕获接口。性能瓶颈纹理复制和格式转换是主要CPU开销。如果录制4K分辨率数据量巨大。一个优化技巧是如果不需要最高画质可以在复制后立即将图像缩放到目标录制分辨率而不是先复制全分辨率再缩放。内存释放SharpDX中的COM对象如Texture2D,Resource必须显式调用Dispose()否则会导致严重的内存泄漏。建议使用using语句块确保资源释放。3.2 摄像头捕获与属性控制摄像头模块使用AForge.NET的VideoCaptureDevice类它让摄像头操作变得异常简单。基础实现// 1. 枚举所有摄像头 var videoDevices new FilterInfoCollection(FilterCategory.VideoInputDevice); // 2. 创建捕获设备 VideoCaptureDevice videoSource new VideoCaptureDevice(videoDevices[0].MonikerString); // 3. 设置分辨率帧率关键步骤 videoSource.VideoResolution videoSource.VideoCapabilities .FirstOrDefault(cap cap.FrameSize.Width 1280 cap.FrameSize.Height 720); videoSource.DesiredFrameRate 30; // 4. 订阅新帧事件 videoSource.NewFrame OnNewFrameFromCamera; // 5. 开始捕获 videoSource.Start();高级控制与常见问题设置摄像头属性除了分辨率和帧率我们经常需要调整曝光、白平衡、对焦等。这些属性需要通过DirectShow的IAMCameraControl和IAMVideoProcAmp接口来访问。AForge.NET的VideoCaptureDevice提供了一个GetCameraProperty和SetCameraProperty的途径但可能需要你传递正确的属性ID和值范围。这部分需要查阅摄像头驱动支持的属性代码需要做兼容性判断。c# aforge设置摄像头视频属性和控制属性这是搜索热词也确实是痛点。很多新手会发现设置了DesiredFrameRate但实际帧率不生效。这是因为你设置的值可能不在摄像头驱动支持的范围内。正确的做法是先通过VideoCapabilities数组查看摄像头真正支持的所有分辨率和帧率组合然后从中选择你想要的那个VideoCapability对象将其直接赋值给VideoResolution属性。DesiredFrameRate最好与你选择的VideoCapability中的帧率一致。丢帧处理在OnNewFrameFromCamera事件处理程序中如果处理速度跟不上摄像头的帧率会导致事件堆积和内存增长。一个实用的技巧是检查帧的时间戳如果当前处理的帧比上一帧旧说明是堆积的旧帧就直接丢弃它只处理最新的帧。资源释放停止摄像头时不仅要调用Stop()最好也取消事件订阅(videoSource.NewFrame - OnNewFrameFromCamera)并将对象置为null以确保GC可以回收资源。3.3 音视频编码与混合录制这是最复杂的部分我们使用FFmpeg库将多路流混合编码成一个文件。流程概述初始化FFmpeg注册所有编解码器和格式。创建输出上下文指定输出文件格式如MP4。创建流为屏幕视频、摄像头视频、系统音频或麦克风音频分别创建AVStream。配置编码器为每个流创建并配置编码器如libx264for H.264视频aacfor 音频设置码率、分辨率、帧率、GOP等参数。编写文件头。主录制循环屏幕帧到来将Bitmap数据转换为FFmpeg接受的AVFrame例如AV_PIX_FMT_BGR24进行编码得到AVPacket写入输出上下文。摄像头帧到来同样转换并编码。关键步骤画面混合。如果要做画中画我们需要在将摄像头帧送入编码器前先将其缩放然后叠加到屏幕帧的指定位置。这可以通过FFmpeg的libavfilter滤镜系统来实现。我们需要构建一个滤镜图输入是屏幕和摄像头两路原始帧输出是混合后的一帧。音频采样到来从系统音频接口如NAudio库捕获或麦克风获取音频样本填充到音频AVFrame中编码并写入。音画同步这是核心挑战。每个视频帧和音频包都必须带有正确的时间戳PTS, Presentation Time Stamp。我们通常选择一个主时钟如系统时间或音频时钟视频帧的PTS根据其捕获时间相对于主时钟来计算。FFmpeg在写入时会根据PTS来安排音画的播放顺序。结束录制写入文件尾释放所有资源。实操中的硬骨头滤镜图构建使用avfilter_graph_alloc()等函数构建滤镜图非常繁琐。一个简单的画中画滤镜描述字符串可能像这样[0:v]scaleiw/4:ih/4 [pip]; [1:v][pip]overlayW-w-10:H-h-10。你需要深刻理解FFmpeg滤镜语法并在C#中正确传递这些字符串和参数。内存与性能原始图像数据到AVFrame的转换、编码都是CPU密集型操作。必须使用BufferPool重用内存避免频繁分配和GC。对于高清视频可以考虑将缩放、色彩空间转换等操作放在GPU上例如使用CUDA或OpenCL但这会极大增加复杂性。错误处理FFmpeg函数调用后必须检查返回值。一个编码错误可能导致整个文件损坏。稳健的做法是当连续遇到多次编码错误时安全地中止录制并保存已成功写入的部分。4. 项目工程结构与代码组织一个清晰的项目结构对于维护和阅读至关重要。VideoRecSoft的解决方案大致组织如下VideoRecSoft.sln ├── VideoRecSoft.Core (类库) │ ├── Capturers/ │ │ ├── IScreenCapturer.cs (接口) │ │ ├── DesktopDuplicationScreenCapturer.cs (具体实现) │ │ ├── ICameraCapturer.cs │ │ └── DirectShowCameraCapturer.cs │ ├── Encoders/ │ │ ├── IVideoEncoder.cs │ │ └── FfmpegVideoEncoder.cs (负责编码和混合) │ ├── Models/ │ │ ├── RecorderSettings.cs (录制参数分辨率、码率、保存路径等) │ │ └── RecordingSession.cs (管理一次录制会话的状态) │ └── Utilities/ │ └── FrameConverter.cs (负责Bitmap与AVFrame间的转换) ├── VideoRecSoft.Wpf (WPF应用程序) │ ├── ViewModels/ (MVVM模式下的ViewModel) │ │ ├── MainViewModel.cs │ │ └── ... │ ├── Views/ (XAML视图) │ │ ├── MainWindow.xaml │ │ └── ... │ ├── Services/ (UI相关服务如对话框) │ └── App.xaml └── 第三方库引用 ├── FFmpeg.AutoGen ├── AForge.Video.DirectShow ├── SharpDX.DXGI ├── NAudio (用于音频捕获) └── ...这种架构的优势高内聚低耦合屏幕捕获、摄像头捕获、编码逻辑被分离到独立的类中通过接口交互。这意味着你可以轻松替换实现例如将FFmpeg编码器换成Media Foundation编码器。易于测试核心逻辑在类库中可以编写单元测试模拟帧输入验证编码输出。UI与逻辑分离WPF项目只关注用户界面和交互所有业务逻辑都通过ViewModel调用Core中的服务。5. 编译、部署与常见问题排查实录5.1 环境准备与编译开发环境Visual Studio 2022安装.NET Desktop Development工作负载。项目目标框架建议为.NET 6或.NET 8长期支持版以获得更好的性能和跨平台潜力尽管核心功能目前是Windows特有的。NuGet包管理通过NuGet安装以下关键包FFmpeg.AutoGen注意这通常只包含C#绑定你需要自行下载对应平台的FFmpeg共享库DLL文件并放置到输出目录如x64或x86子文件夹。这是新手最容易卡住的地方。AForge.Video.DirectShowSharpDX.DXGI(注意SharpDX已归档但对于Desktop Duplication API它仍然是常用选择。也可考虑使用较新的Microsoft.Windows.SDK.CPP和WinRT交互但更复杂)NAudioFFmpeg库处理从官网如gyan.dev下载Shared版本的FFmpeg将其中的avcodec-xx.dll,avformat-xx.dll,avutil-xx.dll,swscale-xx.dll等核心DLL复制到你的项目输出目录。在代码初始化时需要调用FFmpegBinariesHelper.RegisterFFmpegBinaries()来告诉FFmpeg.AutoGen这些DLL的位置。5.2 典型运行问题与解决方案在开发和运行过程中你几乎一定会遇到以下问题问题1启动时抛出DllNotFoundException或BadImageFormatException提示找不到FFmpeg的DLL。排查这是64位/32位不匹配的经典问题。你的C#项目平台目标比如x64必须与你下载的FFmpeg DLL的架构完全一致。解决在Visual Studio中确认项目属性 - 生成 - 平台目标是x64还是Any CPU并取消“首选32位”。去FFmpeg官网下载对应架构win64或win32的Shared版本DLL。将这些DLL放在可执行文件同级目录或者更好的做法是放在x64或x86子目录下并在程序启动时将AppDomain.CurrentDomain.BaseDirectory下的这个子目录添加到PATH环境变量临时或使用SetDllDirectoryAPI。问题2摄像头列表为空或调用AForge.Video.DirectShow相关方法时出错。排查首先确认系统是否有可用的摄像头驱动。然后检查代码中FilterInfoCollection的实例化是否正确。解决确保引用了正确的AForge.Video.DirectShow包。有时需要以管理员权限运行Visual Studio或编译后的程序才能正确访问某些DirectShow过滤器。另外某些杀毒软件或防火墙可能会拦截对摄像头的访问。问题3录制文件播放时音画不同步。排查这是时间戳PTS计算错误导致的。检查屏幕帧和摄像头帧的捕获时间戳是否准确应使用高精度计时器如Stopwatch或DateTime.UtcNow.Ticks。检查音频采样点的PTS计算是否正确音频PTS 累计采样数 / 采样率。解决确保使用统一的时钟基准。通常以音频时钟为主时钟视频PTS向音频对齐。在FFmpeg编码器中设置AVStream的time_base时间基要合理通常视频是1/framerate音频是1/sample_rate。写入AVPacket时其pts和dts必须根据这个time_base进行换算。问题4录制过程中内存持续增长最终程序崩溃。排查这是典型的内存泄漏。使用内存分析工具如Visual Studio的诊断工具或JetBrains dotMemory检查。常见泄漏点未释放的FFmpeg资源每个AVFrame,AVPacket,AVFilterGraph等在创建后最终必须调用对应的av_xxx_free()或av_free()函数。FFmpeg.AutoGen的封装对象通常实现了IDisposable务必使用using或在finally块中Dispose。事件未取消订阅摄像头NewFrame事件、或其他定时器事件在窗口关闭或停止录制时必须取消订阅。未释放的COM对象所有SharpDX对象Texture2D,Resource等都必须Dispose。队列阻塞如果生产者捕获线程速度大于消费者编码线程未处理的帧会堆积在内存队列中。需要实现一个带最大长度的阻塞队列并在队列满时丢弃最旧的帧。问题5尝试使用某些高级摄像头属性如曝光、对焦时失败。排查不是所有摄像头驱动都支持通过DirectShow接口控制所有属性。你需要先查询属性是否可用。解决通过VideoCaptureDevice的GetCameraPropertyRange方法获取某个属性如CameraControlProperty.Exposure的支持范围、默认值和步进。只有在IsPropertySupported返回true时才尝试去设置它。设置时值必须在最小值和最大值之间。开发这样一个综合性项目就像在搭一个精密的多流水线工厂。每个环节——捕获、转换、编码、写入——都必须稳定高效并且协同工作。调试过程往往是痛苦的你可能需要单独测试每一个模块例如先只录屏幕再只录摄像头最后混合用日志记录下每一帧的时间戳和大小逐步定位问题所在。但当看到最终生成的视频文件完美同步地包含了屏幕操作和你的摄像头画面时那种成就感是无与伦比的。这个项目源码的价值不仅在于它实现了功能更在于它完整展示了一个真实桌面应用从需求分析、技术选型、模块设计到问题排查的全过程这才是最值得学习和借鉴的地方。本文还有配套的精品资源点击获取