如何使用 C#.NET 为 ONVIF IP 摄像机创建视频录制应用程序(NVR/DVR 软件)






4.74/5 (24投票s)
我将展示如何使用 C# 将音频和视频流录制成 MPEG-4 文件格式,以便保存和存储视频监控区域内发生的一切。
目录
IP 摄像机视频录制简介
随着 IP 摄像机监控系统的普及,与安防系统(及其功能)相关的新兴和更高层次的需求也随之出现。这催生了摄像机视频录制解决方案,它允许您录制音频和视频流,以便保存和存储视频监控区域内发生的所有事件。
当您想录制视频消息发送给同事、朋友或家人,或者想将视频文件上传并广播到互联网时,视频录制会很有用。在犯罪案件中需要证据时,它尤其有用。
在我之前的文章(https://codeproject.org.cn/Articles/825074/How-to-broadcast-live-IP-camera-stream-as-Flash-vi)中,我展示了如何将实时 IP 摄像机流集成到您的网站中,并承诺将带来另一个 IP 摄像机开发。因此,在这篇文章中,我打算解释如何使用 C#.NET 创建一个视频录像机,以便录制和保存您的摄像机流。
关于 NVR 和 DVR
为了更好地理解,我们仔细研究一下通常部署在 IP 视频监控系统中的网络视频录像机(NVR)和数字视频录像机(DVR)。NVR 是一种以数字格式录制视频的软件。尽管它不包含任何专用视频采集硬件,但该软件通常运行在专用设备上(见下图)。
NVR 与 DVR 不同,因为它们的输入来自网络,而不是直接连接到视频采集卡或调谐器。DVR 上的视频在 DVR 上进行编码和处理,而 NVR 上的视频在摄像机上进行编码和处理,然后流式传输到 NVR 进行存储或远程查看。(也有混合 NVR/DVR 安全系统,它们包含两者的功能。)
在本技巧中,我将向您展示如何实现一个视频录像机应用程序,该应用程序可以在您的 PC 上使用,并且能够在不使用任何专用设备的情况下录制摄像机视频(参见上图)。我的解决方案基于 ONVIF 基于 IP 的安全标准化。(在这篇文章中,我不想再次深入探讨 ONVIF 的细节。您可以在我之前的文章中阅读更多关于 ONVIF 技术的信息
入门
简要理论概述之后,是时候开始您的 IP 摄像机录像机项目了。对于此开发,您将需要 (1) ONVIF SDK 和 (2) C# 编程的 IDE。让我们更仔细地看看这两个先决条件。
1. ONVIF SDK 安装
通过使用我之前的文章,您已经了解到,如果要构建任何 IP 摄像机软件,IP 摄像机 SDK 是必不可少的。为此,我这次也使用了 Ozeki Camera SDK 的组件。您可以从其官方网站下载该 SDK。
2. 在 Visual Studio 中创建 Windows 窗体应用程序
对于这个项目,与 C# 编程通常情况一样,我使用 Microsoft Visual Studio 作为 IDE。如果您需要此软件,可以从微软官方网站获取。(虽然 .NET Framework 会随 Visual Studio 自动安装,但请确保您的 PC 上已安装 .NET Framework 4.0。)
要实现此视频录像机,您需要创建一个 Windows 窗体应用程序。首先,您需要单击“新建项目”图标来创建一个新的 Visual Studio 项目。现在,您需要将 DLL 文件(由 Camera SDK 提供)作为引用添加到您的项目中。最后,只剩下一个设置需要完成:确保您的“目标框架”是“.NET Framework 4.0”。
如果您已完成所有先前描述的初始步骤,那么让我们开始构建这个摄像机项目。
后端开发
让我们通过查看我在本项目中使用的类来开始开发。下面我将介绍以下三个类
- Program.cs
- Form1.cs
- Form1.Designer.cs
首先,看看 Program.cs 类。它包含 Main()
方法,该方法是应用程序的入口点。如下图所示,将在此类中调用 Form 的运行
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace Network_Video_Recorder01
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
好的,现在让我们转到 Form1.cs 类,它包含视频录像机应用程序的所有功能,可用于实现所需的功能。第一步,您需要连接到您的摄像头才能查看其图像。您可以在下面看到可用于在您的应用程序和摄像头之间建立连接的方法
_camera = IPCameraFactory.GetCamera("192.168.115.198:8080",
"admin", "admin") // It initializes the camera that has been declared
//as a private member of the class. Three arguments are used:
//the IP address of the camera, username and password.
_connector.Connect(_camera.VideoChannel, _imageProvider) // Using this method,
//you can create connection between the camera image and the
//image provider object (that is used to display the camera image on the GUI).
_camera.Start() // It can be used to receive the camera image.
_videoViewerWF1.Start(): // It can be used to display the camera image on the GUI.
成功连接后,您还需要一些额外的语句和方法才能录制音频和视频流。您可以在下面看到它们
Button_CaptureVideoStart_Click() // It initializes the path variable
//that is used to determine the destination folder of the captured video.
//Then calls the StartVideoCapture() method with this parameter.
StartVideoCapture() // It creates a string that contains the actual date and time
//then uses its parameter to complete the path. Finally, initializes the
//Mpeg4Recorder object, subscribes to its events and establishes the
//connection between the media channels and the Mpeg4Recorder object.
Mpeg4Recorder_MultiplexFinished() // It terminates all connections and closes the recorder object.
StopVideoCapture() // It calls a multiplexing method and terminates the connections.
Button_SaveTo_Click() // It makes the user be able to choose the path and saves the
//result into the TextBox GUI element that can be seen on the picture below.
以下代码片段展示了完整的 Form1.cs 类,其中包含摄像机视频录制所需的所有应用程序功能
using System;
using System.Drawing;
using System.Windows.Forms;
using Ozeki.Media.IPCamera;
using Ozeki.Media.MediaHandlers.Video;
using Ozeki.Media.Video.Controls;
using Ozeki.Media.MediaHandlers;
namespace Network_Video_Recorder01
{
public partial class Form1 : Form
{
private IIPCamera _camera;
private DrawingImageProvider _imageProvider;
private MediaConnector _connector;
private VideoViewerWF _videoViewerWf;
private MPEG4Recorder _mpeg4Recorder;
public Form1()
{
InitializeComponent();
_imageProvider = new DrawingImageProvider();
_connector = new MediaConnector();
_videoViewerWf = new VideoViewerWF();
SetVideoViewer();
}
private void SetVideoViewer()
{
CameraBox.Controls.Add(_videoViewerWf);
_videoViewerWf.Size = new Size(260, 180);
_videoViewerWf.BackColor = Color.Black;
_videoViewerWf.TabStop = false;
_videoViewerWf.Location = new Point(14, 19);
_videoViewerWf.Name = "_videoViewerWf";
}
private void button_Connect_Click(object sender, EventArgs e)
{
_camera = IPCameraFactory.GetCamera("192.168.115.175:8080", "admin", "admin");
_connector.Connect(_camera.VideoChannel, _imageProvider);
_videoViewerWf.SetImageProvider(_imageProvider);
_videoViewerWf.Start();
_camera.Start();
}
private void Button_CaptureVideoStart_Click(object sender, EventArgs e)
{
var path = TextBox_SaveTo.Text;
if (!String.IsNullOrEmpty(path))
StartVideoCapture(path);
}
private void StartVideoCapture(string path)
{
var date = DateTime.Now.Year + "y-" + DateTime.Now.Month + "m-" + DateTime.Now.Day + "d-" +
DateTime.Now.Hour + "h-" + DateTime.Now.Minute + "m-" + DateTime.Now.Second + "s";
string currentpath;
if (String.IsNullOrEmpty(path))
currentpath = date + ".mp4";
else
currentpath = path + "\\" + date + ".mp4";
_mpeg4Recorder = new MPEG4Recorder(currentpath);
_mpeg4Recorder.MultiplexFinished += Mpeg4Recorder_MultiplexFinished;
_connector.Connect(_camera.AudioChannel, _mpeg4Recorder.AudioRecorder);
_connector.Connect(_camera.VideoChannel, _mpeg4Recorder.VideoRecorder);
}
private void Mpeg4Recorder_MultiplexFinished(object sender, Ozeki.VoIP.VoIPEventArgs<bool> e)
{
_connector.Disconnect(_camera.AudioChannel, _mpeg4Recorder.AudioRecorder);
_connector.Disconnect(_camera.VideoChannel, _mpeg4Recorder.VideoRecorder);
_mpeg4Recorder.Dispose();
}
private void Button_CaptureVideoStop_Click(object sender, EventArgs e)
{
StopVideoCapture();
}
private void StopVideoCapture()
{
_mpeg4Recorder.Multiplex();
_connector.Disconnect(_camera.AudioChannel, _mpeg4Recorder.AudioRecorder);
_connector.Disconnect(_camera.VideoChannel, _mpeg4Recorder.VideoRecorder);
}
private void Button_SaveTo_Click(object sender, EventArgs e)
{
var result = folderBrowserDialog1.ShowDialog();
if (result == DialogResult.OK)
TextBox_SaveTo.Text = folderBrowserDialog1.SelectedPath;
}
}
}
前端开发(GUI)
完成后端开发后,我们来看看图形用户界面。下图展示了我的 GUI——如果您已成功构建并运行此项目,您应该会看到类似的 Windows 窗体应用程序。要显示摄像机图像,请单击“连接”按钮。您在 Form1.cs 类中指定的摄像机图像将很快加载到“实时摄像机”部分。
以下代码片段展示了完整的 Form1.Designer.cs 类,其中包含用于开发图形用户界面的所有元素。上面看到的 GUI 就是通过使用此代码片段实现的
namespace Network_Video_Recorder01
{
partial class Form1
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.folderBrowserDialog1 = new System.Windows.Forms.FolderBrowserDialog();
this.GroupBox_Connect = new System.Windows.Forms.GroupBox();
this.button_Connect = new System.Windows.Forms.Button();
this.CameraBox = new System.Windows.Forms.GroupBox();
this.button_CaptureVideo1Stop = new System.Windows.Forms.Button();
this.TextBox_SaveTo = new System.Windows.Forms.TextBox();
this.button_SaveTo1 = new System.Windows.Forms.Button();
this.button_CaptureVideo1Start = new System.Windows.Forms.Button();
this.GroupBox_Capture = new System.Windows.Forms.GroupBox();
this.GroupBox_Connect.SuspendLayout();
this.GroupBox_Capture.SuspendLayout();
this.SuspendLayout();
//
// GroupBox_Connect
//
this.GroupBox_Connect.Controls.Add(this.button_Connect);
this.GroupBox_Connect.Location = new System.Drawing.Point(10, 10);
this.GroupBox_Connect.Name = "GroupBox_Connect";
this.GroupBox_Connect.Size = new System.Drawing.Size(110, 60);
this.GroupBox_Connect.TabIndex = 0;
this.GroupBox_Connect.TabStop = false;
this.GroupBox_Connect.Text = "Connect";
//
// button_Connect
//
this.button_Connect.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.button_Connect.ForeColor = System.Drawing.Color.Black;
this.button_Connect.Location = new System.Drawing.Point(10, 20);
this.button_Connect.Name = "button_Connect";
this.button_Connect.Size = new System.Drawing.Size(90, 25);
this.button_Connect.TabIndex = 6;
this.button_Connect.Text = "Connect";
this.button_Connect.UseVisualStyleBackColor = true;
this.button_Connect.Click += new System.EventHandler(this.button_Connect_Click);
//
// CameraBox
//
this.CameraBox.Location = new System.Drawing.Point(10, 90);
this.CameraBox.Name = "CameraBox";
this.CameraBox.Size = new System.Drawing.Size(290, 210);
this.CameraBox.TabIndex = 3;
this.CameraBox.TabStop = false;
this.CameraBox.Text = "Live camera ";
//
// button_CaptureVideo1Stop
//
this.button_CaptureVideo1Stop.Location = new System.Drawing.Point(190, 20);
this.button_CaptureVideo1Stop.Name = "button_CaptureVideo1Stop";
this.button_CaptureVideo1Stop.Size = new System.Drawing.Size(90, 25);
this.button_CaptureVideo1Stop.TabIndex = 36;
this.button_CaptureVideo1Stop.Text = "Stop capture";
this.button_CaptureVideo1Stop.UseVisualStyleBackColor = true;
this.button_CaptureVideo1Stop.Click += new System.EventHandler(this.Button_CaptureVideoStop_Click);
//
// TextBox_SaveTo
//
this.TextBox_SaveTo.Location = new System.Drawing.Point(140, 60);
this.TextBox_SaveTo.Name = "TextBox_SaveTo";
this.TextBox_SaveTo.Size = new System.Drawing.Size(140, 20);
this.TextBox_SaveTo.TabIndex = 35;
//
// button_SaveTo1
//
this.button_SaveTo1.Location = new System.Drawing.Point(10, 60);
this.button_SaveTo1.Name = "button_SaveTo1";
this.button_SaveTo1.Size = new System.Drawing.Size(90, 25);
this.button_SaveTo1.TabIndex = 34;
this.button_SaveTo1.Text = "Save to:";
this.button_SaveTo1.UseVisualStyleBackColor = true;
this.button_SaveTo1.Click += new System.EventHandler(this.Button_SaveTo_Click);
//
// button_CaptureVideo1Start
//
this.button_CaptureVideo1Start.Location = new System.Drawing.Point(10, 20);
this.button_CaptureVideo1Start.Name = "button_CaptureVideo1Start";
this.button_CaptureVideo1Start.Size = new System.Drawing.Size(90, 25);
this.button_CaptureVideo1Start.TabIndex = 33;
this.button_CaptureVideo1Start.Text = "Start capture";
this.button_CaptureVideo1Start.UseVisualStyleBackColor = true;
this.button_CaptureVideo1Start.Click += new System.EventHandler(this.Button_CaptureVideoStart_Click);
//
// GroupBox_Capture
//
this.GroupBox_Capture.Controls.Add(this.TextBox_SaveTo);
this.GroupBox_Capture.Controls.Add(this.button_CaptureVideo1Stop);
this.GroupBox_Capture.Controls.Add(this.button_CaptureVideo1Start);
this.GroupBox_Capture.Controls.Add(this.button_SaveTo1);
this.GroupBox_Capture.Location = new System.Drawing.Point(10, 300);
this.GroupBox_Capture.Name = "GroupBox_Capture";
this.GroupBox_Capture.Size = new System.Drawing.Size(290, 100);
this.GroupBox_Capture.TabIndex = 0;
this.GroupBox_Capture.TabStop = false;
this.GroupBox_Capture.Text = "Capture";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(314, 414);
this.Controls.Add(this.GroupBox_Capture);
this.Controls.Add(this.CameraBox);
this.Controls.Add(this.GroupBox_Connect);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.Name = "Form1";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Network Video Recorder";
this.GroupBox_Connect.ResumeLayout(false);
this.GroupBox_Capture.ResumeLayout(false);
this.GroupBox_Capture.PerformLayout();
this.ResumeLayout(false);
}
private System.Windows.Forms.GroupBox GroupBox_Connect;
private System.Windows.Forms.Button button_Connect;
private System.Windows.Forms.GroupBox CameraBox;
private System.Windows.Forms.Button button_CaptureVideo1Stop;
private System.Windows.Forms.TextBox TextBox_SaveTo;
private System.Windows.Forms.Button button_SaveTo1;
private System.Windows.Forms.Button button_CaptureVideo1Start;
private System.Windows.Forms.FolderBrowserDialog folderBrowserDialog1;
private System.Windows.Forms.GroupBox GroupBox_Capture;
}
}
您可以单击“开始捕获”按钮开始录制音频和视频摄像机流。如果想结束录制,请单击“停止捕获”按钮。为了保存您的录制内容,请定义一个您想保存和存储视频文件的目标文件夹,然后单击“保存到”按钮。如下图所示,我已指定我的录制内容将保存到桌面上的“Test_recording”文件夹中。打开此文件夹后,您还可以看到我录制的摄像机流以 .mp4 视频文件的形式存在。这样,当我不在时,我就可以查看我的桌子周围和我的电脑上发生了什么。
结论
在认识到 ONVIF 技术的重要性之后,在我关于如何将实时 IP 摄像机流集成到网站的第一篇文章中,我承诺将带来另一个 IP 摄像机解决方案。这一次,我提供了一个新指南,解释了如何使用 C#.NET 为您的摄像机创建视频录制应用程序(NVR/DVR 软件)。该软件允许您录制和保存摄像机的音频和视频流,因此可用于安全目的、捕捉和分享重要时刻,或者仅仅是为了娱乐。例如,通过实现电子邮件发送功能,您可以轻松实现录制内容的自动化分享。要使用我的源代码,需要以下先决条件:一个兼容 ONVIF 的摄像机 SDK(如 Ozeki Camera SDK)和一个用于 C# 编程的 IDE(如 Microsoft Visual Studio)。
参考文献
在本节中,我列出了我撰写此技巧所使用的所有网页资源。在下面,您还可以找到所需软件的直接下载链接
- http://en.wikipedia.org/wiki/Network_Video_Recorder
- https://codeproject.org.cn/Articles/825074/How-to-broadcast-live-IP-camera-stream-as-Flash-vi
- http://www.onvif.org/specs/core/ONVIF-Core-Spec-v210.pdf
- http://www.camera-sdk.com/p_13-download-onvif.html
- http://www.microsoft.com/en-US/download/details.aspx?id=17851