当前位置: 首页 > ops >正文

用OpencvSharp编写视频录制工具

代码如下:

using System;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using Size = OpenCvSharp.Size;

namespace CameraRecorder
{
public partial class MainForm : Form
{
// RTSP 配置
//private string _rtspUrl = "rtsp://username:password@ip:port/stream";
private string _rtspUrl = System.Configuration.ConfigurationManager.AppSettings["CameraURL"].Trim();
private const int ReconnectInterval = 5000; // 重连间隔(ms)

        // 录制配置
private const string SaveDirectory = @"D:\VideoRecords\";
private const int Fps = 25;
private FourCC VideoCodec = FourCC.X264;

        // 状态控制
private volatile bool _isRecording;
private Thread _recordingThread;
private VideoCapture _videoCapture;
private VideoWriter _videoWriter;
private DateTime _currentHour;
private int _fileIndex = 0; // 文件索引,用于同一小时内的分段

        public MainForm()
{
InitializeComponent();
CheckDirectory();
}

        private void CheckDirectory()
{
if (!Directory.Exists(SaveDirectory))
{
Directory.CreateDirectory(SaveDirectory);
}
}

        private void btnStart_Click(object sender, EventArgs e)
{
if (_isRecording) return;

            _isRecording = true;
btnStart.Enabled = false;
btnStop.Enabled = true;
_fileIndex = 0; // 重置文件索引

            _recordingThread = new Thread(RecordingWorker);
_recordingThread.IsBackground = true;
_recordingThread.Start();
}

        private void btnStop_Click(object sender, EventArgs e)
{
StopRecording();
}

        private void RecordingWorker()
{
while (_isRecording)
{
try
{
// 创建新的视频捕获对象
_videoCapture = new VideoCapture(_rtspUrl);

                    // 检查是否成功打开
if (!_videoCapture.IsOpened())
{
Log("无法连接摄像头");
SafeReleaseCapture();
Thread.Sleep(ReconnectInterval);
continue;
}

                    Log("摄像头连接成功");
var frameSize = new Size(
(int)_videoCapture.Get(VideoCaptureProperties.FrameWidth),
(int)_videoCapture.Get(VideoCaptureProperties.FrameHeight));

                    // 初始化视频写入器(如果是新小时则重置索引)
if (DateTime.Now.Hour != _currentHour.Hour)
{
_fileIndex = 0;
}
CreateNewVideoWriter(frameSize);

                    Mat frame = new Mat();
while (_isRecording && _videoCapture.IsOpened())
{
// 检查是否需要创建新文件(每小时或连接中断后)
if (DateTime.Now.Hour != _currentHour.Hour)
{
_fileIndex = 0; // 新小时重置索引
SafeReleaseWriter();
CreateNewVideoWriter(frameSize);
}

                        // 读取帧
if (_videoCapture.Read(frame) && !frame.Empty())
{
// 写入视频帧
_videoWriter.Write(frame);

                            // 显示预览
DisplayFrame(frame);
}
else
{
Log("帧读取失败,尝试重连...");
break;
}

                        // 降低CPU使用率
Thread.Sleep(10);
}
}
catch (Exception ex)
{
Log($"捕获到异常: {ex.Message}");
}
finally
{
// 安全释放资源
SafeReleaseWriter();
SafeReleaseCapture();
}

                // 断线重连等待
if (_isRecording)
{
Log($"等待 {ReconnectInterval / 1000}秒后重连...");
Thread.Sleep(ReconnectInterval);
}
}
}

        private void CreateNewVideoWriter(Size frameSize)
{
_currentHour = DateTime.Now;

            // 生成文件名(带索引)
string fileName;
do
{
fileName = _fileIndex == 0
? $"{_currentHour:yyyyMMdd_HH}.mp4"
: $"{_currentHour:yyyyMMdd_HH}_{_fileIndex}.mp4";

                _fileIndex++;
} while (File.Exists(Path.Combine(SaveDirectory, fileName)));

            string filePath = Path.Combine(SaveDirectory, fileName);

            // 创建新的视频写入器
_videoWriter = new VideoWriter(filePath, VideoCodec, Fps, frameSize);

            Log($"创建视频文件: {fileName}");
}

