修复点播判断错误导致的15s超长延时
增加默认不关闭推流, 无人观看超时或点击停止按钮关闭流
修复点播其他bug
9个文件已修改
143 ■■■■ 已修改文件
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/channelList.vue 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/gb28181/devicePlayer.vue 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java
@@ -148,6 +148,11 @@
     */
    private  boolean hasAudio;
    /**
     *  是否正在播放
     */
    private  boolean play;
    public String getChannelId() {
        return channelId;
    }
@@ -388,4 +393,12 @@
    public void setHasAudio(boolean hasAudio) {
        this.hasAudio = hasAudio;
    }
    public boolean isPlay() {
        return play;
    }
    public void setPlay(boolean play) {
        this.play = play;
    }
}
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
@@ -38,6 +38,9 @@
    @Value("${media.secret}")
    private String mediaSecret;
    @Value("${media.streamNoneReaderDelayMS}")
    private String streamNoneReaderDelayMS;
    @Value("${sip.ip}")
    private String sipIP;
@@ -54,9 +57,10 @@
        MediaServerConfig mediaServerConfig = getMediaServerConfig();
        if (mediaServerConfig != null) {
            logger.info("zlm接入成功...");
            storager.updateMediaInfo(mediaServerConfig);
            logger.info("设置zlm...");
            saveZLMConfig();
            mediaServerConfig = getMediaServerConfig();
            storager.updateMediaInfo(mediaServerConfig);
        }
    }
@@ -79,7 +83,7 @@
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            getMediaServerConfig();
            mediaServerConfig = getMediaServerConfig();
        }
        return mediaServerConfig;
    }
