帮酷LOGO
  • 显示原文与译文双语对照的内容
文章标签:控制  摄像头  WEB  



330177/screen.jpg

介绍

在本文中,你将看到另一个网络摄像机控件的实现。 控件是一个简单且易于使用的控件: 无附加依赖关系和简单接口。

控件提供以下功能:

  • 获取系统上可用的网络摄像机设备的列表。
  • 显示来自网络摄像机设备的视频流。
  • 获取正在捕获的当前图像。

要求

  • 控件的WinForms版本是使用. NET 框架 2.0实现的。
  • 控件的WPF版本是使用. NET 框架 4客户端配置文件实现的。
  • 控件使用自 Windows XP SP2之后可用的VMR-9 呈现器筛选器。

控件同时支持x86和x64平台目标。

背景

有很多方法可以在 Windows 中捕获视频流。 没有提到所有这些,基本的是DirectShow框架和AVICap库。 我们将使用基于DirectShow的方法,因为它更加强大和灵活。

DirectShow框架使用图形。过滤器和引脚等概念操作。 过滤器形成一个捕获图,通过它媒体流流。 图中的过滤器使用大头针互相连接。 网络摄像机是从视频流开始的捕获过滤器。 控件窗口被传递给呈现器过滤器,该过滤器接收并显示视频流。 还有其他in-the-middle过滤器,例如颜色空间转换过滤器。 这就是捕获图。 有关更多信息,请参见 MSDN文档

.有关实现的详细信息

如果你对实现细节不感兴趣,那么你可以跳过 this 。

实现被划分为三层。

  • 底层是作为本地DLL模块实现的,它将我们的调用转发到DirectShow框架。
  • 为了便于分发,本机DLL模块作为资源嵌入到控件的集合中。 在运行时阶段,DLL模块将被提取到磁盘上的临时文件,并通过后期绑定技术使用。 释放该控件后,将删除临时文件。 换句话说,控件是作为单个文件分发的。 所有这些操作都由中间层实现。
  • 顶层实现控件类本身和用于标识网络摄像机设备的WebCameraId 类。

下面的图显示了实现的逻辑结构。

https://www.codeproject.com/KB/audio-video/330177/classdiagram.png

客户只应该使用顶层。

底层

底层实现以下实用程序来处理捕获图。

///<summary>/// Enumerates video input devices in a system.///</summary>///<paramname="callback">A callback method.</param>DSUTILS_API void__stdcall EnumVideoInputDevices(EnumVideoInputDevicesCallback callback);///<summary>/// Builds a video capture graph.///</summary>///<returns>If the function succeeds, the return value is zero.</returns>DSUTILS_API int__stdcall BuildCaptureGraph();///<summary>/// Adds a renderer filter to a video capture graph,/// which renders a video stream within a container window.///</summary>///<paramname="hWnd">A container window that video should be clipped to.</param>///<returns>If the function succeeds, the return value is zero.</returns>DSUTILS_API int__stdcall AddRenderFilter(HWND hWnd);///<summary>/// Adds a video stream source to a video capture graph.///</summary>///<paramname="devicePath">A device path of a video capture filter to add.</param>///<returns>If the function succeeds, the return value is zero.</returns>DSUTILS_API int__stdcall AddCaptureFilter(BSTR devicePath);///<summary>/// Removes a video stream source from a video capture graph.///</summary>///<returns>If the function succeeds, the return value is zero.</returns>DSUTILS_API int__stdcall ResetCaptureGraph();///<summary>/// Runs all the filters in a video capture graph. While the graph is running,/// data moves through the graph and is rendered. ///</summary>///<returns>If the function succeeds, the return value is zero.</returns>DSUTILS_API int__stdcall Start();///<summary>/// Stops all the filters in a video capture graph.///</summary>///<returns>If the function succeeds, the return value is zero.</returns>DSUTILS_API int__stdcall Stop();///<summary>/// Retrieves the current image being displayed by the renderer filter.///</summary>///<paramname="ppDib">Address of a pointer to a BYTE that will receive the DIB.</param>///<returns>If the function succeeds, the return value is zero.</returns>DSUTILS_API int__stdcall GetCurrentImage(BYTE **ppDib);///<summary>/// Retrieves the unstretched video size.///</summary>///<paramname="lpWidth">A pointer to a LONG that will receive the width.</param>///<paramname="lpHeight">A pointer to a LONG that will receive the height.</param>///<returns>If the function succeeds, the return value is zero.</returns>DSUTILS_API int__stdcall GetVideoSize(LONG *lpWidth, LONG *lpHeight);///<summary>/// Destroys a video capture graph.///</summary>DSUTILS_API void__stdcall DestroyCaptureGraph();

中间层

中间层在 DirectShowProxy 类中实现。

首先,我们应该从资源提取捕获图实用程序DLL模块,并将它的保存到临时文件中。

_dllFile = Path.GetTempFileName();using (FileStream stream = new FileStream(_dllFile, FileMode.Create, FileAccess.Write))
{
 using (BinaryWriter writer = new BinaryWriter(stream))
 {
 writer.Write(IsX86Platform?
 Resources.DirectShowFacade : Resources.DirectShowFacade64);
 }
}

然后我们将我们的DLL模块加载到调用进程的地址空间。

_hDll = LoadLibrary(_dllFile);if (_hDll == IntPtr.Zero)
{
 thrownew Win32Exception(Marshal.GetLastWin32Error());
}

