修复ui开启音频无法播放的bug
修复可能导致录象查看的bug
修复开启openRTPServer时的bug
9个文件已修改
110 ■■■■■ 已修改文件
README.md 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/channelList.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/gb28181/devicePlayer.vue 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md
@@ -1,6 +1,6 @@
# wvp
WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的网络视频平台,负责实现核心信令与设备管理后台部分,支持NAT穿透,支持海康、大华、宇视等品牌的IPC、NVR、DVR接入。   
流媒体服务基于ZLMediaKit-https://github.com/xiongziliang/ZLMediaKit
流媒体服务基于ZLMediaKit-https://github.com/xiongziliang/ZLMediaKit
前段页面基于MediaServerUI进行修改.
# 应用场景:
@@ -16,24 +16,25 @@
![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_20201012_151606.png)
# 原版特性:
1. 视频预览
2. 云台控制(方向、缩放控制)
3. 视频设备信息同步
4. 离在线监控
5. 录像查询与回放(基于NVR\DVR,暂不支持快进、seek操作)
6. 无人观看自动断流
1. 视频预览;
2. 云台控制(方向、缩放控制);
3. 视频设备信息同步;
4. 离在线监控;
5. 录像查询与回放(基于NVR\DVR,暂不支持快进、seek操作);
6. 无人观看自动断流;
7. 支持UDP和TCP两种国标信令传输模式;
# 新支持特性
1. 集成web界面, 不需要单独部署前端服务, 直接利用wvp内置文件服务部署.
2. 支持平台接入, 针对大平台大量设备的情况进行优化.
3. 支持检索,通道筛选.
4. 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题.
5. 支持启用udp多端口模式, 提高udp模式下媒体传输性能.
6. 支持通道是否含有音频的设置
7. 支持通道子目录查询
8. 支持udp/tcp,两种模式传输视频流
# 新支持特性
1. 集成web界面, 不需要单独部署前端服务, 直接利用wvp内置文件服务部署, 随wvp一起部署;
2. 支持平台接入, 针对大平台大量设备的情况进行优化;
3. 支持检索,通道筛选;
4. 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题;
5. 支持启用udp多端口模式, 提高udp模式下媒体传输性能;
6. 支持通道是否含有音频的设置;
7. 支持通道子目录查询;
8. 支持udp/tcp国标流传输模式;
9. 支持直接输出RTSP、RTMP、HTTP-FLV、Websocket-FLV、HLS多种协议流地址
10.
# 待实现: 
上级级联  
推流列表  
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -538,7 +538,7 @@
            recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\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("<Secrecy>0</Secrecy>\\r\n");
            recordInfoXml.append("<Secrecy>0</Secrecy>\r\n");
            // 大华NVR要求必须增加一个值为all的文本元素节点Type
            recordInfoXml.append("<Type>all</Type>\r\n");
            recordInfoXml.append("</Query>\r\n");
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -140,8 +140,6 @@
            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.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtspPort(), streamId));
            storager.startPlay(streamInfo);
        }
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -74,6 +74,15 @@
        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){
        Map<String, Object> param = new HashMap<>();
        param.put("stream_id",stream_id);
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java
@@ -37,7 +37,7 @@
            System.out.println(jsonObject.toJSONString());
            return newPort;
        }else {
            return getNewRTPPort(streamId);
            return getNewRTPPort(ssrc);
        }
    }
src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java
@@ -165,7 +165,7 @@
    @Override
    public void updateCatch() {
        System.out.println("##################");
    }
    @Override
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
@@ -64,18 +64,25 @@
        while (lockFlag) {
            try {
                if (System.currentTimeMillis() - startTime > 15 * 1000) {
                    storager.stopPlay(streamInfo);
                    return new ResponseEntity<String>("timeout",HttpStatus.OK);
                }else {
                    streamInfo = storager.queryPlayByDevice(deviceId, channelId);
                    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);
                        continue;
                    }else {
                        lockFlag = false;
                        streamInfo = storager.queryPlay(streamInfo);
                    };
                }
            } catch (InterruptedException e) {
web_src/src/components/channelList.vue
@@ -54,12 +54,15 @@
          </el-table-column>
            <el-table-column prop="ptztypeText" label="云台类型">
            </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">
                                <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-s-open"  type="primary" v-if="scope.row.parental == 1" @click="changeSubchannel(scope.row)">查看子目录</el-button>
                                <!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> -->
                <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-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-video-camera" type="primary" >设备录象</el-button>-->
                  <!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> -->
                </el-button-group>
                            </template>
                        </el-table-column>
                </el-table>
web_src/src/components/gb28181/devicePlayer.vue
@@ -1,8 +1,7 @@
<template>
    <div id="devicePlayer">
        <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 && !hasaudio" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" fluent autoplay live ></LivePlayer>
      <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :hasaudio="hasaudio" fluent autoplay live ></LivePlayer>
            <div id="shared" style="text-align: right; margin-top: 1rem;">
                <el-tabs v-model="tabActiveName">
                    <el-tab-pane label="媒体流信息" name="media">
@@ -123,20 +122,17 @@
        methods: {
            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) {
        //   var realHasAudio = false;
        //   for (let i = 0; i < streamInfo.tracks; i++) {
        //     if (streamInfo.tracks[i].codec_type == 1) { // 判断为音频
        //       realHasAudio = true;
        //     }
        //   }
        //   this.hasaudio = realHasAudio && this.hasaudio;
        // }
        if( this.hasaudio && !!streamInfo.tracks && streamInfo.tracks.length > 0) {
          var realHasAudio = false;
          for (let i = 0; i < streamInfo.tracks; i++) {
            if (streamInfo.tracks[i].codec_type == 1) { // 判断为音频
              realHasAudio = true;
            }
          }
          this.hasaudio = realHasAudio && this.hasaudio;
        }
        this.ssrc = streamInfo.ssrc;
                this.deviceId = deviceId;
                this.channelId = channelId;