        private void SafeReleaseCapture()
{
try
{
if (_videoCapture != null)
{
_videoCapture.Release();
_videoCapture.Dispose();
_videoCapture = null;
}
}
catch (Exception ex)
{
Log($"释放摄像头资源错误: {ex.Message}");
}
}

        private void SafeReleaseWriter()
{
try
{
if (_videoWriter != null)
{
_videoWriter?.Release();
_videoWriter?.Dispose();
_videoWriter = null;
}
}
catch (Exception ex)
{
Log($"释放视频写入器错误: {ex.Message}");
}
}

        private void DisplayFrame(Mat frame)
{
if (pictureBoxPreview.InvokeRequired)
{
pictureBoxPreview.Invoke(new Action<Mat>(DisplayFrame), frame);
return;
}

            try
{
// 高效更新预览图
using (var tempBitmap = frame.ToBitmap())
{
if (pictureBoxPreview.Image != null)
{
var oldImage = pictureBoxPreview.Image;
pictureBoxPreview.Image = null;
oldImage.Dispose();
}
pictureBoxPreview.Image = (Bitmap)tempBitmap.Clone();
}
}
catch (Exception ex)
{
Log($"预览错误: {ex.Message}");
}
}

        private void Log(string message)
{
if (txtLog.InvokeRequired)
{
txtLog.Invoke(new Action<string>(Log), message);
return;
}

            txtLog.AppendText($"[{DateTime.Now:HH:mm:ss}] {message}\r\n");
txtLog.ScrollToCaret();
}

        private void StopRecording()
{
_isRecording = false;
btnStart.Enabled = true;
btnStop.Enabled = false;
Cv2.WaitKey(500);
// 安全释放资源
SafeReleaseWriter();
SafeReleaseCapture();
}

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
StopRecording();
_recordingThread?.Join(2000); // 等待线程结束
}
}
}

http://www.xdnf.cn/news/18365.html

相关文章:

  • Matplotlib数据可视化实战:Matplotlib数据可视化入门与实践
  • 【Android】悬浮窗清理
  • Pytorch基础学习--张量(生成,索引,变形)
  • 从系统漏洞归零到候诊缩短20%:一个信创样本的效能革命
  • 机器学习聚类与集成算法全解析:从 K-Means 到随机森林的实战指南
  • CRMEB私域电商系统后台开发实战:小程序配置全流程解析
  • 贪吃蛇游戏(纯HTML)
  • 什么是区块链?从比特币到Web3的演进
  • 图像中物体计数:基于YOLOv5的目标检测与分割技术
  • 十分钟速通堆叠
  • 智慧城市SaaS平台/市政设施运行监测系统之空气质量监测系统、VOC气体监测系统、污水水质监测系统及环卫车辆定位调度系统架构内容
  • 终结开发混乱,用 Amazon Q 打造AI助手
  • 华为云ModelArts+Dify AI:双剑合璧使能AI应用敏捷开发
  • CSS【详解】性能优化
  • 【知识储备】PyTorch / TensorFlow 和张量的联系
  • 数字货币发展存在的问题:交易平台的问题不断,但监管日益加强
  • React + Antd+TS 动态表单容器组件技术解析与实现
  • Linux -- 封装一个线程池
  • 射频电路的完整性简略
  • ubuntu编译ijkplayer版本k0.8.8(ffmpeg4.0)
  • JVM-(7)堆内存逻辑分区
  • 智能编程中的智能体与 AI 应用:概念、架构与实践场景
  • 【Flutter】Container设置对齐方式会填满父组件剩余空间
  • BaaS(Backend as a Service)技术深度解析:云时代的后端开发革命
  • 数据结构青铜到王者第一话---数据结构基本常识(1)
  • Spring面试宝典:Spring IOC的执行流程解析
  • JavaScript 十六进制与字符串互相转(HEX)
  • 通义千问VL-Plus:当AI“看懂”屏幕,软件测试的OCR时代正式终结!
  • 微信小程序基础Day1
  • iOS 文件管理全景实战 多工具协同提升开发与调试效率