在.NET环境下,开发者经常需要将音视频数据捕获并保存为AVI文件。本文将介绍一个功能强大的类库,它提供了丰富的特性来实现这一需求。
该类库支持以下功能:
以下是如何使用这个类库的简单示例:
首先,确保项目中引用了DirectX.Capture.dll。
using System;
using DirectX.Capture;
// 使用系统上第一个可用的视频和音频设备进行捕获
Capture capture = new Capture(Filters.VideoInputDevices[0], Filters.AudioInputDevices[0]);
// 开始捕获
capture.Start();
// 停止捕获
capture.Stop();
这个示例将使用系统上第一个安装的视频和音频设备进行音视频捕获。如果只需要捕获视频,可以将第二个参数传递为null。
以下示例展示了如何更改视频和音频设置。例如,可以通过Capture.FrameRate和Capture.AudioSampleSize等属性来程序化调整捕获。使用Capture.VideoCaps和Capture.AudioCaps来确定这些属性的有效值。
Capture capture = new Capture(Filters.VideoInputDevices[0], Filters.AudioInputDevices[1]);
capture.VideoCompressor = Filters.VideoCompressors[0];
capture.AudioCompressor = Filters.AudioCompressors[0];
capture.FrameRate = 29.997; // NTSC
capture.FrameSize = new Size(640, 480); // 640x480
capture.AudioSamplingRate = 44100; // 44.1 kHz
capture.AudioSampleSize = 16; // 16-bit
capture.AudioChannels = 1; // 单声道
capture.Filename = @"C:\MyVideo.avi";
capture.Start();
// ...
capture.Stop();
上述示例还展示了如何使用视频和音频压缩器。在大多数情况下,会希望使用压缩器。未压缩的视频每分钟可以轻松消耗超过1GB的磁盘空间。尽可能早地设置Capture.VideoCompressor和Capture.AudioCompressor属性。更改它们需要重建内部过滤器图,这通常会导致大多数其他属性被重置为默认值。
该项目使用100% DirectShow来捕获视频。一旦开始捕获,DirectShow会生成另一个线程,并自行处理检索/移动所有视频和音频数据。这意味着应该能够以与用C语言编写的应用程序相同的速度和质量进行捕获。
DirectShow是作为一组COM组件实现的,使用.NET互操作来访问它们。NETMaster在DShowNET项目中进行了先驱工作。这个Capture库使用DShowNET作为互操作层,只进行了少量扩展。这就是前面提到的DShowNET.dll。
所有这些的顶部是Capture类库。任何DirectShow应用程序的核心都是过滤器图和过滤器图管理器。有关过滤器图及其组件的概述,请参见MSDN上的《过滤器图及其组件》。
该库始终尝试尽可能少地工作。问题是:DirectShow非常灵活,但对驱动程序开发人员几乎没有严格的标准,而且测试的硬件有限。因此,该类尝试避免执行可能不必要的任何工作,希望在此过程中避免潜在的不兼容性。
一个例子是视频预览。可以使用以下方式开始和停止预览:
// 开始预览
capture.PreviewWindow = myPanelControl;
// 停止预览
capture.PreviewWindow = null;
希望这很容易使用。在内部,DirectShow做了很多工作:为WDM设备添加所需的上游过滤器,搜索预览引脚,使用Overlay Manager进行视频端口(硬件覆盖),当没有单独的预览引脚可用时插入SmartTee过滤器等。而不是在类创建时立即渲染预览流,该类等到PreviewWindow属性被设置。
对于不需要预览的开发人员,这些工作永远不会完成。这意味着应用程序更有可能在更广泛的硬件上工作。对于需要预览的开发人员,这使得定位问题的原因并优雅地修复或处理它变得更加容易。
Capture类上的许多属性都是直接从底层DirectShow COM组件中检索的。如果需要在代码块中多次引用属性,请复制该值并使用副本。
// 每次迭代都从DirectShow检索AudioSampleSize
for (int c = 0; c < 32; c++) {
if (c == capture.AudioSampleSize)
MessageBox.Show("Found!");
}
// 更快的解决方案
int x = capture.AudioSampleSize;
for (int c = 0; c < 32; c++) {
if (c == x)
MessageBox.Show("Found!");
}
为什么类不简单地在内部缓存该值?不知道过滤器(设备驱动程序)何时会更改此值,所以必须每次都检索该值。这意味着将始终获得属性的真实值。
DirectShow互操作层由NETMaster在DShowNET项目中开发。MSDN风格的文档是使用nDoc从源代码生成的。
已使用Asus v7700(NVidia GeForce2,参考驱动程序)和板载声卡进行了测试。不能保证其他硬件会工作。然而,期望大多数视频捕获卡和声卡会工作。可能会遇到电视调谐器卡和DV设备(火线摄像机)的问题,尽管它们应该是可以解决的。
尝试DirectX SDK中的AMCap示例(DX9\Samples\C++\DirectShow\Bin\AMCap.exe)或Virtual VCR,一个免费的DirectShow捕获应用程序。
这个类库使用COM互操作来访问DirectShow的全部功能,所以如果另一个应用程序可以成功使用硬件设备,那么应该可以修改这个类库来使用该设备。请在下面的论坛中发布经验,无论是好是坏。
以下增强已发布到讨论板: