增加录像回放

main
chengyu 6 days ago
parent 20cf575b0c
commit 5c2ed7256a
  1. 118
      public/dahua/demo.html
  2. 2
      public/dahua/demo_api.html
  3. 2
      public/dahua/mobile.html
  4. 20
      src/views/vehicleMachinery/video-manage/api.js
  5. 96
      src/views/vehicleMachinery/video-manage/index.vue

@ -74,56 +74,50 @@
<!-- 中间内容部分 --> <!-- 中间内容部分 -->
<div class="container"> <div class="container">
<div class="real-container"> <div class="real-container">
实时预览:
<div id="ws-real-player" style="height: 500px; margin-bottom: 20px;"></div> <div id="ws-real-player" style="height: 500px; margin-bottom: 20px;"></div>
<div> <div>
<!-- <button type="button" class="btn btn-outline-primary" id="real-btn">实时预览</button> --> <button type="button" class="btn btn-outline-primary" id="real-btn">实时预览</button>
<!-- <button type="button" class="btn btn-outline-primary" id="close-real">关闭预览</button> --> <button type="button" class="btn btn-outline-primary" id="close-real">关闭预览</button>
<!-- <button type="button" class="btn btn-outline-primary" id="talk-btn">开始对讲</button> --> <!-- <button type="button" class="btn btn-outline-primary" id="talk-btn">开始对讲</button> -->
<!-- <button type="button" class="btn btn-outline-primary" id="talk-close-btn">关闭对讲</button> --> <!-- <button type="button" class="btn btn-outline-primary" id="talk-close-btn">关闭对讲</button> -->
</div> </div>
</div> </div>
<div class="record-container" style="display: none;"> <div class="record-container">
录像回放播放框 录像回放:
<div id="ws-record-player" style="height: 500px; margin-bottom: 20px;"></div> <div id="ws-record-player" style="height: 500px; margin-bottom: 20px;"></div>
<div class="has-success"> <div style="display: flex;">
<label class="form-label mt-4" for="record-ws">请输入websocket地址(必传,非接口返回,需自行拼接)(必传)</label> <!-- 日期 -->
<input id="record-ws" placeholder="wss://10.10.10.10:9320" type="text" class="form-control"> <input style="width:140px;" type="date" id="date" class="form-control" id="record-date" value="2024-09-17"/>
<div class="valid-feedback">可通过右侧填写平台信息, 自动拼接, 对讲和实时预览通用,录像回放端口默认 ws 是 9320 wss 是 9322</div> <button style="margin:0 5px" type="button" class="btn btn-outline-primary" id="record-btn">录像回放</button>
</div>
<div class="has-success">
<label class="form-label mt-4" for="record-rtsp">请输入录像回放 rtsp 地址(必传):</label>
<input id="record-rtsp" placeholder="rtsp://10.10.10.10:9320/playback/pu/1?token=1" type="text" class="form-control">
<div class="valid-feedback">获取rtsp地址<a href="https://open-icc.dahuatech.com/iccdoc/enterprisebase/5.0.15/wiki/admin/replay.html#rtsp%E4%BB%A5%E6%97%B6%E9%97%B4%E5%BD%A2%E5%BC%8F%E5%9B%9E%E6%94%BE%E5%BD%95%E5%83%8F">点击跳转</a>,先查询录像文件records, 在查询rtsp9(推荐时间形式回放), 注意流地址要拼接接口返回的token</div>
</div>
<div>
<fieldset>
<label class="form-label mt-4" for="record-channelId">请输入 通道ID(必传):</label>
<input id="record-channelId" class="form-control" type="text" placeholder="1000000$1$0$0">
</fieldset>
</div>
<div class="has-success">
<label for="record-records" class="form-label mt-4">请输入录像文件(必传, 不传不可拖拽)</label>
<textarea placeholder="默认传: []" class="form-control" id="record-records" rows="3">[]</textarea>
<div class="valid-feedback">records,获取录像文件接口<a target="_blank" href="https://open-icc.dahuatech.com/iccdoc/enterprisebase/5.0.15/wiki/admin/replay.html#%E6%9F%A5%E8%AF%A2%E6%99%AE%E9%80%9A%E5%BD%95%E5%83%8F%E4%BF%A1%E6%81%AF%E5%88%97%E8%A1%A8">点击跳转</a>, 有录像文件才能支持拖拽,传入接口返回的records数组</div>
</div>
<div>
<button type="button" class="btn btn-outline-primary" id="record-btn">录像回放</button>
<button type="button" class="btn btn-outline-primary" id="close-record">关闭录像</button> <button type="button" class="btn btn-outline-primary" id="close-record">关闭录像</button>
<button type="button" class="btn btn-outline-primary" id="close-record">录像下载接口</button> <!-- <button type="button" class="btn btn-outline-primary" id="close-record">录像下载接口</button> -->
</div> </div>
</div> </div>
</div> </div>
<script type="text/javascript" src="/dahua/lib/WSPlayer/PlaySDKInterface.js"></script> <script type="text/javascript" src="/dahua/lib/WSPlayer/PlaySDKInterface.js"></script>
<script type="text/javascript" src="/dahua/lib/WSPlayer/WSPlayer.js"></script> <script type="text/javascript" src="/dahua/lib/WSPlayer/WSPlayer.js"></script>
<script type="module"> <script type="module">
let cameraInfo = new URLSearchParams(window.location.search).get('cameraInfo') // record-date默认今日
try { document.getElementById('date').value = new Date().toISOString().split('T')[0];
cameraInfo = JSON.parse(cameraInfo) let cameraInfo;
} catch (error) { let recordInfo;
console.log('error :>> ', error); //接收消息
} window.addEventListener('message', function(event) {
console.log('channelCode :>> ', cameraInfo); console.log('接收到消息:', event.data);
if (event.data === 'reload') {
location.reload()
}else if (event.data.type ==='startVideo') {
cameraInfo = event.data.data;
playReal()
}
else if (event.data.type ==='playRecord') {
recordInfo = event.data.data;
playRecord()
}
});
function isMobile() { function isMobile() {
// 正则表达式匹配常见的移动端平台关键字 // 正则表达式匹配常见的移动端平台关键字
return /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec)/i.test(navigator.userAgent); return /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec)/i.test(navigator.userAgent);
@ -171,7 +165,7 @@
case 'initializationCompleted': case 'initializationCompleted':
console.log('初始化完成') console.log('初始化完成')
realPlayer.setPlayerAdapter("stretching") realPlayer.setPlayerAdapter("stretching")
playReal() // playReal()
// 初始化完成,可调用播放方法(适用于动态加载解码库) // 初始化完成,可调用播放方法(适用于动态加载解码库)
// 若回调未触发时就使用实时预览/录像回放,则无法播放。 // 若回调未触发时就使用实时预览/录像回放,则无法播放。
// 此时我们可以调用一个 // 此时我们可以调用一个
@ -272,7 +266,7 @@
maxNum: 4, /** 一个播放器上限能播放的路数,可根据实际情况设置,支持 1 4 9 16 25 **/ maxNum: 4, /** 一个播放器上限能播放的路数,可根据实际情况设置,支持 1 4 9 16 25 **/
num: 1, /** 初始化,页面显示的路数 **/ num: 1, /** 初始化,页面显示的路数 **/
showControl: true, /** 是否显示工具栏,默认显示 **/ showControl: true, /** 是否显示工具栏,默认显示 **/
prefixUrl: 'lib', /** 解码库所在位置的前缀 **/ prefixUrl: 'dahua/lib', /** 解码库所在位置的前缀 **/
receiveMessageFromWSPlayer: (methods, data, err) => { /* 回调函数,可以在以下回调函数里面做监听 */ receiveMessageFromWSPlayer: (methods, data, err) => { /* 回调函数,可以在以下回调函数里面做监听 */
switch(methods) { switch(methods) {
case 'initializationCompleted': case 'initializationCompleted':
@ -325,10 +319,21 @@
// 流地址方式播放录像回放 // 流地址方式播放录像回放
const playRecord = () => { const playRecord = () => {
let wsURL = getDom('#record-ws').value let records = recordInfo.records;
let rtspURL = getDom('#record-rtsp').value let wsURL = `ws://59.47.183.182:9320`
let channelId = getDom('#record-channelId').value let rtspURL = `${recordInfo.url}?token=${recordInfo.token}`;
let records = JSON.parse(getDom('#record-records').value) let channelId = recordInfo.channelId
console.log('params :>> ', {
wsURL,
rtspURL,
channelId: channelId, // 必传, 通道id
records: records || [], // 录像列表
selectIndex: 0, // 必传
playRecordByTime: true, // 默认不传,拖拽异常时请传入false
channelData: {
records: records || []
}, // 选传, 通道信息
});
recordPlayer.recordByUrl({ recordPlayer.recordByUrl({
wsURL, wsURL,
rtspURL, rtspURL,
@ -336,8 +341,8 @@
records: records || [], // 录像列表 records: records || [], // 录像列表
selectIndex: 0, // 必传 selectIndex: 0, // 必传
playRecordByTime: true, // 默认不传,拖拽异常时请传入false playRecordByTime: true, // 默认不传,拖拽异常时请传入false
// startTime, // 建议传,不传则会通过records数组中截取, 格式 // new Date().getTime() / 1000 【到秒时间戳】 // startTime: records[0].startTime, // 建议传,不传则会通过records数组中截取, 格式 // new Date().getTime() / 1000 【到秒时间戳】
// endTime, // 建议传,不传则会通过records数组中截取, 格式 // new Date().getTime() / 1000 【到秒时间戳】 // endTime: records[0].endTime, // 建议传,不传则会通过records数组中截取, 格式 // new Date().getTime() / 1000 【到秒时间戳】
// playerAdapter: opt.playerAdapter, // 选传, , selfAdaption 自适应 | stretching 拉伸 // playerAdapter: opt.playerAdapter, // 选传, , selfAdaption 自适应 | stretching 拉伸
channelData: { channelData: {
records: records || [] records: records || []
@ -351,17 +356,21 @@
}) })
getDom('#real-btn').addEventListener('click', () => { getDom('#real-btn').addEventListener('click', () => {
playReal() //向父窗口发送消息
}) window.parent.postMessage({type: 'startVideo'}, '*')
getDom('#talk-btn').addEventListener('click', () => { // playReal()
startTalk()
}) })
// getDom('#talk-btn').addEventListener('click', () => {
// startTalk()
// })
// 关闭对讲,此功能需调用关闭对讲接口 // 关闭对讲,此功能需调用关闭对讲接口
// getDom('#talk-close-btn').addEventListener('click', () => { // getDom('#talk-close-btn').addEventListener('click', () => {
// stopTalk() // stopTalk()
// }) // })
getDom('#record-btn').addEventListener('click', () => { getDom('#record-btn').addEventListener('click', () => {
playRecord() let date = getDom('#date').value;
window.parent.postMessage({type:'playRecord',date}, '*')
// playRecord()
}) })
getDom('#close-real').addEventListener('click', e => { getDom('#close-real').addEventListener('click', e => {
realPlayer && realPlayer.close(0) realPlayer && realPlayer.close(0)
@ -415,13 +424,12 @@
getDom('#notify').innerHTML = isHttp ? `注:当前是${window.location.protocol}服务, 如果你是集成在 https 项目中,请 启动 node https.cjs 进行查看` : `注:当前是${window.location.protocol}服务, 如果你是集成在 http 项目中,请 启动 node http.cjs 进行查看。 https服务,需要在大华的平台上配置https证书,否则无法拉流<a href="https://open-icc.dahuatech.com/iccdoc/openfaq/wiki/iccfaq/question/admin.html#WSPlayer%E6%8B%89%E6%B5%81wss%E9%85%8D%E7%BD%AE%EF%BC%9B%E8%A7%86%E9%A2%91%E5%BA%94%E7%94%A8"" target="_blank">跳转到证书说明</a>` getDom('#notify').innerHTML = isHttp ? `注:当前是${window.location.protocol}服务, 如果你是集成在 https 项目中,请 启动 node https.cjs 进行查看` : `注:当前是${window.location.protocol}服务, 如果你是集成在 http 项目中,请 启动 node http.cjs 进行查看。 https服务,需要在大华的平台上配置https证书,否则无法拉流<a href="https://open-icc.dahuatech.com/iccdoc/openfaq/wiki/iccfaq/question/admin.html#WSPlayer%E6%8B%89%E6%B5%81wss%E9%85%8D%E7%BD%AE%EF%BC%9B%E8%A7%86%E9%A2%91%E5%BA%94%E7%94%A8"" target="_blank">跳转到证书说明</a>`
getDom('#real_port_label').innerHTML = window.location.protocol + '实时预览拉流端口:' getDom('#real_port_label').innerHTML = window.location.protocol + '实时预览拉流端口:'
getDom('#record_port_label').innerHTML = window.location.protocol + '录像回放拉流端口:' getDom('#record_port_label').innerHTML = window.location.protocol + '录像回放拉流端口:'
getDom('#is-support-talk').innerHTML = isHttp ? '(当前为http协议,不支持对讲)' : '' // getDom('#is-support-talk').innerHTML = isHttp ? '(当前为http协议,不支持对讲)' : ''
if(isHttp) { // if(isHttp) {
getDom('#talk-rtsp').disabled = true // getDom('#talk-rtsp').disabled = true
getDom('#audioType').disabled = true // getDom('#audioType').disabled = true
getDom('#gbDevice').disabled = true // getDom('#gbDevice').disabled = true
} // }
console.log('object :>> ', object);
</script> </script>
</body> </body>

@ -402,7 +402,7 @@
maxNum: 25, /** 一个播放器上限能播放的路数,可根据实际情况设置,支持 1 4 9 16 25 **/ maxNum: 25, /** 一个播放器上限能播放的路数,可根据实际情况设置,支持 1 4 9 16 25 **/
num: 1, /** 初始化,页面显示的路数 **/ num: 1, /** 初始化,页面显示的路数 **/
showControl: true, /** 是否显示工具栏,默认显示 **/ showControl: true, /** 是否显示工具栏,默认显示 **/
prefixUrl: 'lib', /** 解码库所在位置的前缀 **/ prefixUrl: 'dahua/lib', /** 解码库所在位置的前缀 **/
useH265MSE: false, // 软解 useH265MSE: false, // 软解
receiveMessageFromWSPlayer: (methods, data, err) => { /* 回调函数,可以在以下回调函数里面做监听 */ receiveMessageFromWSPlayer: (methods, data, err) => { /* 回调函数,可以在以下回调函数里面做监听 */
switch (methods) { switch (methods) {

@ -360,7 +360,7 @@
maxNum: 25, /** 一个播放器上限能播放的路数,可根据实际情况设置,支持 1 4 9 16 25 **/ maxNum: 25, /** 一个播放器上限能播放的路数,可根据实际情况设置,支持 1 4 9 16 25 **/
num: 1, /** 初始化,页面显示的路数 **/ num: 1, /** 初始化,页面显示的路数 **/
showControl: true, /** 是否显示工具栏,默认显示 **/ showControl: true, /** 是否显示工具栏,默认显示 **/
prefixUrl: 'lib', /** 解码库所在位置的前缀 **/ prefixUrl: 'dahua/lib', /** 解码库所在位置的前缀 **/
isWebView: true, isWebView: true,
receiveMessageFromWSPlayer: (methods, data, err) => { /* 回调函数,可以在以下回调函数里面做监听 */ receiveMessageFromWSPlayer: (methods, data, err) => { /* 回调函数,可以在以下回调函数里面做监听 */
switch (methods) { switch (methods) {

@ -73,7 +73,7 @@ export const getDevicePage = (arg) => {
dataType: 'json', dataType: 'json',
}) })
} }
// 查询 // 查询实时流
export const getRtspUrl = (arg) => { export const getRtspUrl = (arg) => {
return axios.request({ return axios.request({
url: '/dahua/video/getRtspUrl', url: '/dahua/video/getRtspUrl',
@ -82,3 +82,21 @@ export const getRtspUrl = (arg) => {
dataType: 'json', dataType: 'json',
}) })
} }
// 查询录像:
export const getPlaybackByTimeRtspUrl = (arg) => {
return axios.request({
url: '/dahua/video/getPlaybackByTimeRtspUrl ',
method: 'post',
data: arg,
dataType: 'json',
})
}
// 查询录像记录:
export const getRegularVideoRecords = (arg) => {
return axios.request({
url: '/dahua/video/getRegularVideoRecords ',
method: 'post',
data: arg,
dataType: 'json',
})
}

@ -1,18 +1,28 @@
<script setup> <script setup>
import { reactive,onMounted } from 'vue' import { reactive,onMounted } from 'vue'
import { getRtspUrl,getDevicePage, vehicleList, vehicleAdd, vehicleUpdate, vehicleDelete, vehicleGetById, iproductionplanList } from './api' import { getRtspUrl,getDevicePage,getPlaybackByTimeRtspUrl,getRegularVideoRecords, vehicleList, vehicleAdd, vehicleUpdate, vehicleDelete, vehicleGetById, iproductionplanList } from './api'
import { baseModelOptions, baseFilterOptions } from './options' import { baseModelOptions, baseFilterOptions } from './options'
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus'
const state = reactive({ const state = reactive({
currentCameraIndex: 0, currentCameraIndex: 0,
cameraList: [], cameraList: [],
cameraInfo: null cameraInfo: null
}) })
const switchCamera = (index) => { const switchCamera = (index)=>{
state.cameraInfo = null;
state.currentCameraIndex = index; state.currentCameraIndex = index;
}
const startVideo = () => {
let index = state.currentCameraIndex;
let channelCode = state.cameraList[index].units && state.cameraList[index].units[0].channels[0].channelCode; let channelCode = state.cameraList[index].units && state.cameraList[index].units[0].channels[0].channelCode;
if(!channelCode){
ElMessage({
message: '设备异常',
type: 'error'
})
return;
}
getRtspUrl({ getRtspUrl({
data: { data: {
channelId: channelCode, channelId: channelCode,
@ -25,9 +35,71 @@ const switchCamera = (index) => {
url: res.data.url, url: res.data.url,
token: res.data.token token: res.data.token
} }
console.log(res) //
let video = document.getElementById('video')
video.contentWindow.postMessage({
type: 'startVideo',
data: {...state.cameraInfo}
}, '*')
}) })
} }
const playRecord = (date) => {
let index = state.currentCameraIndex;
let channelCode = state.cameraList[index].units && state.cameraList[index].units[0].channels[0].channelCode;
if(!channelCode){
ElMessage({
message: '设备异常',
type: 'error'
})
return;
}
//
let current = new Date(date);
let startTime = parseInt(current.setHours(0, 0, 0, 0)/1000);
let endTime = parseInt(current.setHours(23, 59, 59, 999)/1000);
getRegularVideoRecords({
data: {
channelId: channelCode,
recordSource: '1',
startTime,
endTime,
streamType: "1",
recordType: "1"
}
}).then(res=>{
console.log('res :>> ', res);
let records = res.data.records;
getPlaybackByTimeRtspUrl({
data: {
channelId: records[0].channelId,
recordSource: records[0].recordSource,
startTime: records[0].startTime,
endTime: records[0].endTime,
streamType: records[0].streamType,
recordType: records[0].recordType
}
}).then(res=>{
let recordInfo = {
records: records,
channelId: records[0].channelId,
url: res.data.url,
token: res.data.token,
startTime: records[0].startTime,
endTime: records[0].endTime,
streamType: records[0].streamType,
recordType: records[0].recordType
}
//
let video = document.getElementById('video')
console.log('video :>> ', video);
video.contentWindow.postMessage({
type: 'playRecord',
data: JSON.parse(JSON.stringify(recordInfo))
}, '*')
})
})
}
onMounted(() => { onMounted(() => {
getDevicePage({ getDevicePage({
"pageNum": 1, "pageNum": 1,
@ -37,6 +109,15 @@ onMounted(() => {
}).catch((err) => { }).catch((err) => {
}); });
//
window.addEventListener('message', (event) => {
console.log('event :>> ', event.data);
if (event.data.type === 'startVideo') {
startVideo();
}else if(event.data.type ==='playRecord'){
playRecord(event.data.date);
}
});
}) })
</script> </script>
@ -52,16 +133,13 @@ onMounted(() => {
:class="{ active: state.currentCameraIndex === index }" :class="{ active: state.currentCameraIndex === index }"
> >
{{ camera.deviceName }} {{ camera.deviceName }}
<el-tag style="scale: 0.8;position: absolute;top: -10px;right:-15px;border-radius: 20px;" :type="camera.isOnline == 1 ? 'success':'error'">{{ camera.isOnline == 1 ? '在线' : '离线' }}</el-tag> <el-tag style="scale: 0.8;position: absolute;top: -10px;right:-15px;border-radius: 20px;" :type="camera.isOnline == 1 ? 'success':'danger'">{{ camera.isOnline == 1 ? '在线' : '离线' }}</el-tag>
</span> </span>
</template> </template>
</div> </div>
<!-- 摄像头展示区域 --> <!-- 摄像头展示区域 -->
<div class="camera-display"> <div class="camera-display">
<iframe v-if="state.cameraInfo" id="video" :src="`/dahua/demo.html?cameraInfo=${JSON.stringify(state.cameraInfo)}`" style="width:100%;height:900px;" frameborder="0"></iframe> <iframe id="video" src="/dahua/demo.html" style="width:100%;height:900px;" frameborder="0"></iframe>
<div v-else>
该监控无法连接
</div>
<!-- <video <!-- <video
v-if="state.currentCameraIndex !== -1" v-if="state.currentCameraIndex !== -1"
:src="cameraList[state.currentCameraIndex].source" :src="cameraList[state.currentCameraIndex].source"

Loading…
Cancel
Save