|
@@ -45,10 +45,33 @@
|
|
:message="videoError"
|
|
:message="videoError"
|
|
:hasAudio="hasAudio"
|
|
:hasAudio="hasAudio"
|
|
style="max-height: 100%"
|
|
style="max-height: 100%"
|
|
|
|
+ v-if="activePlayer === 'jessibuca'"
|
|
fluent autoplay live></player>
|
|
fluent autoplay live></player>
|
|
|
|
+ <rtc-player
|
|
|
|
+ v-if="activePlayer === 'webrtc'"
|
|
|
|
+ ref="webrtc"
|
|
|
|
+ @eventCallback="webrtcPlayEventHandle"
|
|
|
|
+ :videoUrl="videoUrl"
|
|
|
|
+ :error="videoError"
|
|
|
|
+ :message="videoError"
|
|
|
|
+ :hasAudio="hasAudio"
|
|
|
|
+ />
|
|
</div>
|
|
</div>
|
|
<div class="player-option-box">
|
|
<div class="player-option-box">
|
|
<div>
|
|
<div>
|
|
|
|
+ <el-radio-group
|
|
|
|
+ v-model="activePlayer"
|
|
|
|
+ size="medium"
|
|
|
|
+ @input="playerChangeHandle"
|
|
|
|
+ >
|
|
|
|
+ <el-radio-button
|
|
|
|
+ v-for="(item,i) in playerList"
|
|
|
|
+ :key="item.key + i"
|
|
|
|
+ :label="item.key">
|
|
|
|
+ {{ item.text }}
|
|
|
|
+ </el-radio-button>
|
|
|
|
+ </el-radio-group>
|
|
|
|
+
|
|
<el-button-group>
|
|
<el-button-group>
|
|
<el-time-picker
|
|
<el-time-picker
|
|
size="mini"
|
|
size="mini"
|
|
@@ -118,14 +141,55 @@ import player from './common/jessibuca.vue'
|
|
import moment from 'moment'
|
|
import moment from 'moment'
|
|
import recordDownload from './dialog/recordDownload.vue'
|
|
import recordDownload from './dialog/recordDownload.vue'
|
|
import handle from "@/until/handle";
|
|
import handle from "@/until/handle";
|
|
|
|
+import RtcPlayer from "@components/dialog/rtcPlayer.vue";
|
|
|
|
+import mpegTsVideo from "@components/common/mpegtsVideo.vue";
|
|
|
|
+import h265WebJessibuca from "@components/common/h265webJessibuca.vue";
|
|
|
|
+import H265WebJs from "@components/common/h265webJs.vue";
|
|
|
|
+import {webrtcEvent} from "@/map/eventMap";
|
|
|
|
+import {strFill} from "@/until/str";
|
|
|
|
+
|
|
|
|
+const playList = [
|
|
|
|
+ {
|
|
|
|
+ key: "jessibuca",
|
|
|
|
+ text: "265 jessibuca播放器",
|
|
|
|
+ description: "用于播放265格式视频",
|
|
|
|
+ support: ['h264', 'h265'],
|
|
|
|
+ audioSupport: ['aac'],
|
|
|
|
+ disable: false
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ key: "webrtc",
|
|
|
|
+ text: "webrtc 播放器",
|
|
|
|
+ description: "延迟较低的播放器,需要浏览器支持",
|
|
|
|
+ support: ['h264'],
|
|
|
|
+ audioSupport: ['opus', 'pcma', 'pcmu'],
|
|
|
|
+ disable: false
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+];
|
|
|
|
+const playerProtocol = {
|
|
|
|
+ webrtc: ["rtc", "rtcs"],
|
|
|
|
+ jessibuca: ["ws_flv", "wss_flv"],
|
|
|
|
+};
|
|
|
|
+let playTimeSliderMarks = {};
|
|
|
|
+for (let i = 0 ; i < 24 ; i++)
|
|
|
|
+{
|
|
|
|
+ playTimeSliderMarks[i * 3600] = `${strFill(i, 2)}:00`;
|
|
|
|
+}
|
|
|
|
|
|
export default {
|
|
export default {
|
|
name: 'app',
|
|
name: 'app',
|
|
components: {
|
|
components: {
|
|
|
|
+ H265WebJs, h265WebJessibuca, mpegTsVideo,
|
|
|
|
+ RtcPlayer,
|
|
uiHeader, player, recordDownload
|
|
uiHeader, player, recordDownload
|
|
},
|
|
},
|
|
data() {
|
|
data() {
|
|
return {
|
|
return {
|
|
|
|
+ activePlayer: '',
|
|
|
|
+ playerList: playList,
|
|
|
|
+ fps: 15,
|
|
deviceId: this.$route.params.deviceId,
|
|
deviceId: this.$route.params.deviceId,
|
|
channelId: this.$route.params.channelId,
|
|
channelId: this.$route.params.channelId,
|
|
recordsLoading: false,
|
|
recordsLoading: false,
|
|
@@ -159,33 +223,7 @@ export default {
|
|
timeRange: null,
|
|
timeRange: null,
|
|
startTime: null,
|
|
startTime: null,
|
|
endTime: null,
|
|
endTime: null,
|
|
- playTimeSliderMarks: {
|
|
|
|
- 0: "00:00",
|
|
|
|
- 3600: "01:00",
|
|
|
|
- 7200: "02:00",
|
|
|
|
- 10800: "03:00",
|
|
|
|
- 14400: "04:00",
|
|
|
|
- 18000: "05:00",
|
|
|
|
- 21600: "06:00",
|
|
|
|
- 25200: "07:00",
|
|
|
|
- 28800: "08:00",
|
|
|
|
- 32400: "09:00",
|
|
|
|
- 36000: "10:00",
|
|
|
|
- 39600: "11:00",
|
|
|
|
- 43200: "12:00",
|
|
|
|
- 46800: "13:00",
|
|
|
|
- 50400: "14:00",
|
|
|
|
- 54000: "15:00",
|
|
|
|
- 57600: "16:00",
|
|
|
|
- 61200: "17:00",
|
|
|
|
- 64800: "18:00",
|
|
|
|
- 68400: "19:00",
|
|
|
|
- 72000: "20:00",
|
|
|
|
- 75600: "21:00",
|
|
|
|
- 79200: "22:00",
|
|
|
|
- 82800: "23:00",
|
|
|
|
- 86400: "24:00",
|
|
|
|
- },
|
|
|
|
|
|
+ playTimeSliderMarks: playTimeSliderMarks,
|
|
extList: [],
|
|
extList: [],
|
|
filterExt: null,
|
|
filterExt: null,
|
|
};
|
|
};
|
|
@@ -209,6 +247,9 @@ export default {
|
|
this.playerStyle["height"] = this.winHeight + "px";
|
|
this.playerStyle["height"] = this.winHeight + "px";
|
|
this.chooseDate = moment().format('YYYY-MM-DD')
|
|
this.chooseDate = moment().format('YYYY-MM-DD')
|
|
this.dateChange();
|
|
this.dateChange();
|
|
|
|
+ if(!this.activePlayer){
|
|
|
|
+ this.activePlayer = this.playerList[0].key;
|
|
|
|
+ }
|
|
},
|
|
},
|
|
beforeDestroy() {
|
|
beforeDestroy() {
|
|
console.log('准备销毁页面')
|
|
console.log('准备销毁页面')
|
|
@@ -228,7 +269,7 @@ export default {
|
|
|
|
|
|
this.setTime(this.chooseDate + " 00:00:00", this.chooseDate + " 23:59:59");
|
|
this.setTime(this.chooseDate + " 00:00:00", this.chooseDate + " 23:59:59");
|
|
this.recordsLoading = true;
|
|
this.recordsLoading = true;
|
|
- this.detailFiles = [];
|
|
|
|
|
|
+ // this.detailFiles = [];
|
|
let url = '/api/gb_record/query/'
|
|
let url = '/api/gb_record/query/'
|
|
url += `${this.deviceId}/${this.channelId}`;
|
|
url += `${this.deviceId}/${this.channelId}`;
|
|
url += `?startTime=${this.startTime}`;
|
|
url += `?startTime=${this.startTime}`;
|
|
@@ -318,14 +359,19 @@ export default {
|
|
method: 'get',
|
|
method: 'get',
|
|
url: '/api/playback/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.startTime + '&endTime=' +
|
|
url: '/api/playback/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.startTime + '&endTime=' +
|
|
this.endTime
|
|
this.endTime
|
|
- }).then((res) => {
|
|
|
|
|
|
+ }).then(async (res) => {
|
|
if (res.data.code === 0) {
|
|
if (res.data.code === 0) {
|
|
this.streamInfo = res.data.data;
|
|
this.streamInfo = res.data.data;
|
|
|
|
+ await this.queryMediaInfo(true);
|
|
|
|
+ console.log(`streamInfo`);
|
|
|
|
+ console.log(this.streamInfo);
|
|
this.app = this.streamInfo.app;
|
|
this.app = this.streamInfo.app;
|
|
this.streamId = this.streamInfo.stream;
|
|
this.streamId = this.streamInfo.stream;
|
|
this.mediaServerId = this.streamInfo.mediaServerId;
|
|
this.mediaServerId = this.streamInfo.mediaServerId;
|
|
this.ssrc = this.streamInfo.ssrc;
|
|
this.ssrc = this.streamInfo.ssrc;
|
|
- this.videoUrl = this.getUrlByStreamInfo();
|
|
|
|
|
|
+ // 根据tracks 选择播放器
|
|
|
|
+
|
|
|
|
+ this.videoUrl = this.getUrlByStreamInfo(this.streamInfo);
|
|
} else {
|
|
} else {
|
|
this.$message({
|
|
this.$message({
|
|
showClose: true,
|
|
showClose: true,
|
|
@@ -426,14 +472,30 @@ export default {
|
|
let differenceTime = timeForFile[0].getTime() - new Date(this.chooseDate + " 00:00:00").getTime()
|
|
let differenceTime = timeForFile[0].getTime() - new Date(this.chooseDate + " 00:00:00").getTime()
|
|
return parseFloat((differenceTime - this.sliderMIn * 1000) / ((this.sliderMax - this.sliderMIn) * 1000)) * 100;
|
|
return parseFloat((differenceTime - this.sliderMIn * 1000) / ((this.sliderMax - this.sliderMIn) * 1000)) * 100;
|
|
},
|
|
},
|
|
- getUrlByStreamInfo() {
|
|
|
|
|
|
+ getUrlByStreamInfo(streamInfo) {
|
|
|
|
+ if (this.enableDebug) {
|
|
|
|
+ console.log("获取基础拉流地址");
|
|
|
|
+ console.log(streamInfo);
|
|
|
|
+ }
|
|
|
|
+ if (!streamInfo) {
|
|
|
|
+ return '';
|
|
|
|
+ }
|
|
|
|
+ if(!this.activePlayer){
|
|
|
|
+ this.activePlayer = this.playerList[0].key;
|
|
|
|
+ }
|
|
|
|
+ let playerData = playerProtocol[this.activePlayer];
|
|
if (location.protocol === "https:") {
|
|
if (location.protocol === "https:") {
|
|
- this.videoUrl = this.streamInfo["wss_flv"]
|
|
|
|
|
|
+ this.videoUrl = streamInfo[playerData[1]]
|
|
} else {
|
|
} else {
|
|
- this.videoUrl = this.streamInfo["ws_flv"]
|
|
|
|
|
|
+ this.videoUrl = streamInfo[playerData[0]]
|
|
}
|
|
}
|
|
- return this.videoUrl;
|
|
|
|
|
|
+ if (this.enableDebug) {
|
|
|
|
+ console.log(`播放url: ${this.videoUrl}`);
|
|
|
|
+ }
|
|
|
|
+ console.log(`播放url: ${this.videoUrl}`);
|
|
|
|
+ console.log(this.videoUrl)
|
|
|
|
|
|
|
|
+ return this.videoUrl;
|
|
},
|
|
},
|
|
timePickerChange: function (val) {
|
|
timePickerChange: function (val) {
|
|
this.setTime(val[0], val[1])
|
|
this.setTime(val[0], val[1])
|
|
@@ -504,7 +566,144 @@ export default {
|
|
this.detailFiles = this.detailFiles;
|
|
this.detailFiles = this.detailFiles;
|
|
}
|
|
}
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ },
|
|
|
|
+ findPlayer(videoType, audioType)
|
|
|
|
+ {
|
|
|
|
+ // 先尝试寻找到所有支持对应协议的播放器
|
|
|
|
+ let arr = this.playerList.filter(item => {
|
|
|
|
+ return item.support.includes(videoType)
|
|
|
|
+ })
|
|
|
|
+ console.log(arr);
|
|
|
|
+ console.log(arr);
|
|
|
|
+ console.log(arr);
|
|
|
|
+ console.log(arr);
|
|
|
|
+ // 从中找到同时支持
|
|
|
|
+ let res_player = arr.find(item => {
|
|
|
|
+ // 判断播放器是否同时支持 视频与音频
|
|
|
|
+ return item.audioSupport.includes(audioType);
|
|
|
|
+ })
|
|
|
|
+ if (!res_player)
|
|
|
|
+ {
|
|
|
|
+ res_player = arr[0]
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return res_player;
|
|
|
|
+ },
|
|
|
|
+ async queryMediaInfo(isFirst) {
|
|
|
|
+ let info = this.streamInfo
|
|
|
|
+ let url = `/zlm/${info.mediaServerId}/index/api/getMediaInfo`
|
|
|
|
+ url += `?vhost=__defaultVhost__`
|
|
|
|
+ url += `&schema=rtsp`
|
|
|
|
+ url += `&app=${info.app}`
|
|
|
|
+ url += `&stream=${info.stream}`
|
|
|
|
+ url += this.shareCode ? `&shareCode=${this.shareCode}` : ''
|
|
|
|
+ let [err, res] = await handle(this.$axios.axios({
|
|
|
|
+ method: 'get',
|
|
|
|
+ url: url
|
|
|
|
+ }));
|
|
|
|
+
|
|
|
|
+ if (err) {
|
|
|
|
+ console.error(err);
|
|
|
|
+ this.$message.warning("流媒体信息获取失败")
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ console.log(res);
|
|
|
|
+ if (res.data.code !== 0) {
|
|
|
|
+ this.tracksNotLoaded = true;
|
|
|
|
+ this.$message({
|
|
|
|
+ showClose: true,
|
|
|
|
+ message: '获取编码信息失败,',
|
|
|
|
+ type: 'warning'
|
|
|
|
+ });
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ if (res.data.tracks) {
|
|
|
|
+ this.tracks = res.data.tracks;
|
|
|
|
+ if (this.tracks) {
|
|
|
|
+ console.log(`---------tracks----------`)
|
|
|
|
+ // 根据我们摄像头默认fps值来进行配置播放器
|
|
|
|
+ let fps = 15;
|
|
|
|
+ let isH265 = false;
|
|
|
|
+ let isPCMAudio = false;
|
|
|
|
+ for (const tracksElement of this.tracks) {
|
|
|
|
+ console.log(tracksElement);
|
|
|
|
+ if (tracksElement.fps) {
|
|
|
|
+ fps = tracksElement.fps;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ console.log(`${tracksElement.codec_id_name}:code ${tracksElement.codec_id_name ? 'is h265' : 'not h265'}`)
|
|
|
|
+ if (tracksElement.codec_id_name && tracksElement.codec_id_name.includes("265")) {
|
|
|
|
+ isH265 = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (tracksElement.codec_id_name && tracksElement.codec_id_name.includes("PCM"))
|
|
|
|
+ {
|
|
|
|
+ isPCMAudio = true;
|
|
|
|
+ // console.log("audio")
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ this.fps = fps;
|
|
|
|
+ if (isFirst) {
|
|
|
|
+ // g711 264
|
|
|
|
+ // g711 265
|
|
|
|
+ let _Player = this.findPlayer(isH265?'h265':'h264',
|
|
|
|
+ isPCMAudio?'pcma':'aac');
|
|
|
|
+ if (_Player) {
|
|
|
|
+ console.log(`切换为${_Player.key}播放器`);
|
|
|
|
+ this.activePlayer = _Player.key;
|
|
|
|
+ } else {
|
|
|
|
+ console.log("使用默认播放器");
|
|
|
|
+ this.activePlayer = playList[0].key;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ console.log('没有编码信息');
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ playerChangeHandle() {
|
|
|
|
+ console.log('切换播放器');
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
+ // todo 切换url
|
|
|
|
+ this.videoUrl = this.getUrlByStreamInfo(this.streamInfo);
|
|
|
|
+ })
|
|
|
|
+ },
|
|
|
|
+ webrtcPlayEventHandle(type, e) {
|
|
|
|
+ // 添加同类型防抖
|
|
|
|
+ if (this.enableDebug) {
|
|
|
|
+ console.log('playEventHandle');
|
|
|
|
+ console.log(type, e);
|
|
|
|
+ }
|
|
|
|
+ if (this.playEventHandleTimer) {
|
|
|
|
+ clearTimeout(this.playEventHandleTimer);
|
|
|
|
+ }
|
|
|
|
+ this.playEventHandleTimer = setTimeout(() => {
|
|
|
|
+ this.webrtcEventExecute(type, e);
|
|
|
|
+ }, 300);
|
|
|
|
+ },
|
|
|
|
+ webrtcEventExecute(type, e) {
|
|
|
|
+ // 防抖,防止多次触发
|
|
|
|
+ if (type === webrtcEvent.apiFail.code) {
|
|
|
|
+ this.$notify.error({
|
|
|
|
+ title: 'ZLM连接失败',
|
|
|
|
+ dangerouslyUseHTMLString: true,
|
|
|
|
+ message: `<span>连接zlm服务失败${e.message}</span> <br/>
|
|
|
|
+ <a href="${this.videoUrl}" target="_blank"><span>手动访问</span></a>`,
|
|
|
|
+ duration: 0
|
|
|
|
+ });
|
|
|
|
+ this.videoError = true;
|
|
|
|
+ this.videoUrl = '';
|
|
|
|
+
|
|
|
|
+ } else if (type === webrtcEvent.played.code) {
|
|
|
|
+ this.$message.success('播放成功');
|
|
|
|
+ } else if (type === webrtcEvent.sdpFail.code) {
|
|
|
|
+ console.log(e);
|
|
|
|
+ this.$notify.error({
|
|
|
|
+ title: 'sdp 交互失败',
|
|
|
|
+ duration: 4500
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ },
|
|
}
|
|
}
|
|
};
|
|
};
|
|
</script>
|
|
</script>
|