Camera Vision - C# 上的视频监控






4.92/5 (323投票s)
一个 C# 视频监控应用程序,允许同时监控多个 IP 摄像头。
引言
纵观当今视频监控系统的发展趋势,可以轻松地注意到基于 IP 的解决方案正迅速普及。有相当多的制造商提供各种 IP 视频摄像头和视频服务器,旨在服务于专业的 IP 视频监控系统。更重要的是,许多公司提供解决方案,旨在将当前的 CCTV 视频监控系统转换为基于 IP 的系统,保留当前设备和基础设施。除了这些不仅提供硬件解决方案,还提供软件的公司之外,还有许多其他公司主要专注于 IP 视频监控系统的软件部分,为小型或大型企业以及个人用户提供完整的解决方案。
在本文中,我想分享一些我与不同制造商的各种 IP 视频摄像头和视频服务器合作的经验。提供的信息主要与从您自己的应用程序访问这些摄像头有关,该应用程序可能是满足您个人需求的简单应用程序,也可能是一些更复杂的东西,甚至接近某种视频监控应用程序。
作为本文的演示应用程序,我提供了一个 C# 应用程序,该应用程序允许单摄像头查看以及同时多摄像头查看。该应用程序不仅允许同时查看来自单个视频服务器的多个摄像头,还允许同时查看来自不同制造商的许多不同摄像头。该应用程序支持的视频源范围是:
- 持续更新的 JPEG 源;
- MJPEG(运动 JPEG)流;
- 一些 Axis 摄像头和视频服务器(205、206、2100、2110、2120、2130R、2400、2401、2460);
- D-Link 摄像头(仅支持 JPEG);
- 松下摄像头;
- PiXORD 摄像头;
- StarDot 摄像头(NetCam、Express 6);
- 支持 DirectShow 接口的本地设备;
- MMS(Microsoft Media Services)流。
什么是 IP 摄像头?
IP 摄像头的主要区别和优势在于它们以数字形式输出,并且可以直接插入以太网交换机并通过 IP 网络访问。为此,IP 摄像头不仅包含摄像头本身,还包含一个小型板载计算机,通常运行嵌入式 Linux。这台计算机的目的是
- 将模拟图像转换为压缩的数字图像(某些摄像头/服务器除了主 CPU 外还有一个额外的压缩微处理器);
- 通过 IP 网络访问图像(通常这些摄像头运行一个 Web 服务器,该服务器不仅提供访问数字图像的能力,还通过 HTTP 协议提供摄像头配置信息)。
视频服务器是更复杂的设备,通常不带摄像头。取而代之的是,它们配有几个视频输入连接器(通常为 1 到 6 个),用户可以将任何想要的模拟摄像头插入这些连接器。与 IP 摄像头一样,视频服务器也将模拟摄像头的图像转换为数字形式,并通过 IP 网络提供访问,但它们还为视频档案创建提供了额外的选项(为此,视频服务器配备了硬盘驱动器)。
IP 摄像头和服务器可以通过 IP 网络访问这一事实是一个巨大的优势。它不仅允许在这些摄像头的实际位置进行监控,在那里您配备了专门的监控系统,还允许使用特殊的视频监控应用程序(如 Web 浏览器)从世界上的任何其他 IP 设备进行监控(参见下图)。并且可以从普通工作站、PDA 和手机进行操作。基于 IP 的视频解决方案的应用范围远远超出了仅进行监控和存储视频档案。这些摄像头/服务器的数字输出使它们能够轻松地与数百个应用程序集成,例如:
- 运动检测/跟踪(在整个视频帧中,或在指定的感兴趣区域);
- 交通控制和车牌识别;
- 人员跟踪及身份识别能力;
- 等等。
视频格式
几乎所有 IP 视频摄像头/服务器都支持的最简单的视频格式,甚至不能称为视频格式。该格式只是普通的 JPEG。大多数摄像头通过访问特殊 URL(应由摄像头制造商记录)来检索单个图像。例如,以下 URL 允许从 Axis 摄像头检索图像:http://webcam.mmhk.cz/axis-cgi/jpg/image.cgi。
这种方法有优缺点。缺点是每次需要检索新图像时都需要向摄像头的 Web 服务器发送新的 HTTP 请求,这会因为发送/接收的额外数据(HTTP 头)而导致一些速度损失。优点是监控应用程序可以轻松地自己控制最大帧率 – 它可以以任何任意速度(每分钟一次或每秒 15 次,如果网络和摄像头速度允许)访问 URL 以获取下一帧。
第二种流行格式是 MJPEG(运动 JPEG)。这种格式不仅允许下载单个 JPEG 图像,还可以下载 JPEG 流。与普通 JPEG 格式一样,客户端应用程序向特殊的摄像头 URL 发送 HTTP 请求,例如:http://146.176.65.10/axis-cgi/mjpg/video.cgi。但摄像头对此请求的回复不是单个 JPEG,而是由 HTTP 头中的一个特殊分隔符分隔的 JPEG 流。当客户端应用程序不再需要接收视频数据时,它会关闭与摄像头的连接。
MJPEG 方法似乎好得多,因为它有一个明显的优势——它只需要发送一次 HTTP 请求,然后就可以连续接收摄像头的 JPEG。但在这种方法中,您无法轻松控制帧率。访问这样的 MJPEG URL,您的摄像头将以某种预定义的帧率提供给您。如果您想更改它,您需要向 URL 添加一些额外的参数。这听起来问题不大,但实际上,它可能会导致一些问题。我将尝试描述最常见的一个。假设您请求(或默认设置)每秒 15 帧来自某个摄像头。但是,碰巧的是,在您和您的摄像头网络之间,网络速度下降了,您每秒只能收到 5 帧。假设您的摄像头有 30 帧的缓冲区,例如。所以,您的摄像头在 2 秒内生成了 30 帧,而您只在 6 秒内消耗了它们。这意味着您将看到最后一帧有 4 秒的延迟——在大多数情况下这太晚了。当然,这只是一个示例,摄像头会不时刷新它们的缓冲区并做一些其他事情来避免这种延迟。但是,这里有一个我亲眼所见的真实例子。有一次,一个人走进一个由某个摄像头监控的房间,在那里待了一小段时间,然后他去了另一个房间,并在摄像头监控应用程序(摄像头制造商提供的应用程序)中看到了自己在那个前房间里走动。
许多不同制造商的许多摄像头支持的格式远不止 JPEG 或 MJPEJ。有些摄像头支持 MPEG-2,有些支持 MPEG-4。此外,一些摄像头不仅支持视频,还支持声音传输,甚至支持双向通信。
在本文的后续部分,我将稍微详细地介绍如何访问一些摄像头——访问单个 JPEG 帧和 MJPEG 流(本文不涵盖 MPEG 格式)。大多数摄像头制造商在其网站上提供 API 和 SDK,因此可以更详细地了解这些信息。
访问 JPEG 和 MJPEG
显示来自任何 JPEG 源(摄像头)的数据非常简单——您只需要不断地向源发送 HTTP 请求,下载响应数据,并从中提取位图。以下是快速检索 IP 摄像头单个 JPEG 帧的示例。
string sourceURL = "http://webcam.mmhk.cz/axis-cgi/jpg/image.cgi";
byte[] buffer = new byte[100000];
int read, total = 0;
// create HTTP request
HttpWebRequest req = (HttpWebRequest) WebRequest.Create( sourceURL );
// get response
WebResponse resp = req.GetResponse( );
// get response stream
Stream stream = resp.GetResponseStream( );
// read data from stream
while ( ( read = stream.Read( buffer, total, 1000 ) ) != 0 )
{
total += read;
}
// get bitmap
Bitmap bmp = (Bitmap) Bitmap.FromStream(
new MemoryStream( buffer, 0, total ) );
但是,请记住,大多数摄像头都不是免费开放访问的,就像上面的示例一样。很可能,您希望通过密码来保护您的摄像头,而密码应该以某种方式指定。
// create HTTP request
HttpWebRequest req =
(HttpWebRequest) WebRequest.Create( sourceURL );
// set login and password
req.Credentials = new NetworkCredential( login, password );
...
访问 MJPEG 源要复杂得多。首先,让我们看一下响应内容类型。它应该看起来像这样:
multipart/x-mixed-replace; boundary=--myboundary
可能不会完全一样,但它的类型肯定是 multipart/x-mixed-replace
,后面跟着一个特定的边界。在这种情况下,边界值是 "--myboundary"
。现在,让我们看一下实际的流数据:
--myboundary
Content-Type: image/jpeg
... image binary data ...
--myboundary
Content-Type: image/jpeg
... image binary data ...
--myboundary
Content-Type: image/jpeg
...
将所有这些汇总起来,访问 MJPEG 源的算法就变得清晰了:
- 解析响应内容类型以提取实际的边界值;
- 读取流的初始部分,搜索第一个边界;
- 读取二进制数据直到下一个边界;
- 从读取的缓冲区中提取图像;
- 处理图像(显示,做任何其他事情);
- 在循环中继续步骤 3-5。
实际上,访问 MJPEG 源的想法并不像我之前说的那样复杂,但可以肯定的是,它的实现不会像 JPEG 源那样简单。
Axis 摄像头和视频服务器
Axis 摄像头和视频服务器似乎是我用过的最好的 IP 视频摄像头。从用户的角度来看,这些摄像头提供良好的视频质量和帧率,并且易于安装和配置。从程序员的角度来看,这些设备甚至更好——该公司提供了我所见过的最好的 IP 摄像头开发人员文档。该公司提供了关于如何通过 HTTP 访问这些摄像头的完整文档,还提供了 SDK [^]。
以下是访问 Axis IP 摄像头/服务器的 JPEG 和 MJPEG 流的 URL 格式:
JPEG:
http://<servername>/axis-cgi/jpg/image.cgi
MJPEG:
http://<servername>/axis-cgi/mjpg/video.cgi
这两个 URL 都可以接受一些参数,这些参数可能会影响结果。最流行的参数是分辨率(指定视频输出的大小)、摄像头(在视频服务器的情况下指定摄像头的编号)以及所需的帧率(仅适用于 MJPEG 源)。
Samples:
http://<servername>/axis-cgi/jpg/image.cgi?resolution=320x240
http://<servername>/axis-cgi/mjpg/video.cgi?camera=2
http://<servername>/axis-cgi/mjpg/video.cgi?camera=2&des_fps=5
要获取完整的 HTTP API 和所有支持的参数列表,请参阅 Axis 支持网站。
StarDot 摄像头和视频服务器
StarDot 的 IP 摄像头和视频服务器产品线似乎并不多,而且在过去两年里也没有变化。他们现在只有一个型号的 IP 摄像头和一个型号的视频服务器。对我来说,他们唯一的优势是——他们的视频服务器支持多达 6 个模拟摄像头。但其他一切都让他们无法与 Axis 等公司竞争。例如,他们的 IP 摄像头的帧率确实很低(对于安全来说不可接受),而且这些摄像头不支持 MJPEG。开发人员的信息量似乎也很少。
访问其产品的 URL 格式非常简单:
StarDot NetCam:
http://<servername>/netcam.jpg
StarDot Express 6 (video server)
http://<servername>/jpeg.cgi?<cameranumber>
http://<servername>/jpeg.cgi?3
PiXORD 摄像头
PiXORD 的产品线主要由不同型号的 IP 摄像头组成,这些摄像头似乎是相当不错的摄像头,提供良好的质量和帧率,并支持 MJPEG 流。该公司提供其产品的 SDK,但只有在注册程序之后才能访问。
以下是访问其 IP 摄像头的 URL 格式:
JPEG:
http://<servername>/images<channel><resolution>
http://<servername>/images1sif
MJPEG:
http://<servername>/getimage?camera=<channel>[&fmt=<resolution>][&delay=<delay>]
http://<servername>/getimage?camera=1&fmt=sif&delay=10
松下摄像头
我并没有与松下摄像头有过很多合作,只是在网上找到了一些摄像头,您也可以在本文提供的示例应用程序中进行浏览。
访问松下摄像头的 URL 格式:
JPEG:
http://<servername>/SnapshotJPEG[?Resolution=<resolution>][&Quality=<quality>]
http://<servername>/SnapshotJPEG?Resolution=320x240&Quality=Standard
MJPEG:
http://<servername>/nphMotionJpeg[?Resolution=<resolution>][&Quality=<quality>]
http://<servername>/nphMotionJpeg?Resolution=320x240&Quality=Standard
D-Link 摄像头
D-Link 拥有广泛的 IP 视频摄像头产品线,并且是首批在其摄像头中使用 MPEG-4 的公司之一。实际上,这些摄像头以 MPEG 视频作为其主要目标——它们不支持任何其他功能,如 MJPEG。他们的大多数摄像头还支持音频,并且某些型号甚至支持双向音频。从用户的角度来看,安装和配置这些摄像头相当简单,它们支持许多不同的设置。从开发者的角度来看,这些摄像头没那么容易。该公司不想分享太多开发信息,而且很难在他们的网站上找到任何开发人员信息。这一事实使得如果您想开发自己的监控软件而不是使用他们自己(有 bug 的)软件,这些摄像头就不那么有吸引力了。顺便说一下,我之前告诉过的那个家伙在另一个房间里看到自己在走动的故事,就是关于一个 D-Link 摄像头的。
目前,我只知道一种访问 D-Link 摄像头(JPEG 格式)的方法:
http://<servername>/cgi-bin/video.jpg
其他一些视频源
可以使用除 HTTP 以外的其他方法访问许多其他视频源。例如,您可以轻松访问通过 USB 端口连接到 PC 的本地网络摄像头等视频源,或者可以通过 MMS(Microsoft Media Services)访问远程视频流。访问这两种视频源最常见的方法之一是使用 DirectShow。示例应用程序演示了该技术,您可以使用 CodeProject 上关于该主题的几篇其他文章进行更深入的研究。
应用程序代码
应用程序的主要目标是使其灵活且可扩展。应用程序本身可以与任何视频源通信——可能是 IP 视频摄像头或服务器,可能是连接到 USB 的本地摄像头,可能是远程服务器的 MMS 流,或者可能是任何其他视频源。更重要的是,应用程序可以同时处理所有这些视频源,并将它们全部显示在单个屏幕上。
应用程序的另一个主要特点是它可以即时轻松扩展。应用程序本身模块对任何视频源及其配置方式一无所知;它只知道如何显示它们。与特定视频源通信的所有逻辑都隐藏在单独的模块中,应用程序与它们没有紧密耦合。如果您有新的视频源并希望应用程序与之配合使用,则无需更改应用程序本身的任何一行代码。您只需要创建一个负责与自定义视频源通信的新模块,并将该模块放在应用程序的文件夹中。
实现该想法的关键方法是创建一个描述所有视频源通用功能的接口。该接口是 IVideoSource
。然后,创建了一组类来实现上述接口,并封装了与特定视频源通信和从中提取图像数据的例程。每个此类类完全负责提供应用程序显示图像所需的所有工作。这些类的代码不包含在应用程序代码中,而是包含在单独的程序集中,这些程序集代表了可以轻松添加到应用程序以扩展其功能的应用程序模块。
每个视频源模块可能包含任意数量的视频提供程序——提供视频源访问的类。大多数此类模块仅包含一个视频提供程序,但有些模块有多个——可能倾向于以某种方式对视频提供程序进行分组(例如,访问同一制造商摄像头/服务器的所有视频提供程序进入同一个模块)。
所有这些视频提供程序都可以用作访问应用程序中不同视频源的完整类。但是,要使应用程序可扩展和可配置,还需要实现两个方面。首先,我们所有的视频提供程序都应该是自描述和自配置的。为此,添加了另外两个接口:IVideoSourceDescription
和 IVideoSourcePage
。每个实现 IVideoSourceDescription
接口的类都提供提供程序的名称和描述,这允许保存和加载其配置以及创建配置的视频提供程序。实现 IVideoSourcePage
接口的类代表视频提供程序配置的属性页。这些附加类也进入视频提供程序的模块。将所有这些结合起来,就清楚了,一个包含单个视频提供程序的最简单模块应该包含三个类:提供程序描述、提供程序配置页以及视频提供程序本身。
最后一步就是在应用程序端实现——应用程序应该找到所有模块并收集那里所有关于视频源提供程序的信息。这实际上可以通过反射非常容易地完成。首先,应用程序搜索应用程序文件夹中的所有 DLL 文件。然后,它尝试将每个文件加载为程序集,并枚举程序集中的所有类型,搜索实现 IVideoSourceDescription
接口的类型。一旦找到这样的类型,它就会被实例化并要求提供视频提供程序的名称、描述和其他信息。这个模块的调查过程只在应用程序启动时进行一次,但应用程序可以很容易地修改为按用户请求调用该过程(如果用户添加了一个新的视频提供程序模块,但不想重新启动应用程序,这可能很有用)。
一些水下石头(潜在问题)
有一个 .NET 1.0(和 1.1)框架的一个已知 bug,实际上它不是 bug,而是一个特性。但是这个特性给 MJPEG 模式下与某些摄像头的通信带来了很大的问题。问题在于,某些 MJPEG 视频源不完全符合 HTTP 标准 100%。或者换句话说——微软过于挑剔,并在其框架的第一版中非常严格地遵守 HTTP 标准。一些摄像头的 HTTP 头中缺少一些非常小的东西,而 .NET 会立即拒绝与它们工作,并生成一个 WebException
,其描述如下:
The underlying connection was closed:
The server committed an HTTP protocol violation.
幸运的是,这是 .NET 的一个已知特性,并且可以修复。首先,您需要至少获取框架的 1.1 版本并为其安装第一个服务包。然后,您需要为您的应用程序创建一个应用程序配置文件,并将其放在应用程序文件夹中。以下是使 MJPEG 源正常工作的文件的最小内容:
<configuration>
<startup>
<supportedRuntime version="v1.1.4322" />
</startup>
<system.net>
<settings>
<httpWebRequest useUnsafeHeaderParsing="true" />
</settings>
</system.net>
</configuration>
.NET 的 HttpWebRequest
类具有一个称为连接组的特性。默认情况下,所有 HTTP 请求都在同一个连接组中创建,但每个连接组都有同时打开的连接限制。因此,这就产生了一个问题,即您无法同时监控许多摄像头。好消息是,这个问题也可以轻松解决——HttpWebRequest
类有一个名为 ConnectionGroupName
的属性,因此您可以自己管理连接分组。
结论
附带的应用程序演示了文章中描述的所有技术,并允许监控来自不同制造商的许多不同摄像头。该应用程序允许您一次在一个屏幕上监控单个摄像头或多个摄像头(支持全屏模式)。请不要将该应用程序视为一个完整的视频监控应用程序,而将其视为一个演示、一个概念验证、您自己软件的起点。但是,该应用程序可能出于许多个人原因而有用。
您可以在 CodeProject 上找到另一个有趣的应用程序,它也处理视频摄像头,并且基于我在文章中描述的技术:CodeProject。
演示应用程序包括来自世界各地的许多免费摄像头:拉斯维加斯、斯图加特机场等等。您可以轻松地在互联网上找到更多免费访问的摄像头,将它们添加到应用程序中,然后享受监控它们的乐趣。