调试样式好于默认示范的,用这个调试查看也行
默认支持ip账号密码频道替换后预览,rtsp方式未验证
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>海康RTSP视频流播放器</title>
<script src="./jquery-3.3.1.min.js"></script>
<script src="./webVideoCtrl.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c);
color: white;
min-height: 100vh;
padding: 20px;
overflow-x: hidden;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
text-align: center;
padding: 20px 0;
margin-bottom: 30px;
background: rgba(0, 0, 0, 0.3);
border-radius: 15px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.1);
}
h1 {
font-size: 2.5rem;
margin-bottom: 10px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
.subtitle {
font-size: 1.2rem;
opacity: 0.9;
margin-bottom: 10px;
}
.main-content {
display: flex;
flex-wrap: wrap;
gap: 30px;
}
.video-section {
flex: 1;
min-width: 300px;
background: rgba(0, 0, 0, 0.4);
border-radius: 15px;
padding: 20px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.control-section {
width: 300px;
background: rgba(0, 0, 0, 0.4);
border-radius: 15px;
padding: 20px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.video-container {
width: 100%;
height: 400px;
background: rgba(0, 0, 0, 0.6);
border-radius: 10px;
margin-bottom: 20px;
overflow: hidden;
position: relative;
border: 1px solid rgba(255, 255, 255, 0.1);
}
#divPlugin {
width: 100%;
height: 100%;
}
.loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.2rem;
text-align: center;
width: 100%;
}
.status {
background: rgba(0, 0, 0, 0.5);
padding: 15px;
border-radius: 10px;
margin-bottom: 20px;
font-size: 0.9rem;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.status h3 {
margin-bottom: 10px;
color: #4dabf7;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
padding-bottom: 5px;
}
.status-item {
display: flex;
margin-bottom: 8px;
}
.status-label {
width: 120px;
font-weight: bold;
}
.status-value {
flex: 1;
word-break: break-all;
}
.success {
color: #69db7c;
}
.error {
color: #ff6b6b;
}
.warning {
color: #ffd43b;
}
.control-panel {
margin-top: 20px;
}
.input-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
}
input {
width: 100%;
padding: 12px;
border-radius: 8px;
border: none;
background: rgba(255, 255, 255, 0.1);
color: white;
font-size: 1rem;
}
input:focus {
outline: none;
background: rgba(255, 255, 255, 0.15);
}
.btn-group {
display: flex;
gap: 10px;
margin-top: 20px;
}
button {
flex: 1;
padding: 12px;
border: none;
border-radius: 8px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
font-size: 1rem;
display: flex;
align-items: center;
justify-content: center;
gap: 5px;
}
.btn-play {
background: #40c057;
color: white;
}
.btn-stop {
background: #fa5252;
color: white;
}
.btn-login {
background: #4dabf7;
color: white;
}
.btn-play:hover {
background: #2f9e44;
}
.btn-stop:hover {
background: #e03131;
}
.btn-login:hover {
background: #339af0;
}
.info-box {
margin-top: 20px;
background: rgba(0, 0, 0, 0.5);
padding: 15px;
border-radius: 10px;
font-size: 0.9rem;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.info-box h3 {
margin-bottom: 10px;
color: #4dabf7;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
padding-bottom: 5px;
}
.info-box p {
line-height: 1.6;
margin-bottom: 10px;
padding-left: 10px;
position: relative;
}
.info-box p:before {
content: "•";
position: absolute;
left: 0;
color: #4dabf7;
}
.tabs {
display: flex;
margin-bottom: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.tab {
padding: 10px 20px;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s;
}
.tab.active {
border-bottom: 2px solid #4dabf7;
color: #4dabf7;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
@media (max-width: 768px) {
.main-content {
flex-direction: column;
}
.control-section {
width: 100%;
}
h1 {
font-size: 2rem;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>海康视频监控系统</h1>
<div class="subtitle">支持设备登录预览与RTSP流直接播放</div>
<div class="subtitle">基于海康WebVideoCtrl插件</div>
</header>
<div class="main-content">
<div class="video-section">
<div class="tabs">
<div class="tab active" data-tab="device">设备预览</div>
<div class="tab" data-tab="rtsp">RTSP流播放</div>
</div>
<div class="video-container">
<div id="divPlugin"></div>
<div class="loading" id="loadingMessage">初始化海康插件中...</div>
</div>
<div class="status">
<h3>系统状态</h3>
<div class="status-item">
<div class="status-label">插件状态:</div>
<div class="status-value" id="pluginStatus">初始化中...</div>
</div>
<div class="status-item">
<div class="status-label">设备状态:</div>
<div class="status-value" id="deviceStatus">未登录</div>
</div>
<div class="status-item">
<div class="status-label">播放状态:</div>
<div class="status-value" id="playStatus">未播放</div>
</div>
<div class="status-item">
<div class="status-label">RTSP地址:</div>
<div class="status-value" id="rtspUrl">未设置</div>
</div>
</div>
</div>
<div class="control-section">
<h2>控制面板</h2>
<div id="deviceTab" class="tab-content active">
<div class="input-group">
<label for="ip">设备IP:</label>
<input type="text" id="ip" value="192.168.2.253">
</div>
<div class="input-group">
<label for="port">端口:</label>
<input type="text" id="port" value="80">
</div>
<div class="input-group">
<label for="user">用户名:</label>
<input type="text" id="user" value="admin">
</div>
<div class="input-group">
<label for="pwd">密码:</label>
<input type="password" id="pwd" value="xx2">
</div>
<div class="btn-group">
<button class="btn-login" id="btnLogin">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0zm3.5 7.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5H11.5z"/>
</svg>
登录设备
</button>
</div>
<div class="btn-group">
<button class="btn-play" id="btnPreview">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="m11.596 8.697-6.363 3.692c-.54.313-1.233-.066-1.233-.697V4.308c0-.63.692-1.01 1.233-.696l6.363 3.692a.802.802 0 0 1 0 1.393z"/>
</svg>
开始预览
</button>
<button class="btn-stop" id="btnStopPreview">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm4 0a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5z"/>
</svg>
停止预览
</button>
</div>
</div>
<div id="rtspTab" class="tab-content">
<div class="input-group">
<label for="rtspInput">RTSP地址:</label>
<input type="text" id="rtspInput" placeholder="输入RTSP地址...">
</div>
<div class="btn-group">
<button class="btn-play" id="btnPlay">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="m11.596 8.697-6.363 3.692c-.54.313-1.233-.066-1.233-.697V4.308c0-.63.692-1.01 1.233-.696l6.363 3.692a.802.802 0 0 1 0 1.393z"/>
</svg>
播放RTSP
</button>
<button class="btn-stop" id="btnStop">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm4 0a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5z"/>
</svg>
停止播放
</button>
</div>
</div>
<div class="info-box">
<h3>使用说明</h3>
<p>设备预览模式需要登录海康设备</p>
<p>RTSP模式可直接播放RTSP流</p>
<p>切换模式前请先停止当前播放</p>
<p>示例RTSP地址: rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov</p>
</div>
</div>
</div>
</div>
<script>
// 应用状态
const state = {
pluginReady: false,
deviceLoggedIn: false,
previewPlaying: false,
rtspPlaying: false,
deviceId: "",
initInProgress: false,
channels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
split: 2,
controller: null
};
// 获取DOM元素
const elements = {
pluginStatus: document.getElementById('pluginStatus'),
deviceStatus: document.getElementById('deviceStatus'),
playStatus: document.getElementById('playStatus'),
rtspUrl: document.getElementById('rtspUrl'),
loadingMessage: document.getElementById('loadingMessage'),
btnLogin: document.getElementById('btnLogin'),
btnPreview: document.getElementById('btnPreview'),
btnStopPreview: document.getElementById('btnStopPreview'),
btnPlay: document.getElementById('btnPlay'),
btnStop: document.getElementById('btnStop'),
rtspInput: document.getElementById('rtspInput'),
ip: document.getElementById('ip'),
port: document.getElementById('port'),
user: document.getElementById('user'),
pwd: document.getElementById('pwd')
};
// 更新状态显示
function updateStatus() {
elements.pluginStatus.textContent = state.pluginReady ? "已就绪" : "未就绪";
elements.pluginStatus.className = state.pluginReady ? "status-value success" : "status-value error";
elements.deviceStatus.textContent = state.deviceLoggedIn ? "已登录" : "未登录";
elements.deviceStatus.className = state.deviceLoggedIn ? "status-value success" : "status-value";
if (state.previewPlaying) {
elements.playStatus.textContent = "设备预览中";
elements.playStatus.className = "status-value success";
} else if (state.rtspPlaying) {
elements.playStatus.textContent = "RTSP播放中";
elements.playStatus.className = "status-value success";
} else {
elements.playStatus.textContent = "未播放";
elements.playStatus.className = "status-value";
}
}
// 初始化插件
function initPlugin() {
if (state.initInProgress) return;
state.initInProgress = true;
if (!window.WebVideoCtrl) {
console.error("WebVideoCtrl 插件未加载");
elements.pluginStatus.textContent = "插件未加载";
elements.loadingMessage.textContent = "海康插件加载失败,请检查webVideoCtrl.js文件";
return;
}
// 初始化插件
WebVideoCtrl.I_InitPlugin({
bWndFull: true,
cbInitPluginComplete: function () {
console.log("插件初始化完成");
// 插入插件到容器中
WebVideoCtrl.I_InsertOBJECTPlugin("divPlugin")
.then(function () {
console.log("插件插入成功");
state.pluginReady = true;
elements.loadingMessage.style.display = 'none';
updateStatus();
// 尝试自动登录设备
setTimeout(() => {
loginDevice();
}, 500);
})
.catch(function (err) {
console.error("插件插入失败:", err);
elements.pluginStatus.textContent = "插件插入失败";
elements.loadingMessage.textContent = "插件插入失败: " + err;
});
}
});
}
// 登录设备
function loginDevice() {
if (!state.pluginReady) {
alert("插件未准备好,请稍后再试");
return;
}
const ip = elements.ip.value;
const port = parseInt(elements.port.value) || 80;
const user = elements.user.value;
const pwd = elements.pwd.value;
if (!ip || !user || !pwd) {
alert("请输入完整的设备登录信息");
return;
}
state.deviceId = `${ip}_${port}`;
elements.deviceStatus.textContent = "登录中...";
WebVideoCtrl.I_Login(
ip,
1, // 协议
port,
user,
pwd,
{
success: function (xmlDoc) {
if ($(xmlDoc).find("statusString").text() === "OK") {
state.deviceLoggedIn = true;
updateStatus();
alert("设备登录成功");
} else {
console.error("登录状态异常");
elements.deviceStatus.textContent = "登录失败";
}
},
error: function (err) {
console.error("登录失败:", err);
elements.deviceStatus.textContent = "登录失败";
alert("设备登录失败: " + err);
}
}
);
}
// 开始预览所有通道
function startPreviewAllChannels() {
if (!state.pluginReady || !state.deviceLoggedIn) {
alert("请确保插件已就绪且设备已登录");
return;
}
if (state.previewPlaying) return;
try {
// 停止所有播放
if (window.WebVideoCtrl && WebVideoCtrl.I_StopAllPlay) {
WebVideoCtrl.I_StopAllPlay();
}
// 设置画面分割
WebVideoCtrl.I_ChangeWndNum(state.split);
// 开始预览所有通道
state.channels.forEach(function (channel, index) {
playChannel(channel, index);
});
state.previewPlaying = true;
state.rtspPlaying = false;
updateStatus();
} catch (error) {
console.error("预览启动失败:", error);
alert("预览启动失败: " + error);
}
}
// 播放通道
function playChannel(iChannelID, wndIndex) {
if (!window.WebVideoCtrl || !WebVideoCtrl.I_StartRealPlay) {
console.error("WebVideoCtrl.I_StartRealPlay 方法未定义");
return;
}
WebVideoCtrl.I_StartRealPlay(state.deviceId, {
iWndIndex: wndIndex,
iChannelID: parseInt(iChannelID),
iStreamType: 2, // 主码流
bZeroChannel: false,
iPort: 554, // RTSP 端口
success: function () {
console.log("通道 " + iChannelID + " 预览启动成功");
},
error: function (err) {
console.error("通道 " + iChannelID + " 预览启动失败:", err);
}
});
}
// 停止预览
function stopPreview() {
if (window.WebVideoCtrl && WebVideoCtrl.I_StopAllPlay) {
WebVideoCtrl.I_StopAllPlay();
state.previewPlaying = false;
updateStatus();
}
}
// 播放RTSP流
function playRTSP() {
if (!state.pluginReady) {
alert("插件未准备好,请稍后再试");
return;
}
// 获取输入的RTSP地址
const rtsp = elements.rtspInput.value;
if (!rtsp || !rtsp.startsWith('rtsp://')) {
alert("请输入有效的RTSP地址");
return;
}
// 停止当前播放
if (window.WebVideoCtrl && WebVideoCtrl.I_StopAllPlay) {
WebVideoCtrl.I_StopAllPlay();
}
// 更新状态
elements.rtspUrl.textContent = rtsp;
state.previewPlaying = false;
state.rtspPlaying = true;
updateStatus();
try {
// 设置单窗口模式
WebVideoCtrl.I_ChangeWndNum(1);
// 使用RTSP流播放
WebVideoCtrl.I_StartRealPlayEx({
iWndIndex: 0,
url: rtsp,
success: function() {
console.log("RTSP播放成功");
},
error: function(err) {
console.error("RTSP播放失败:", err);
state.rtspPlaying = false;
elements.playStatus.textContent = "播放失败: " + err;
elements.playStatus.className = "status-value error";
alert("RTSP播放失败: " + err);
}
});
} catch (e) {
console.error("播放异常:", e);
state.rtspPlaying = false;
updateStatus();
alert("播放异常: " + e);
}
}
// 停止RTSP播放
function stopRTSP() {
if (window.WebVideoCtrl && WebVideoCtrl.I_StopAllPlay) {
WebVideoCtrl.I_StopAllPlay();
state.rtspPlaying = false;
updateStatus();
}
}
// 清理资源
function cleanup() {
if (window.WebVideoCtrl) {
try {
WebVideoCtrl.I_StopAllPlay();
WebVideoCtrl.I_DestroyPlugin();
} catch (e) {
console.error("清理资源时出错:", e);
}
}
}
// 页面加载完成后初始化
document.addEventListener("DOMContentLoaded", function() {
// 初始化插件
initPlugin();
// 绑定按钮事件
elements.btnLogin.addEventListener('click', loginDevice);
elements.btnPreview.addEventListener('click', startPreviewAllChannels);
elements.btnStopPreview.addEventListener('click', stopPreview);
elements.btnPlay.addEventListener('click', playRTSP);
elements.btnStop.addEventListener('click', stopRTSP);
// 绑定标签切换事件
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', function() {
// 移除所有active类
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
// 添加active类到当前标签
this.classList.add('active');
const tabId = this.getAttribute('data-tab') + 'Tab';
document.getElementById(tabId).classList.add('active');
});
});
// 页面关闭前清理资源
window.addEventListener('beforeunload', cleanup);
// 初始状态更新
updateStatus();
});
</script>
</body>
</html>