C#控制网络摄像头摄像并保存:从设备调用到视频存储的完整实现

1次阅读
没有评论

共计 5233 个字符,预计需要花费 14 分钟才能阅读完成。

image.webp

技术背景

网络摄像头控制在现代软件开发中有广泛应用,比如视频会议、安防监控、远程医疗等场景。作为开发者,我们经常需要实现以下核心功能:

C# 控制网络摄像头摄像并保存:从设备调用到视频存储的完整实现

  • 枚举并连接可用摄像头设备
  • 实时预览视频流
  • 录制视频并保存到本地
  • 处理不同分辨率和帧率的适配

在这个过程中,我们会遇到几个典型的技术挑战:

  1. 设备兼容性问题:不同厂商的摄像头驱动和接口协议可能有差异
  2. 实时性要求:视频流处理需要高效的内存管理和线程控制
  3. 格式兼容性:录制的视频需要在各种播放器上正常打开
  4. 权限管理:特别是在 Windows 10/11 上需要处理摄像头隐私设置

技术选型

C# 生态中有几个主流方案可以实现摄像头控制:

1. AForge.NET 框架

优点:

  • 简单易用的高级 API
  • 内置图像处理功能
  • 良好的文档和社区支持

缺点:

  • 停止维护
  • 对新设备兼容性有限

2. DirectShow

优点:

  • Windows 原生多媒体框架
  • 广泛的硬件兼容性
  • 细粒度控制

缺点:

  • API 较复杂
  • 需要处理 COM 互操作

3. MediaCapture (UWP)

优点:

  • 现代 API 设计
  • 良好的权限管理
  • 支持最新硬件特性

缺点:

  • 仅限 UWP 应用
  • 功能有一定限制

对于桌面应用开发,我推荐 DirectShow 方案,因为它提供了最全面的控制能力,且不受应用类型限制。下面我们就以 DirectShow 为例,详细讲解实现过程。

核心实现

1. 准备工作

首先需要安装必要的 NuGet 包:

Install-Package DirectShowLib
Install-Package AForge.Video.DirectShow

2. 枚举摄像头设备

// 获取所有视频输入设备
var videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);

// 打印设备列表
foreach (FilterInfo device in videoDevices)
{Console.WriteLine($"设备名称: {device.Name}, 设备 Moniker: {device.MonikerString}");
}

3. 初始化视频捕获

// 创建视频源
var videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString);

// 设置视频分辨率
videoSource.VideoResolution = videoSource.VideoCapabilities
    .FirstOrDefault(cap => cap.FrameSize.Width == 1280 && cap.FrameSize.Height == 720);

// 注册新帧事件
videoSource.NewFrame += OnNewFrame;

// 开始捕获
videoSource.Start();

4. 实时预览处理

private void OnNewFrame(object sender, NewFrameEventArgs eventArgs)
{
    // 获取当前帧
    Bitmap frame = (Bitmap)eventArgs.Frame.Clone();

    // 在 UI 线程更新预览
    if (pictureBoxPreview.InvokeRequired)
    {pictureBoxPreview.Invoke(new Action(() => 
        {pictureBoxPreview.Image?.Dispose();
            pictureBoxPreview.Image = frame;
        }));
    }
    else
    {pictureBoxPreview.Image?.Dispose();
        pictureBoxPreview.Image = frame;
    }
}

5. 视频录制与保存

我们需要使用 AForge.Video.FFMPEG 库来保存视频:

// 创建视频写入器
var videoWriter = new VideoFileWriter();

// 开始录制
void StartRecording(string filePath)
{
    var frameSize = videoSource.VideoResolution.FrameSize;
    videoWriter.Open(filePath, frameSize.Width, frameSize.Height, 25, VideoCodec.MPEG4);
    isRecording = true;
}

// 在 NewFrame 事件中添加录制逻辑
if (isRecording)
{videoWriter.WriteVideoFrame(frame);
}

// 停止录制
void StopRecording()
{
    isRecording = false;
    videoWriter.Close();}

完整代码示例

下面是一个完整的 Windows Forms 应用示例:

using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using AForge.Video;
using AForge.Video.DirectShow;
using AForge.Video.FFMPEG;

public class CameraRecorderForm : Form
{
    private VideoCaptureDevice videoSource;
    private VideoFileWriter videoWriter;
    private bool isRecording = false;
    private PictureBox pictureBoxPreview = new PictureBox();
    private Button btnStartStop = new Button();