@@ -106,6 +110,7 @@
        param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrex));
        param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex));
        param.put("hook.timeoutSec","20");
        param.put("general.streamNoneReaderDelayMS",streamNoneReaderDelayMS);
        JSONObject responseJSON = zlmresTfulUtils.setServerConfig(param);
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
@@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.storager;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.PageResult;
@@ -180,4 +181,6 @@
    StreamInfo queryPlayBySSRC(String ssrc);
    StreamInfo queryPlayByDevice(String deviceId, String code);
    Map<String, StreamInfo> queryPlayByDeviceId(String deviceId);
}
src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java
@@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.storager.jdbc;
import java.util.List;
import java.util.Map;
import com.genersoft.iot.vmp.common.PageResult;
import com.genersoft.iot.vmp.common.StreamInfo;
@@ -186,4 +187,9 @@
    public StreamInfo queryPlayByDevice(String deviceId, String code) {
        return null;
    }
    @Override
    public Map<String, StreamInfo> queryPlayByDeviceId(String deviceId) {
        return null;
    }
}
src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java
@@ -134,6 +134,8 @@
    @Override
    public PageResult queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, String online, int page, int count) {
        // 获取到所有正在播放的流
        Map<String, StreamInfo> stringStreamInfoMap = queryPlayByDeviceId(deviceId);
        List<DeviceChannel> result = new ArrayList<>();
        PageResult pageResult = new PageResult<DeviceChannel>();
        String queryContent = "*";
@@ -154,13 +156,19 @@
        int maxCount = (page + 1 ) * count;
        if (deviceChannelList != null && deviceChannelList.size() > 0 ) {
            for (int i = page * count; i < (pageResult.getTotal() > maxCount ? maxCount : pageResult.getTotal() ); i++) {
                result.add((DeviceChannel)redis.get((String)deviceChannelList.get(i)));
                DeviceChannel deviceChannel = (DeviceChannel)redis.get((String)deviceChannelList.get(i));
                StreamInfo streamInfo = stringStreamInfoMap.get(deviceId + "_" + deviceChannel.getChannelId());
                deviceChannel.setPlay(streamInfo != null);
                if (streamInfo != null) deviceChannel.setSsrc(streamInfo.getSsrc());
                result.add(deviceChannel);
            }
            pageResult.setData(result);
        }
        return pageResult;
    }
    @Override
    public List<DeviceChannel> queryChannelsByDeviceId(String deviceId) {
@@ -231,7 +239,13 @@
    @Override
    public DeviceChannel queryChannel(String deviceId, String channelId) {
        return (DeviceChannel)redis.get(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + "_" + channelId + "_");
        DeviceChannel deviceChannel = null;
        List<Object> deviceChannelList = redis.keys(VideoManagerConstants.CACHEKEY_PREFIX + deviceId +
                "_" + channelId  + "*");
        if (deviceChannelList != null && deviceChannelList.size() > 0 ) {
            deviceChannel = (DeviceChannel)redis.get((String)deviceChannelList.get(0));
        }
        return deviceChannel;
    }
@@ -345,6 +359,12 @@
    @Override
    public boolean stopPlay(StreamInfo streamInfo) {
        if (streamInfo == null) return false;
        DeviceChannel deviceChannel = queryChannel(streamInfo.getDeviceID(), streamInfo.getCahnnelId());
        if (deviceChannel != null) {
            deviceChannel.setSsrc(null);
            deviceChannel.setPlay(false);
            updateChannel(streamInfo.getDeviceID(), deviceChannel);
        }
        return redis.del(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX,
                streamInfo.getSsrc(),
                streamInfo.getDeviceID(),
@@ -366,7 +386,7 @@
    @Override
    public StreamInfo queryPlayBySSRC(String ssrc) {
        List<Object> playLeys = redis.keys(String.format("%S_%s_*", VideoManagerConstants.PLAYER_PREFIX, ssrc));
        if (playLeys.size() == 0) return null;
        if (playLeys == null || playLeys.size() == 0) return null;
        return (StreamInfo)redis.get(playLeys.get(0).toString());
    }
@@ -375,6 +395,7 @@
        List<Object> playLeys = redis.keys(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX,
                deviceId,
                code));
        if (playLeys == null || playLeys.size() == 0) return null;
        return (StreamInfo)redis.get(playLeys.get(0).toString());
    }
@@ -438,6 +459,19 @@
        }
    }
    @Override
    public Map<String, StreamInfo> queryPlayByDeviceId(String deviceId) {
        Map<String, StreamInfo> streamInfos = new HashMap<>();
        List<Object> playLeys = redis.keys(String.format("%S_*_%S_*", VideoManagerConstants.PLAYER_PREFIX, deviceId));
        if (playLeys.size() == 0) return streamInfos;
        for (int i = 0; i < playLeys.size(); i++) {
            String key = (String) playLeys.get(i);
            StreamInfo streamInfo = (StreamInfo)redis.get(key);
            streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getCahnnelId(), streamInfo);
        }
        return streamInfos;
    }
}
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
@@ -41,18 +41,36 @@
    public ResponseEntity<String> play(@PathVariable String deviceId,@PathVariable String channelId){
        
        Device device = storager.queryVideoDevice(deviceId);
        StreamInfo streamInfo = cmder.playStreamCmd(device, channelId);
        StreamInfo streamInfo = storager.queryPlayByDevice(deviceId, channelId);
        if (streamInfo == null) {
            streamInfo = cmder.playStreamCmd(device, channelId);
        }else {
            String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase();
            JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
            if (rtpInfo.getBoolean("exist")) {
                return new ResponseEntity<String>(JSON.toJSONString(streamInfo),HttpStatus.OK);
            }else {
                storager.stopPlay(streamInfo);
                streamInfo = cmder.playStreamCmd(device, channelId);
            }
        }
        String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase();
        // 等待推流, TODO 默认超时15s
        boolean lockFlag = true;
        long startTime = System.currentTimeMillis();
        String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase();
        // 判断推流是否存在
        while (lockFlag) {
            try {
                if (System.currentTimeMillis() - startTime > 15 * 1000) {
                    storager.stopPlay(streamInfo);
                    return new ResponseEntity<String>("timeout",HttpStatus.OK);
                }else {
                    JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
                    if (rtpInfo == null){
                    Boolean exist = rtpInfo.getBoolean("exist");
                    if (rtpInfo == null || !rtpInfo.getBoolean("exist") || streamInfo.getFlv() != null){
                        continue;
                    }else {
                        lockFlag = false;
@@ -72,10 +90,9 @@
                            }
                        }
                    };
                }
                Thread.sleep(200);
                streamInfo = storager.queryPlayByDevice(deviceId, channelId);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
src/main/resources/application.yml
@@ -43,4 +43,5 @@
    ip: 192.168.1.20
    port: 9080
    secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
    streamNoneReaderDelayMS:  1800000  # 无人观看多久关闭流
web_src/src/components/channelList.vue
@@ -56,7 +56,8 @@
            </el-table-column>
                        <el-table-column label="操作" width="240" 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-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> -->
                            </template>
@@ -198,7 +199,7 @@
                        message: '请求成功',
                        type: 'success'
                    });
                });;
                });
            },
            //通知设备上传媒体流
            sendDevicePush: function(itemData) {
@@ -212,12 +213,30 @@
                    method: 'get',
                    url: '/api/play/' + deviceId + '/' + channelId
                }).then(function(res) {
          console.log(res.data)
                    let ssrc = res.data.ssrc;
          that.isLoging = false
                    that.$refs.devicePlayer.play(res.data,deviceId,channelId,itemData.hasAudio);
          if (!!ssrc) {
            that.$refs.devicePlayer.play(res.data,deviceId,channelId,itemData.hasAudio);
            that.initData();
          }else {
            that.$message.error(res.data);
          }
                }).catch(function(e) {
                });
            },
      stopDevicePush: function(itemData) {
              console.log(itemData)
        var that = this;
        this.$axios({
          method: 'post',
          url: '/api/play/' + itemData.ssrc + '/stop'
        }).then(function(res) {
          console.log(JSON.stringify(res));
          that.initData();
        });
      },
            showDevice: function(){
                this.$router.push(this.beforeUrl).then(()=>{
                    this.initParam();
web_src/src/components/gb28181/devicePlayer.vue
@@ -1,6 +1,6 @@
<template>
    <div id="devicePlayer">
        <el-dialog title="视频播放" top="0" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="stop()">
        <el-dialog title="视频播放" top="0" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()">
      <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">
@@ -145,24 +145,11 @@
                this.showVideoDialog = true;
                console.log(this.ssrc);
            },
            stop: function() {
            close: function() {
                console.log('关闭视频');
                this.$refs.videoPlayer.pause();
                this.videoUrl = '';
                this.showVideoDialog = false;
                this.$axios({
                    method: 'post',
                    url: '/api/play/' + this.ssrc + '/stop'
                }).then(function(res) {
                    console.log(JSON.stringify(res));
                });
                this.$axios({
                    method: 'post',
                    url: '/api/playback/' + this.ssrc + '/stop'
                }).then(function(res) {
                    console.log(JSON.stringify(res));
                });
            },
            copySharedInfo: function(data) {
                console.log('复制内容:' + data);