parent
04af6de363
commit
36cd31d69d
33
README.md
33
README.md
|
@ -16,24 +16,25 @@ WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的网络视频平台
|
||||||
![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_20201012_151606.png)
|
![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_20201012_151606.png)
|
||||||
|
|
||||||
# 原版特性:
|
# 原版特性:
|
||||||
1. 视频预览
|
1. 视频预览;
|
||||||
2. 云台控制(方向、缩放控制)
|
2. 云台控制(方向、缩放控制);
|
||||||
3. 视频设备信息同步
|
3. 视频设备信息同步;
|
||||||
4. 离在线监控
|
4. 离在线监控;
|
||||||
5. 录像查询与回放(基于NVR\DVR,暂不支持快进、seek操作)
|
5. 录像查询与回放(基于NVR\DVR,暂不支持快进、seek操作);
|
||||||
6. 无人观看自动断流
|
6. 无人观看自动断流;
|
||||||
|
7. 支持UDP和TCP两种国标信令传输模式;
|
||||||
|
|
||||||
# 新支持特性
|
# 新支持特性
|
||||||
1. 集成web界面, 不需要单独部署前端服务, 直接利用wvp内置文件服务部署.
|
1. 集成web界面, 不需要单独部署前端服务, 直接利用wvp内置文件服务部署, 随wvp一起部署;
|
||||||
2. 支持平台接入, 针对大平台大量设备的情况进行优化.
|
2. 支持平台接入, 针对大平台大量设备的情况进行优化;
|
||||||
3. 支持检索,通道筛选.
|
3. 支持检索,通道筛选;
|
||||||
4. 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题.
|
4. 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题;
|
||||||
5. 支持启用udp多端口模式, 提高udp模式下媒体传输性能.
|
5. 支持启用udp多端口模式, 提高udp模式下媒体传输性能;
|
||||||
6. 支持通道是否含有音频的设置
|
6. 支持通道是否含有音频的设置;
|
||||||
7. 支持通道子目录查询
|
7. 支持通道子目录查询;
|
||||||
8. 支持udp/tcp,两种模式传输视频流
|
8. 支持udp/tcp国标流传输模式;
|
||||||
|
9. 支持直接输出RTSP、RTMP、HTTP-FLV、Websocket-FLV、HLS多种协议流地址
|
||||||
|
10.
|
||||||
# 待实现:
|
# 待实现:
|
||||||
上级级联
|
上级级联
|
||||||
推流列表
|
推流列表
|
||||||
|
|
|
@ -538,7 +538,7 @@ public class SIPCommander implements ISIPCommander {
|
||||||
recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
|
recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
|
||||||
recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n");
|
recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n");
|
||||||
recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n");
|
recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n");
|
||||||
recordInfoXml.append("<Secrecy>0</Secrecy>\\r\n");
|
recordInfoXml.append("<Secrecy>0</Secrecy>\r\n");
|
||||||
// 大华NVR要求必须增加一个值为all的文本元素节点Type
|
// 大华NVR要求必须增加一个值为all的文本元素节点Type
|
||||||
recordInfoXml.append("<Type>all</Type>\r\n");
|
recordInfoXml.append("<Type>all</Type>\r\n");
|
||||||
recordInfoXml.append("</Query>\r\n");
|
recordInfoXml.append("</Query>\r\n");
|
||||||
|
|
|
@ -140,8 +140,6 @@ public class ZLMHttpHookListener {
|
||||||
streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtmpPort(), streamId));
|
streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtmpPort(), streamId));
|
||||||
streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId));
|
streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId));
|
||||||
streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtspPort(), streamId));
|
streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtspPort(), streamId));
|
||||||
|
|
||||||
|
|
||||||
storager.startPlay(streamInfo);
|
storager.startPlay(streamInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,15 @@ public class ZLMRESTfulUtils {
|
||||||
return sendPost("getMediaList",param);
|
return sendPost("getMediaList",param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JSONObject getMediaInfo(String app, String schema, String stream){
|
||||||
|
Map<String, Object> param = new HashMap<>();
|
||||||
|
param.put("app",app);
|
||||||
|
param.put("schema",schema);
|
||||||
|
param.put("stream",stream);
|
||||||
|
param.put("vhost","__defaultVhost__");
|
||||||
|
return sendPost("getMediaInfo",param);
|
||||||
|
}
|
||||||
|
|
||||||
public JSONObject getRtpInfo(String stream_id){
|
public JSONObject getRtpInfo(String stream_id){
|
||||||
Map<String, Object> param = new HashMap<>();
|
Map<String, Object> param = new HashMap<>();
|
||||||
param.put("stream_id",stream_id);
|
param.put("stream_id",stream_id);
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class ZLMUtils {
|
||||||
System.out.println(jsonObject.toJSONString());
|
System.out.println(jsonObject.toJSONString());
|
||||||
return newPort;
|
return newPort;
|
||||||
}else {
|
}else {
|
||||||
return getNewRTPPort(streamId);
|
return getNewRTPPort(ssrc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -165,7 +165,7 @@ public class VideoManagerJdbcStoragerImpl implements IVideoManagerStorager {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateCatch() {
|
public void updateCatch() {
|
||||||
|
System.out.println("##################");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -64,18 +64,25 @@ public class PlayController {
|
||||||
|
|
||||||
while (lockFlag) {
|
while (lockFlag) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (System.currentTimeMillis() - startTime > 15 * 1000) {
|
if (System.currentTimeMillis() - startTime > 15 * 1000) {
|
||||||
storager.stopPlay(streamInfo);
|
storager.stopPlay(streamInfo);
|
||||||
return new ResponseEntity<String>("timeout",HttpStatus.OK);
|
return new ResponseEntity<String>("timeout",HttpStatus.OK);
|
||||||
}else {
|
}else {
|
||||||
|
streamInfo = storager.queryPlayByDevice(deviceId, channelId);
|
||||||
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
|
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
|
||||||
if (rtpInfo == null || !rtpInfo.getBoolean("exist") || storager.queryPlayByDevice(deviceId, channelId).getFlv() == null){
|
if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo.getFlv() != null){
|
||||||
|
JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId);
|
||||||
|
if (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")) {
|
||||||
|
lockFlag = false;
|
||||||
|
JSONArray tracks = mediaInfo.getJSONArray("tracks");
|
||||||
|
streamInfo.setTracks(tracks);
|
||||||
|
storager.startPlay(streamInfo);
|
||||||
|
}else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}else {
|
||||||
Thread.sleep(2000);
|
Thread.sleep(2000);
|
||||||
continue;
|
continue;
|
||||||
}else {
|
|
||||||
lockFlag = false;
|
|
||||||
streamInfo = storager.queryPlay(streamInfo);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
|
|
|
@ -54,12 +54,15 @@
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="ptztypeText" label="云台类型">
|
<el-table-column prop="ptztypeText" label="云台类型">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="240" align="center" fixed="right">
|
<el-table-column label="操作" width="280" align="center" fixed="right">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
|
<el-button-group>
|
||||||
<el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button>
|
<el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button>
|
||||||
<el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="scope.row.play" @click="stopDevicePush(scope.row)">停止</el-button>
|
<el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="scope.row.play" @click="stopDevicePush(scope.row)">停止</el-button>
|
||||||
<el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.parental == 1" @click="changeSubchannel(scope.row)">查看子目录</el-button>
|
<el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.parental == 1" @click="changeSubchannel(scope.row)">查看</el-button>
|
||||||
|
<!-- <el-button size="mini" icon="el-icon-video-camera" type="primary" >设备录象</el-button>-->
|
||||||
<!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> -->
|
<!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> -->
|
||||||
|
</el-button-group>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="devicePlayer">
|
<div id="devicePlayer">
|
||||||
<el-dialog title="视频播放" top="0" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()">
|
<el-dialog title="视频播放" top="0" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()">
|
||||||
<LivePlayer v-if="showVideoDialog && hasaudio" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" hasaudio fluent autoplay live ></LivePlayer>
|
<LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :hasaudio="hasaudio" fluent autoplay live ></LivePlayer>
|
||||||
<LivePlayer v-if="showVideoDialog && !hasaudio" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" fluent autoplay live ></LivePlayer>
|
|
||||||
<div id="shared" style="text-align: right; margin-top: 1rem;">
|
<div id="shared" style="text-align: right; margin-top: 1rem;">
|
||||||
<el-tabs v-model="tabActiveName">
|
<el-tabs v-model="tabActiveName">
|
||||||
<el-tab-pane label="媒体流信息" name="media">
|
<el-tab-pane label="媒体流信息" name="media">
|
||||||
|
@ -123,20 +122,17 @@
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
play: function(streamInfo, deviceId, channelId, hasAudio) {
|
play: function(streamInfo, deviceId, channelId, hasAudio) {
|
||||||
// this.hasaudio = hasAudio;
|
|
||||||
if (!hasAudio) { // hasaudio == false时设置播放器hasaudio false, 否则不设置
|
|
||||||
this.hasaudio = hasAudio;
|
this.hasaudio = hasAudio;
|
||||||
}
|
|
||||||
// 根据媒体流信息二次判断
|
// 根据媒体流信息二次判断
|
||||||
// if( this.hasaudio && !!streamInfo.tracks && streamInfo.tracks.length > 0) {
|
if( this.hasaudio && !!streamInfo.tracks && streamInfo.tracks.length > 0) {
|
||||||
// var realHasAudio = false;
|
var realHasAudio = false;
|
||||||
// for (let i = 0; i < streamInfo.tracks; i++) {
|
for (let i = 0; i < streamInfo.tracks; i++) {
|
||||||
// if (streamInfo.tracks[i].codec_type == 1) { // 判断为音频
|
if (streamInfo.tracks[i].codec_type == 1) { // 判断为音频
|
||||||
// realHasAudio = true;
|
realHasAudio = true;
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// this.hasaudio = realHasAudio && this.hasaudio;
|
this.hasaudio = realHasAudio && this.hasaudio;
|
||||||
// }
|
}
|
||||||
this.ssrc = streamInfo.ssrc;
|
this.ssrc = streamInfo.ssrc;
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
this.channelId = channelId;
|
this.channelId = channelId;
|
||||||
|
|
Loading…
Reference in New Issue