并将DLL模块函数绑定到类实例方法。

privatedelegateInt32 BuildCaptureGraphDelegate();private BuildCaptureGraphDelegate _buildCaptureGraph;//.. .IntPtr pProcPtr = GetProcAddress(_hDll, "BuildCaptureGraph");
_buildCaptureGraph =
 (BuildCaptureGraphDelegate)Marshal.GetDelegateForFunctionPointer(pProcPtr, 
 typeof(BuildCaptureGraphDelegate));

释放控件时,我们卸载DLL模块并将它的删除。

publicvoid Dispose()
{
 if (_hDll!= IntPtr.Zero)
 {
 FreeLibrary(_hDll);
 _hDll = IntPtr.Zero;
 }
 if (File.Exists(_dllFile))
 {
 File.Delete(_dllFile);
 }
}

顶层

顶层是在 WebCameraControl 类中实现的,具有以下接口。

///<summary>/// Gets a list of available video capture devices.///</summary>///<exceptioncref="Win32Exception">Failed to load the DirectShow utilities dll.</exception>public IEnumerable<WebCameraId> GetVideoCaptureDevices();///<summary>/// Gets a value indicating whether the control is capturing a video stream.///</summary>publicBoolean IsCapturing { get; }///<summary>/// Starts a capture.///</summary>///<paramname="camera">The camera to capture from.</param>///<exceptioncref="ArgumentNullException">A null reference is passed as an argument.</exception>///<exceptioncref="Win32Exception">Failed to load the DirectShow utilities dll.</exception>///<exceptioncref="DirectShowException">Failed to run a video capture graph.</exception>publicvoid StartCapture(WebCameraId camera);///<summary>/// Retrieves the unstretched image being captured.///</summary>///<returns>The current image.</returns>///<exceptioncref="InvalidOperationException">The control is not capturing a video stream.</exception>///<exceptioncref="DirectShowException">Failed to get the current image.</exception>public Bitmap GetCurrentImage();///<summary>/// Gets the unstretched video size.///</summary>public Size VideoSize { get; }///<summary>/// Stops a capture.///</summary>///<exceptioncref="InvalidOperationException">The control is not capturing a video stream.</exception>///<exceptioncref="DirectShowException">Failed to stop a video capture graph.</exception>publicvoid StopCapture();

使用情况

打开软件包管理器控制台,并向你的项目添加一个nuget软件包:

Install-Package WebEye.Controls.WinForms.WebCameraControl

首先,我们需要使用右键单击 Visual Studio 设计器工具箱,然后将"选择项目。"菜单项添加到设计器工具箱。 然后,我们将控件放置在需要的位置和所需的大小上。 控件实例变量的默认名称将为 webCameraControl1

然后,在运行时阶段,我们需要获取系统上可用的网络摄像机列表。

List<WebCameraId> cameras = new List<WebCameraId>(webCameraControl1.GetVideoCaptureDevices());

下面的代码从列表中的第一个照相机开始捕获。

webCameraControl1.StartCapture(cameras[0]); 

请注意,必须创建控制窗口才能启动捕获,否则,由于视频流没有输出引脚。 通常错误是在 Form.Load 事件处理程序中启动捕获时,当尚未创建控件窗口时。

要获取正在捕获的图像,只需调用 GetCurrentImage() 方法。 图像的分辨率和质量取决于相机设备的特性。

Bitmap image = webCameraControl1.GetCurrentImage();

若要停止捕获,请使用 StopCapture() 方法。

webCameraControl1.StopCapture();

你总是可以使用下面的代码来询问捕获状态。

if (webCameraControl1.IsCapturing)
{
 webCameraControl1.StopCapture();
}

要从其他网络摄像机开始捕获,只需再次调用 StartCapture 方法。

webCameraControl1.StartCapture(cameras[1]);

若要报告错误,请使用异常,因此不要忘记将代码包装在 try/catch 块中。 那就是使用它。 要查看完整的示例,请检查演示应用程序源。

WPF版本

在WPF用户控件中,没有窗口句柄与它们关联,这是个问题,因为DirectShow框架需要窗口句柄来输出视频流。 为了解决这个问题,VideoWindow类已经被引入。

<UserControlx:Class="WebCamera.WebCameraControl"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"mc:Ignorable="d"d:DesignHeight="300"d:DesignWidth="300"xmlns:local="clr-namespace:WebCamera"><local:VideoWindowx:Name="_videoWindow"HorizontalAlignment="Stretch"VerticalAlignment="Stretch"/></UserControl>

要将控件的WPF版本添加到项目中,请使用以下nuget命令:

Install-Package WebEye.Controls.Wpf.WebCameraControl

GitHub

项目在以下页面上有一个GitHub存储库。

https://github.com/jacobbo/WebEye/tree/master/WebCameraControl

欢迎任何问题。评论和评论。

历史记录

  • February 29,2016 - 添加了一个nuget软件包。
  • April 10,2015 - 添加了x64平台支持。
  • October 24,2012 - 添加了一个GitHub知识库。
  • September 12 - 2012演示应用程序被更新为报告异常。
  • September 9,2012 - 控件的WPF版本和演示应用程序。
  • February 19,2012 - 更新的演示代码和文档。
  • February 15,2012 - 初始版本。


文章标签:WEB  控制  摄像头  

Copyright © 2011 HelpLib All rights reserved.    知识分享协议 京ICP备05059198号-3  |  如果智培  |  酷兔英语