Opencvsharp对RTSP视频流的显示、抓拍、分段录像
opencv
OpenCV: 开源计算机视觉库
项目地址:https://gitcode.com/gh_mirrors/opencv31/opencv
免费下载资源
·
c#中有许多方法可以对RTSP流进行显示,抓拍或录像,经过不懈努力,觉得Opencvsharp不错,然后动手集成了一个可以预览、停止、抓拍、分段录像的控件dll,方便大家使用,
解决视频流的稳定性问题,还有吃内存问题。经过24小时的录像测试,完美运行。
控件界面如下:
下面的图片是显示两个流,大家可以根据您的需要加载不同数量的视频。
点击预览按钮后,显示效果如下:
控件按钮:预览、停止、抓拍、录像、停止录像
控件特点:设置视频帐号密码IP后可以快速进行连接,抓拍图片可以自定义一些字符串,如项目名称。录像可以设置段大小缺省120s两分钟一个文件,根据场景自由的设置。另外系统中还演示如何压缩指定目录下的所有录像文件。
录像文件一分钟mp4大概有300-700MB,可以压缩到20倍左右。
一、创建控件,名为HKRtsp
NuGet引用:OpenCvSharp3-AnyCPU v4.0.0.20181129
目标框架:Net Framework 4.7.2
这是读视频,抓拍,录像控件
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Drawing;
using OpenCvSharp.Extensions;
using OpenCvSharp;
namespace CaseRecords.Control
{
public partial class HKRtsp : UserControl
{
public HKRtsp()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
syncPostUpdate = SynchronizationContext.Current;
}
#region 变量
CancellationTokenSource cts;
//OpenCvSharp 使用
VideoCapture capture;
private VideoWriter videoWriter;
private bool recordingFlag = false;
private SynchronizationContext syncPostUpdate = null;
#endregion
#region 自定义事件
//包含异常或者正常的消息, type = 0正常,type = 1异常消息
public delegate void VideoHasMessage(long videoId, int type, long caseId, string msg, string memo);
//type = 0 抓拍,type = 1 录像,memo为当前监控的说明
public delegate void UpdateDBAction(long videoId, int type, long caseId, string fileName, string memo);
public VideoHasMessage videoHasMessage;
public UpdateDBAction updateDBAction;
#endregion
#region 海康HKRtsp取流属性
private string _rtspUrl = "";
private string _rtspLoginUserName = "";
private string _rtspLoginPassword = "";
private int _rtspPort = 554;
private string _rtspIp = "";
private long _caseId = 0; //CaseId
private string _rtspMemo = "";
private string _rtspRecordingFileName = "";
private string _rtspRecordingFileNamePattern = "";
// segmentDuration 录像分段时长(秒) 5分钟 5 * 60 = 300
private int segmentDuration = 60;
private int segmentCounter = 0; //当前的分段数量
private string _projectName = ""; //项目名称
private string _videoDescription = ""; //视频位置描述
private long lastSaveTime = 0; //当前开始录像的点
private bool stopFlag = false; //停止
private bool manualControl = false; //手动控制
private int _fps = 25; //缺省的流帧数量
private long _videoId; //当前视频配置Id
private string _sourceDir = ""; //文件存储目录
private bool _useVideo = true; //缺省有效
[Description("监控手动控制"), Category("项目配置")]
public bool ManualControl
{
set
{
manualControl = value;
}
get
{
return manualControl;
}
}
[Description("摄像头是否有效"), Category("项目配置")]
public bool UseVideo
{
set
{
_useVideo = value;
}
get
{
return _useVideo;
}
}
[Description("项目抓拍录像存储存目录"), Category("项目配置")]
public string SourceDir
{
set
{
_sourceDir = value;
}
get
{
return _sourceDir;
}
}
[Description("视频配置ID"), Category("项目配置")]
public long VideoId
{
set
{
_videoId = value;
}
get
{
return _videoId;
}
}
[Description("项目名称"), Category("项目配置")]
public string ProjectName
{
set
{
_projectName = value;
}
get
{
return _projectName;
}
}
[Description("视频位置说明"), Category("项目配置")]
public string VideoDescription
{
set
{
_videoDescription = value;
}
get
{
return _videoDescription;
}
}
[Description("Rtsp流备注说明"), Category("流配置")]
public string RtspMemo
{
set
{
_rtspMemo = value;
}
get
{
return _rtspMemo;
}
}
[Description("Rtsp流地址"), Category("流配置")]
public string RtspUrl
{
set
{
_rtspUrl = value;
}
get
{
return _rtspUrl;
}
}
[Description("Rtsp流IP地址"), Category("流配置")]
public string RtspIp
{
set
{
_rtspIp = value;
}
get
{
return _rtspIp;
}
}
[Description("Rtsp流登录用户名"), Category("流配置")]
public string RtspLoginUserName
{
set
{
_rtspLoginUserName = value;
}
get
{
return _rtspLoginUserName;
}
}
[Description("Rtsp流登录密码"), Category("流配置")]
public string RtspLoginPassword
{
set
{
_rtspLoginPassword = value;
}
get
{
return _rtspLoginPassword;
}
}
[Description("Rtsp流端口"), Category("流配置")]
public int RtspPort
{
set
{
_rtspPort = value;
}
get
{
return _rtspPort;
}
}
[Description("唯一案件Id"), Category("流配置")]
public long CaseId
{
set
{
_caseId = value;
}
get
{
return _caseId;
}
}
[Description("Rtsp录像时间S"), Category("流配置")]
public int SegmentDuration
{
set
{
segmentDuration = value;
}
get
{
return segmentDuration;
}
}
[Description("RTSP帧数量"), Category("流配置")]
public int FPS
{
get
{
return _fps;
}
set
{
_fps = value;
}
}
#endregion
#region 海康HKRtsp取流操作函数
//开始取流
public void StartReadStream()
{
try
{
if (String.IsNullOrEmpty(_rtspUrl) && (String.IsNullOrEmpty(_rtspIp) || String.IsNullOrEmpty(_rtspLoginUserName) ||
String.IsNullOrEmpty(_rtspLoginPassword) || _rtspPort == 0))
{
syncPostUpdate.Post(UpdateDeviceStatus, "视频参数设置不全,无法播放视频!");
return;
}
if (String.IsNullOrEmpty(_rtspUrl))
{
_rtspUrl = String.Format("rtsp://{0}:{1}@{2}:{3}/mpeg4/Channels/1", _rtspLoginUserName, _rtspLoginPassword, _rtspIp, _rtspPort);
}
syncPostUpdate.Post(UpdateDeviceStatus, "开始播放视频:" + _rtspUrl);
if (cts != null)
{
cts.Dispose();
cts = null;
}
cts = new CancellationTokenSource();
Task task = new Task(() =>
{
//打开视频流
if (capture == null)
{
capture = new VideoCapture();
}
toolOpen.Enabled = false;
capture.Open(_rtspUrl);
int time = 10;
if (capture.IsOpened())
{
syncPostUpdate.Post(UpdateButtonStatus, "");
syncPostUpdate.Post(UpdateVideoStatus, 1);
// 获取视频的帧率和分辨率
double fps = capture.Fps;
time = Convert.ToInt32(Math.Round(1000 / capture.Fps));
Debug.Print(String.Format("开始预览视频分辩率:{0},宽:{1},高:{2},时间:{3}", capture.Fps, capture.FrameWidth, capture.FrameHeight, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
while (!stopFlag)
{
if (!capture.IsOpened())
{
Debug.Print("断开连接时,断线重连 ...." + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
capture.Release();
capture.Open(_rtspUrl);
continue;
}
Mat frame = new Mat();
//判断是否被取消
if (cts.Token.IsCancellationRequested)
{
if (picShowImage != null)
{
Image image = picShowImage.Image;
picShowImage.Image = null;
if (image != null)
{
image.Dispose();
}
}
if (frame != null)
{
frame.Release();
frame.Dispose();
frame = null;
}
break;
}
try
{
bool get = capture.Read(frame);
if (get)
{
if (frame.Empty())
{
syncPostUpdate.Post(UpdateDeviceStatus, String.Format("帧内容为空! VideCapture状态:{0},时间:{1},重连中 ..." + capture.IsOpened(), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
syncPostUpdate.Post(UpdateVideoStatus, 2);
capture.Release();
capture.Open(_rtspUrl);
continue;
}
//录像状态时
if (recordingFlag & videoWriter != null && !videoWriter.IsDisposed && videoWriter.IsOpened())
{
// 将当前帧写入VideoWriter对象中
videoWriter.Write(frame);
long currentTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
// 判断是否需要开始保存下一个分段视频
//Debug.Print(String.Format("当前时间:{0},最后保存时间:{1},片段长度:{2}", currentTime, lastSaveTime, segmentDuration));
if (currentTime >= lastSaveTime + segmentDuration)
{
SaveSegment("正常分段");
}
}
//监控预览状态
try
{
if (picShowImage != null && frame != null && !frame.Empty())
{
Image image = picShowImage.Image;
picShowImage.Image = BitmapConverter.ToBitmap(frame);
if (image != null)
{
image.Dispose();
}
}
}
catch (Exception picEx)
{
Debug.Print("显示预览失败:" + picEx.Message);
}
}
else
{
//异常时自动保存录像文件
if (recordingFlag & videoWriter != null && videoWriter.IsOpened())
{
SaveSegment("无帧异常分段");
}
Debug.Print("Frame为空,断线重连 ...." + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
capture.Release();
capture.Open(_rtspUrl);
continue;
}
}
catch (Exception takeError)
{
Debug.Print("捕获RTSP帧错误:" + takeError.Message);
}
finally
{
frame.Dispose();
}
Thread.Sleep(10);
}
}
else
{
syncPostUpdate.Post(UpdateDeviceStatus, "预览视频失败!");
}
//用户退出时自动保存录像文件
if (recordingFlag & videoWriter != null && videoWriter.IsOpened())
{
saveRecordingFile("用户关闭视频");
}
capture.Release();
capture.Dispose();
capture = null;
syncPostUpdate.Post(UpdateButtonStatus, "");
syncPostUpdate.Post(UpdateVideoStatus, 0);
}, cts.Token);
task.Start();
}
catch (Exception ex)
{
syncPostUpdate.Post(UpdateDeviceStatus, String.Format("打开视频失败:{0}", ex.Message));
}
}
//保存分段录像
private void SaveSegment(string type)
{
try
{
if(videoWriter != null)
{
saveRecordingFile(type);
segmentCounter++;
// 重新开始计时
lastSaveTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
_rtspRecordingFileName = String.Format(_rtspRecordingFileNamePattern, segmentCounter.ToString().PadLeft(3, '0'));
// 创建新的VideoWriter对象,设置输出视频的参数
videoWriter.Open(_rtspRecordingFileName, FourCC.MPG4, _fps, new OpenCvSharp.Size(capture.FrameWidth, capture.FrameHeight));
}
}
catch (Exception ex)
{
syncPostUpdate.Post(UpdateDeviceStatus, "保存录像分段失败:" + ex.Message);
}
}
private void saveRecordingFile(String type)
{
try
{
// 释放之前的VideoWriter对象
videoWriter.Release();
updateDBAction(_videoId, 1, _caseId, _rtspRecordingFileName, _rtspMemo);
syncPostUpdate.Post(UpdateDeviceStatus,String.Format("{0},{1},保存录像文件: {2} 成功!", type,
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), _rtspRecordingFileName));
}
catch (Exception ex)
{
syncPostUpdate.Post(UpdateDeviceStatus, String.Format("{0},保存录像失败:{1}", type, ex.Message));
}
}
//停止取流
public void StopReadStream()
{
try
{
if (recordingFlag)
{
StopRecording();
}
cts.Cancel();
syncPostUpdate.Post(UpdateDeviceStatus, "关闭视频");
}
catch (Exception ex)
{
syncPostUpdate.Post(UpdateDeviceStatus, String.Format("停止视频:{0},失败:{0}", _rtspRecordingFileName, ex.Message));
}
}
//抓拍,保存按项目路径,编号,备注来保存
public bool SaveImage(string projectPath, string projectName, out string filePath)
{
filePath = "";
try
{
if (capture == null || capture.IsDisposed)
{
syncPostUpdate.Post(UpdateDeviceStatus, "没有创建OpenCv对象,无法保存抓拍!");
return false;
}
if (!capture.IsOpened())
{
syncPostUpdate.Post(UpdateDeviceStatus, "没有Rtsp流,无法保存抓拍!");
return false;
}
//如果不指定存储目录时,缺省保存到当前应用程序目录
if (String.IsNullOrEmpty(projectPath))
{
projectPath = Application.StartupPath;
}
//如果最后一个字符中 \时,移走
if(projectPath.Substring(projectPath.Length - 1).Equals(@"\")) projectPath = Path.GetDirectoryName(projectPath);
//抓拍保存为当前目录下,原来是按年月日分类保存
//projectPath = projectPath + @"\Images\" + DateTime.Now.ToString("yyyy") + @"\" + DateTime.Now.ToString("MM") + @"\"
// + DateTime.Now.ToString("dd") + @"\" + projectName + @"\Capture\No_" + _caseId.ToString();
//更新存储路径
projectPath = projectPath + @"\Images\" + projectName + @"\" + _videoId + @"\";
if (!Directory.Exists(projectPath))
{
Directory.CreateDirectory(projectPath);
}
projectPath = projectPath + DateTime.Now.ToString("yyyyMMddHHmmss_ff") + ".jpg";
//此方法是通过pictureBox进行抓拍
if (picShowImage.Image != null)
{
filePath = projectPath;
// 将PictureBox中的图像保存为JPG文件
picShowImage.Image.Save(projectPath, System.Drawing.Imaging.ImageFormat.Jpeg);
syncPostUpdate.Post(UpdateDeviceStatus, "抓拍保存:" + projectPath);
updateDBAction(_videoId, 0, _caseId, projectPath, _rtspMemo);
return true;
}
else
{
filePath = "";
syncPostUpdate.Post(UpdateDeviceStatus, "抓拍失败,当前图片为空!");
return false;
}
}
catch (Exception ex)
{
filePath = "";
syncPostUpdate.Post(UpdateDeviceStatus, String.Format("抓拍失败:{0}", ex.Message));
return false;
}
}
//开始录像
public bool StartRecording(string projectPath, string projectName, out string filePath)
{
filePath = "";
try
{
if (capture == null || capture.IsDisposed)
{
syncPostUpdate.Post(UpdateDeviceStatus, "没有创建OpenCv对象,无法保存录像!");
return false;
}
if (!capture.IsOpened())
{
syncPostUpdate.Post(UpdateDeviceStatus, "没有Rtsp流,无法保存录像!");
return false;
}
if (String.IsNullOrEmpty(projectPath))
{
projectPath = Application.StartupPath;
}
//如果最后一个字符中 \时,移走
if (projectPath.Substring(projectPath.Length - 1).Equals(@"\")) projectPath = Path.GetDirectoryName(projectPath);
//录像保存为当前目录下,Video\2024\04\01\汨罗\
//projectPath = projectPath + @"\Video\" + DateTime.Now.ToString("yyyy") + @"\" + DateTime.Now.ToString("MM") + @"\"
// + DateTime.Now.ToString("dd") + @"\" + projectName + @"\VideoRecord\No_" + _caseId.ToString();\
//更新存储路径
projectPath = projectPath + @"\Video\" + projectName + @"\" + _videoId + @"\";
if (!Directory.Exists(projectPath))
{
Directory.CreateDirectory(projectPath);
}
projectPath = projectPath + @"{0}_" + DateTime.Now.ToString("yyyyMMddHHmmss_ff") + ".mp4";
_rtspRecordingFileNamePattern = projectPath;
//初始化第一个分段
segmentCounter = 1;
filePath = String.Format(projectPath, segmentCounter.ToString().PadLeft(3, '0'));
_rtspRecordingFileName = filePath;
syncPostUpdate.Post(UpdateDeviceStatus, "开始录像:" + _rtspRecordingFileName);
if (videoWriter != null)
{
Debug.Print("释放原来的录像对象 VideoWrite");
videoWriter.Release();
videoWriter.Dispose();
videoWriter = null;
}
videoWriter = new VideoWriter(_rtspRecordingFileName, FourCC.MPG4, _fps, new OpenCvSharp.Size(capture.FrameWidth, capture.FrameHeight));
lastSaveTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
recordingFlag = true;
return true;
}
catch (Exception ex)
{
filePath = "";
syncPostUpdate.Post(UpdateDeviceStatus, String.Format("录像操作失败:{0}", ex.Message));
return false;
}
}
//停止录像
public void StopRecording()
{
try
{
recordingFlag = false;
if (videoWriter != null && !videoWriter.IsDisposed)
{
videoWriter.Release();
videoWriter.Dispose();
videoWriter = null;
syncPostUpdate.Post(UpdateDeviceStatus, String.Format("保存录像成功:{0}", _rtspRecordingFileName));
updateDBAction(_videoId, 1, _caseId, _rtspRecordingFileName, _rtspMemo);
}
else
{
//界面显示当前的操作状态
syncPostUpdate.Post(UpdateDeviceStatus, "录像已经停止");
}
}
catch (Exception ex)
{
syncPostUpdate.Post(UpdateDeviceStatus, String.Format("停止录像:{0},失败:{1}", _rtspRecordingFileName, ex.Message));
}
}
//释放
public void Release()
{
try
{
if (recordingFlag)
{
StopRecording();
}
stopFlag = true;
if (videoWriter != null && !videoWriter.IsDisposed)
{
if(recordingFlag) saveRecordingFile("系统关闭");
videoWriter.Release();
videoWriter.Dispose();
videoWriter = null;
}
if (capture != null && !capture.IsDisposed)
{
capture.Release();
capture.Dispose();
capture = null;
}
}
catch (Exception ex)
{
LogHelper.WriteError(String.Format("释放OpenCv错误:{0}", ex.Message));
}
}
public void UpdateVideoStatus(object status)
{
try
{
switch (status)
{
case 0:
if(lblVideoStatus.Image != Properties.Resources.gray16) lblVideoStatus.Image = Properties.Resources.gray16;
break;
case 1:
if (lblVideoStatus.Image != Properties.Resources.green) lblVideoStatus.Image = Properties.Resources.green;
break;
case 2:
if (lblVideoStatus.Image != Properties.Resources.red) lblVideoStatus.Image = Properties.Resources.red;
break;
}
}
catch (Exception ex)
{
LogHelper.WriteError("更新视频状态灯错误:" + ex.Message);
}
}
public void UpdateButtonStatus(object status)
{
try
{
if (!manualControl)
{
toolOpen.Enabled = false;
toolStop.Enabled = false;
toolCapture.Enabled = false;
toolStartRecording.Enabled = false;
toolStopRecording.Enabled = false;
return;
}
// 已经打开流时,不可以再打开,停止按钮有效
if (capture != null)
{
toolOpen.Enabled = false;
toolStop.Enabled = true;
toolCapture.Enabled = true;
if (recordingFlag)
{
toolStartRecording.Enabled = false;
toolStopRecording.Enabled = true;
}
else
{
toolStartRecording.Enabled = true;
toolStopRecording.Enabled = false;
}
}
else
{
toolOpen.Enabled = true;
toolStop.Enabled = false;
toolCapture.Enabled = false;
toolStartRecording.Enabled = false;
toolStopRecording.Enabled = false;
}
}
catch (Exception ex)
{
syncPostUpdate.Post(UpdateDeviceStatus, String.Format("更新控制按钮状态错误:{0}", ex.Message));
}
}
#endregion
private void HKRtsp_SizeChanged(object sender, EventArgs e)
{
try
{
lyMain.Left = 0;
lyMain.Top = 0;
lyMain.Width = this.Width;
lyMain.Height = this.Height - ssStatus.Height - 10;
picShowImage.Width = this.Width;
picShowImage.Height = this.Height - ssStatus.Height - 20;
}
catch (Exception ex)
{
UpdateDeviceStatus("监控预览改变窗口错误:" + ex.Message);
}
}
private void UpdateDeviceStatus(object status)
{
try
{
string txt = (string)status;
ssStatus.Text = txt;
videoHasMessage?.Invoke(_videoId, 0, _caseId, txt, _rtspMemo);
LogHelper.WriteInfo(txt);
Debug.Print("UpdateDeviceStatus:" + txt);
}
catch (Exception ex)
{
LogHelper.WriteException("UpdateDeviceStatus错误", ex);
}
}
private void toolStop_Click(object sender, EventArgs e)
{
try
{
toolStop.Enabled = false;
StopReadStream();
syncPostUpdate.Post(UpdateButtonStatus, "");
}
catch (Exception ex)
{
syncPostUpdate.Post(UpdateDeviceStatus, "停止视频错误:" + ex.Message);
}
finally
{
syncPostUpdate.Post(UpdateButtonStatus, "");
}
}
private void toolOpen_Click(object sender, EventArgs e)
{
try
{
toolOpen.Enabled = false;
StartReadStream();
}
catch (Exception ex)
{
syncPostUpdate.Post(UpdateDeviceStatus, "打开视频错误:" + ex.Message);
}
finally
{
syncPostUpdate.Post(UpdateButtonStatus, "");
}
}
private void toolCapture_Click(object sender, EventArgs e)
{
try
{
toolCapture.Enabled = false;
string fileName = "";
SaveImage(_sourceDir, _projectName, out fileName);
Debug.Print("抓拍图片:" + fileName);
syncPostUpdate.Post(UpdateButtonStatus, "");
}
catch (Exception ex)
{
syncPostUpdate.Post(UpdateDeviceStatus, "抓拍错误:" + ex.Message);
}
finally
{
syncPostUpdate.Post(UpdateButtonStatus, "");
}
}
private void toolStartRecording_Click(object sender, EventArgs e)
{
try
{
toolStartRecording.Enabled = false;
string fileName = "";
StartRecording(_sourceDir, _projectName, out fileName);
Debug.Print("录像文件:" + fileName);
syncPostUpdate.Post(UpdateButtonStatus, "");
}
catch (Exception ex)
{
syncPostUpdate.Post(UpdateDeviceStatus, "开始录像错误:" + ex.Message);
}
finally
{
syncPostUpdate.Post(UpdateButtonStatus, "");
}
}
private void toolStopRecording_Click(object sender, EventArgs e)
{
try
{
toolStartRecording.Enabled = false;
StopRecording();
syncPostUpdate.Post(UpdateButtonStatus, "");
}
catch (Exception ex)
{
syncPostUpdate.Post(UpdateDeviceStatus, "停止录像错误:" + ex.Message);
}
finally
{
syncPostUpdate.Post(UpdateButtonStatus, "");
}
}
private void HKRtsp_Load(object sender, EventArgs e)
{
}
}
}
二、创建控件,名为HKPlayer
引用系统COM中的Windows Media Player
这是播放视频控件
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Diagnostics;
using WMPLib;
/// <summary>
///
/* 1、首先要在项目中导入 COM中选择 WindowsMediaPlayer
* 2、下面是使用实例
* //播放单个文件
* frmPlayer player = new frmPlayer(
true, //播放单个文件
@"G:\CSharp\VideoRecorder\CaseRecords\bin\x64\Debug\Video\proj1\%e7%a3%85%e4%b8%8a%e6%91%84%e5%83%8f%e5%a4%b4\001_20240508162416_91.mp4", //mp4文件路径
null, //多个文件列表
""); //当前mp4位置,以后打包使用
player.ShowDialog();
//播放多个文件时
frmPlayer player = new frmPlayer(
false, //单个文件播放试为假
"", //单个文件为空
list, //多个播放文件集合
""); //Mp4文件当前目录,暂时保留为空
player.ShowDialog();
* //创建播放实例 frmPlayer窗体 ============================================================================================
* private HKPlayer player = new HKPlayer();
private void frmPlayer_Load(object sender, EventArgs e)
{
//播放单个文件时,SinglePlay=True,并且 Mp4FileUrl 必须赋值Mp4文件路径
player.Parent = this;
player.SinglePlay = true;
player.Mp4FileFolder = "";
player.Mp4FileList = null;
player.Mp4FileUrl = @"G:\CSharp\VideoRecorder\CaseRecords\bin\x64\Debug\Video\2024\05\03\miluo\VideoRecord\No_0\1.mp4";
player.Play();
//播放多个文件,SinglePlay=false,并且 Mp4FileList 必须赋值 List<string> 播放列表。
player.Parent = this;
player.SinglePlay = false;
player.Mp4FileFolder = "";
List<string> list = new List<string> {
@"G:\CSharp\VideoRecorder\CaseRecords\bin\x64\Debug\Video\2024\05\03\miluo\VideoRecord\No_0\1.mp4",
@"G:\CSharp\VideoRecorder\CaseRecords\bin\x64\Debug\Video\2024\05\03\miluo\VideoRecord\No_0\2.mp4",
@"G:\CSharp\VideoRecorder\CaseRecords\bin\x64\Debug\Video\2024\05\03\miluo\VideoRecord\No_0\3.mp4"
};
player.Mp4FileList = list;
player.Play();
//播放窗体自适应
frmPlayer_Resize(null, null);
}
private void frmPlayer_FormClosed(object sender, FormClosedEventArgs e)
{
player.Close();
}
private void frmPlayer_Resize(object sender, EventArgs e)
{
player.Left = 0;
player.Top = 0;
player.Width = Width - 20;
player.Height = Height - 50;
}
*/
/// </summary>
namespace CaseRecords.Control
{
public partial class HKPlayer : UserControl
{
public HKPlayer()
{
InitializeComponent();
player.Parent = this;
player.Dock = DockStyle.Fill;
player.MediaError += Player_MediaError;
player.StatusChange += Player_StatusChange;
// 监听播放结束事件来播放下一个文件
player.PlayStateChange += Player_PlayStateChange;
}
private void Player_PlayStateChange(object sender, AxWMPLib._WMPOCXEvents_PlayStateChangeEvent e)
{
try
{
PlayNextMediaFile();
}
catch (Exception ex)
{
playstatus?.Invoke(_projectName, _videoDescription,
string.Format("播放Mp4 Player_PlayStateChange发生错误因为:{0}。", ex.Message));
}
}
private void PlayNextMediaFile()
{
// 假设你有一个文件路径的字符串数组
if (!_singlePlay && player.playState == WMPPlayState.wmppsMediaEnded)
{
}
}
private void Player_StatusChange(object sender, EventArgs e)
{
try
{
/*
* 0 Undefined Windows Media Player is in an undefined state.(未定义)
1 Stopped Playback of the current media item is stopped.(停止)
2 Paused Playback of the current media item is paused. When a media item is paused, resuming playback begins from the same location.(停留)
3 Playing The current media item is playing.(播放)
4 ScanForward The current media item is fast forwarding.
5 ScanReverse The current media item is fast rewinding.
6 Buffering The current media item is getting additional data from the server.(转换)
7 Waiting Connection is established, but the server is not sending data. Waiting for session to begin.(暂停)
8 MediaEnded Media item has completed playback. (播放结束)
9 Transitioning Preparing new media item.
10 Ready Ready to begin playing.(准备就绪)
11 Reconnecting Reconnecting to stream.(重新连接)
wmppsUndefined = 0,
wmppsStopped = 1,
wmppsPaused = 2,
wmppsPlaying = 3,
wmppsScanForward = 4,
wmppsScanReverse = 5,
wmppsBuffering = 6,
wmppsWaiting = 7,
wmppsMediaEnded = 8,
wmppsTransitioning = 9,
wmppsReady = 10,
wmppsReconnecting = 11,
wmppsLast = 12
*/
switch ((int)player.playState)
{
case 1:
Debug.Print("播放状态:停止");
break;
case 2:
Debug.Print("播放状态:暂停");
break;
case 3:
Debug.Print("播放状态:播放");
break;
case 9:
Debug.Print("播放状态:传输");
break;
}
}
catch (Exception ex)
{
playstatus?.Invoke(_projectName, _videoDescription,
string.Format("播放Mp4 Player_StatusChange发生错误因为:{0}。", ex.Message));
}
}
private void Player_MediaError(object sender, AxWMPLib._WMPOCXEvents_MediaErrorEvent e)
{
try
{
IWMPMedia2 errSource = e.pMediaObject as IWMPMedia2;
IWMPErrorItem errorItem = errSource.Error;
playstatus?.Invoke(_projectName, _videoDescription,
string.Format("播放Mp4发生错误代码:{0}在{1}。", errorItem.errorCode.ToString("X") , errSource.sourceURL));
}
catch (Exception ex)
{
playstatus?.Invoke(_projectName, _videoDescription,
string.Format("播放Mp4发生错误因为:{0}。", ex.Message));
}
}
#region 自定义返回事件
public delegate void PlayerStatus(string projectName, string videoDescription, string msg);
public PlayerStatus playstatus;
#endregion
#region Mp4播放器属性
private string _mp4FileUrl = "";
private bool _singlePlay = false;
private string _mp4FileFolder = "";
private List<string> _mp4FileList;
private string _projectName = ""; //项目名称
private string _videoDescription = ""; //视频位置描述
private AxWMPLib.AxWindowsMediaPlayer player = new AxWMPLib.AxWindowsMediaPlayer();
private string currentPlayingFile = ""; //当前播放的文件
[Description("Mp4文件地址"), Category("播放配置")]
public string Mp4FileUrl
{
set
{
_mp4FileUrl = value;
}
get
{
return _mp4FileUrl;
}
}
/// <summary>
/// SinglePlay=true为播放单一文件,=false时,播所有目录下文件
/// </summary>
[Description("Mp4文件播放类型"), Category("播放配置")]
public bool SinglePlay
{
set
{
_singlePlay = value;
}
get
{
return _singlePlay;
}
}
[Description("Mp4播放目录"), Category("播放配置")]
public string Mp4FileFolder
{
set
{
_mp4FileFolder = value;
}
get
{
return _mp4FileFolder;
}
}
[Description("Mp4播放文件列表"), Category("播放配置")]
public List<string> Mp4FileList
{
set
{
_mp4FileList = value;
}
get
{
return _mp4FileList;
}
}
[Description("项目名称"), Category("项目配置")]
public string ProjectName
{
set
{
_projectName = value;
}
get
{
return _projectName;
}
}
[Description("视频位置说明"), Category("项目配置")]
public string VideoDescription
{
set
{
_videoDescription = value;
}
get
{
return _videoDescription;
}
}
#endregion
#region 播放操作
public void Play()
{
try
{
//单个文件播放,并且地址为空时
if ((_singlePlay && string.IsNullOrEmpty(_mp4FileUrl)) || (!_singlePlay && Mp4FileList.Count == 0))
{
Debug.Print("播放文件Url不能为空!");
playstatus?.Invoke(_projectName, _videoDescription, "播放文件Url不能为空!");
return;
}
//开始播放视频
if (_singlePlay)
{
currentPlayingFile = Mp4FileUrl;
player.URL = currentPlayingFile;
}
else
{
//多文件循环播放,一个接一个,除非关闭或者停止
currentPlayingFile = Mp4FileList[0];
player.currentPlaylist = player.newPlaylist("DragonPlayList", "");
foreach (string item in Mp4FileList)
{
player.currentPlaylist.appendItem(player.newMedia(item));
}
}
playstatus?.Invoke(_projectName, _videoDescription, "开始播放文件:" + currentPlayingFile);
player.Ctlcontrols.play();
}
catch (Exception ex)
{
playstatus?.Invoke(_projectName, _videoDescription, "播放失败:" + ex.Message);
}
}
public void Stop()
{
try
{
//停止播放
if (player.playState == WMPPlayState.wmppsPlaying)
{
player.Ctlcontrols.stop();
playstatus?.Invoke(_projectName, _videoDescription, "停止播放:" + currentPlayingFile);
}
}
catch (Exception ex)
{
playstatus?.Invoke(_projectName, _videoDescription, "停止播放失败:" + ex.Message);
}
}
public void Close()
{
try
{
player.MediaError -= Player_MediaError;
player.StatusChange -= Player_StatusChange;
player.PlayStateChange -= Player_PlayStateChange;
if (player != null)
{
if(player.playState == WMPPlayState.wmppsPlaying)
{
player.Ctlcontrols.stop();
}
player.Dispose();
player = null;
}
}
catch (Exception ex)
{
playstatus?.Invoke(_projectName, _videoDescription, "释放播放资源失败:" + ex.Message);
}
}
#endregion
}
}
GitHub 加速计划 / opencv31 / opencv
77.35 K
55.71 K
下载
OpenCV: 开源计算机视觉库
最近提交(Master分支:1 个月前 )
08f7f13d
Move the gcc6 compatibility check to occur on a per-directory basis, … #26234
Proposed fix for #26233 https://github.com/opencv/opencv/issues/26233
### Pull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
1 天前
0f234209
Added buffer-based model loading to FaceRecognizerSF 2 天前
更多推荐
已为社区贡献2条内容
所有评论(0)