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

用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.dtcms.com/a/342934.html

相关文章:

  • HTTP/2 性能提升的核心原因
  • Vue2 ElementUI Upload组件http-request用法
  • (二十一)深入了解AVFoundation-编辑:导出视频与格式转换的全流程
  • 全文 part1 - DGEMM Using Tensor Cores, and Its Accurate and Reproducible Versions
  • DeepSeek-V3.1 发布,迈向 Agent 时代的第一步
  • 0821 sqlite3_get_table函数(数据库函数的补充)
  • Nacos-9--认识Nacos中的Distro协议(Nacos高可用的实现原理)
  • visual studio编译的软件查找所依赖的运行库方法
  • 基于单片机智能路灯控制
  • 学习嵌入式第三十四天
  • 杂记 07
  • BGP高级特性
  • AI论文速读 | 多模态能否助力时间序列预测?时序预测中融合文本的边界与条件
  • Oracle CLOB类型转换
  • 数据分析三剑客
  • 如何解读京东按图搜索(拍立淘)API(jd.item_search_img)的返回值
  • AI大模型支持下的:CMIP6数据分析与可视化、降尺度技术与气候变化的区域影响、极端气候分析
  • JVM-(7)堆内存逻辑分区
  • 3个脱节,5大特征,1套方法:破解AI落地难题
  • 37、需求预测与库存优化 (快消品) - /供应链管理组件/fmcg-inventory-optimization
  • 【互动屏幕】大屏拼接在数字展厅展示上有哪些优势?
  • (CVPR-2025)通过频率分解实现身份保持的文本到视频生成
  • 【音视频】闭合GOP和开放GOP
  • 旅游小程序开发指南
  • 第三阶段数据库-5:数据库的主键,索引,约束,表间关系的图形化操作
  • 8.Shell脚本修炼手册---sed工具的基本使用
  • HarmonyOS 实战:6 种实现实时数据更新的方案全解析(含完整 Demo)
  • JavaScript中的深浅拷贝
  • Llama-Factory微调 Qwen2.5-VL-3B 模型
  • 人工智能未来趋势如何?