    public CameraRecorderForm()
    {
        // 初始化 UI
        this.Text = "摄像头录制工具";
        pictureBoxPreview.Dock = DockStyle.Fill;
        btnStartStop.Text = "开始录制";
        btnStartStop.Dock = DockStyle.Bottom;
        btnStartStop.Click += BtnStartStop_Click;

        this.Controls.Add(pictureBoxPreview);
        this.Controls.Add(btnStartStop);

        // 初始化摄像头
        var videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
        if (videoDevices.Count == 0)
        {MessageBox.Show("未检测到摄像头设备");
            return;
        }

        videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString);
        videoSource.VideoResolution = videoSource.VideoCapabilities
            .FirstOrDefault(cap => cap.FrameSize.Width == 1280 && cap.FrameSize.Height == 720);
        videoSource.NewFrame += OnNewFrame;
        videoSource.Start();}

    private void OnNewFrame(object sender, NewFrameEventArgs eventArgs)
    {using (var frame = (Bitmap)eventArgs.Frame.Clone())
        {
            // 更新预览
            if (pictureBoxPreview.InvokeRequired)
            {pictureBoxPreview.Invoke(new Action(() => 
                {pictureBoxPreview.Image?.Dispose();
                    pictureBoxPreview.Image = (Bitmap)frame.Clone();}));
            }
            else
            {pictureBoxPreview.Image?.Dispose();
                pictureBoxPreview.Image = (Bitmap)frame.Clone();}

            // 录制帧
            if (isRecording && videoWriter != null)
            {videoWriter.WriteVideoFrame(frame);
            }
        }
    }

    private void BtnStartStop_Click(object sender, EventArgs e)
    {if (!isRecording)
        {
            // 开始录制
            var saveDialog = new SaveFileDialog()
            {
                Filter = "MP4 视频 |*.mp4",
                FileName = $"录制_{DateTime.Now:yyyyMMdd_HHmmss}.mp4"
            };

            if (saveDialog.ShowDialog() == DialogResult.OK)
            {videoWriter = new VideoFileWriter();
                videoWriter.Open(saveDialog.FileName, 
                    videoSource.VideoResolution.FrameSize.Width, 
                    videoSource.VideoResolution.FrameSize.Height, 
                    25, VideoCodec.MPEG4);

                isRecording = true;
                btnStartStop.Text = "停止录制";
            }
        }
        else
        {
            // 停止录制
            isRecording = false;
            videoWriter.Close();
            videoWriter = null;
            btnStartStop.Text = "开始录制";

            MessageBox.Show("录制已完成");
        }
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        // 释放资源
        if (videoSource != null && videoSource.IsRunning)
        {videoSource.SignalToStop();
            videoSource.WaitForStop();
            videoSource.NewFrame -= OnNewFrame;
            videoSource = null;
        }

        if (videoWriter != null)
        {videoWriter.Close();
            videoWriter = null;
        }

        base.OnFormClosing(e);
    }
}

性能优化

  1. 内存管理

  2. 及时释放 Bitmap 资源,避免内存泄漏

  3. 使用 using 语句确保资源释放
  4. 减少不必要的位图复制

  5. 帧率控制

  6. 根据实际需求选择合适的帧率

  7. 可以使用帧跳过策略降低 CPU 负载

  8. 异常处理

  9. 处理摄像头断开连接的情况

  10. 处理磁盘空间不足的情况
  11. 添加超时机制

优化后的 NewFrame 事件处理:

private DateTime lastFrameTime = DateTime.MinValue;

private void OnNewFrame(object sender, NewFrameEventArgs eventArgs)
{
    try
    {
        // 控制帧率,每秒最多处理 25 帧
        if ((DateTime.Now - lastFrameTime).TotalMilliseconds < 40)
            return;

        lastFrameTime = DateTime.Now;

        using (var frame = (Bitmap)eventArgs.Frame.Clone())
        {// 省略预览和录制代码...}
    }
    catch (Exception ex)
    {Debug.WriteLine($"帧处理异常: {ex.Message}");
    }
}

避坑指南

  1. 摄像头权限问题

  2. Windows 10/11 需要确保应用有摄像头访问权限

  3. 检查隐私设置中的 ” 相机 ” 权限

  4. 视频无法播放

  5. 确保安装了合适的解码器

  6. 尝试不同的 VideoCodec 选项
  7. 检查视频文件头是否完整写入

  8. 设备不响应

  9. 检查设备是否被其他程序占用

  10. 尝试重新插拔摄像头
  11. 更新摄像头驱动程序

扩展思考

基于这个基础实现,你可以进一步开发更高级的功能:

  1. 人脸识别

  2. 使用 EmguCV 或 Dlib.net 添加人脸检测

  3. 实现人脸跟踪录制

  4. 运动检测

  5. 分析连续帧差异

  6. 设置运动触发录制

  7. 网络流传输

  8. 使用 RTP/RTSP 协议传输视频流

  9. 实现远程监控功能

  10. 多摄像头支持

  11. 同时控制多个摄像头

  12. 实现画中画或多视图布局

总结

本文详细介绍了使用 C# 和 DirectShow 控制网络摄像头并保存视频的完整流程。我们从设备枚举开始,逐步实现了视频预览、录制控制和文件保存功能,并讨论了性能优化和常见问题的解决方案。这个基础实现可以作为各种视频处理应用的起点,你可以根据具体需求进行扩展和定制。

在实际项目中,记得处理好资源释放和异常情况,确保应用的稳定性。如果需要更高级的功能,可以考虑集成专门的计算机视觉库,如 OpenCV 或 MediaPipe,来实现更复杂的视频分析功能。

正文完
 0
评论(没有评论)