diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java index 00bd9951..13503419 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java @@ -148,6 +148,11 @@ public class DeviceChannel { */ private boolean hasAudio; + /** + * 是否正在播放 + */ + private boolean play; + public String getChannelId() { return channelId; } @@ -388,4 +393,12 @@ public class DeviceChannel { public void setHasAudio(boolean hasAudio) { this.hasAudio = hasAudio; } + + public boolean isPlay() { + return play; + } + + public void setPlay(boolean play) { + this.play = play; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java index 81148144..7d7edf40 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java @@ -38,6 +38,9 @@ public class ZLMRunner implements CommandLineRunner { @Value("${media.secret}") private String mediaSecret; + @Value("${media.streamNoneReaderDelayMS}") + private String streamNoneReaderDelayMS; + @Value("${sip.ip}") private String sipIP; @@ -54,9 +57,10 @@ public class ZLMRunner implements CommandLineRunner { 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 @@ public class ZLMRunner implements CommandLineRunner { } catch (InterruptedException e) { e.printStackTrace(); } - getMediaServerConfig(); + mediaServerConfig = getMediaServerConfig(); } return mediaServerConfig; } @@ -106,6 +110,7 @@ public class ZLMRunner implements CommandLineRunner { 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); diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java index 8381738c..0894fe2a 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java +++ b/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 @@ public interface IVideoManagerStorager { StreamInfo queryPlayBySSRC(String ssrc); StreamInfo queryPlayByDevice(String deviceId, String code); + + Map queryPlayByDeviceId(String deviceId); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java index c3a18b79..99f19ee2 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java +++ b/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 class VideoManagerJdbcStoragerImpl implements IVideoManagerStorager { public StreamInfo queryPlayByDevice(String deviceId, String code) { return null; } + + @Override + public Map queryPlayByDeviceId(String deviceId) { + return null; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java index 38a64762..91b60a1f 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java @@ -134,6 +134,8 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { @Override public PageResult queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, String online, int page, int count) { + // 获取到所有正在播放的流 + Map stringStreamInfoMap = queryPlayByDeviceId(deviceId); List result = new ArrayList<>(); PageResult pageResult = new PageResult(); String queryContent = "*"; @@ -154,7 +156,11 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { 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); } @@ -162,6 +168,8 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { return pageResult; } + + @Override public List queryChannelsByDeviceId(String deviceId) { List result = new ArrayList<>(); @@ -231,7 +239,13 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { @Override public DeviceChannel queryChannel(String deviceId, String channelId) { - return (DeviceChannel)redis.get(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + "_" + channelId + "_"); + DeviceChannel deviceChannel = null; + List 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 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { @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 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { @Override public StreamInfo queryPlayBySSRC(String ssrc) { List 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 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { List 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 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { } } + @Override + public Map queryPlayByDeviceId(String deviceId) { + Map streamInfos = new HashMap<>(); + List 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; + } + } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java index 996039ee..d5647fc6 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java @@ -41,18 +41,36 @@ public class PlayController { public ResponseEntity 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(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("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 @@ public class PlayController { } } }; - } - Thread.sleep(200); + streamInfo = storager.queryPlayByDevice(deviceId, channelId); } catch (InterruptedException e) { e.printStackTrace(); } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5b5f01eb..ad375486 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -43,4 +43,5 @@ media: #zlm服务器的ip与http端口, 重点: 这是http端口 ip: 192.168.1.20 port: 9080 secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc + streamNoneReaderDelayMS: 1800000 # 无人观看多久关闭流 diff --git a/web_src/src/components/channelList.vue b/web_src/src/components/channelList.vue index 7c667baa..b3980c32 100644 --- a/web_src/src/components/channelList.vue +++ b/web_src/src/components/channelList.vue @@ -56,7 +56,8 @@ @@ -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(); diff --git a/web_src/src/components/gb28181/devicePlayer.vue b/web_src/src/components/gb28181/devicePlayer.vue index 9e497e4c..ce36483a 100644 --- a/web_src/src/components/gb28181/devicePlayer.vue +++ b/web_src/src/components/gb28181/devicePlayer.vue @@ -1,6 +1,6 @@