Commit 3ec3b88456cf9ac455d93baba40f339bb284dd77

Authored by 648540858
1 parent fbdad00c

修复点播判断错误导致的15s超长延时

增加默认不关闭推流, 无人观看超时或点击停止按钮关闭流
修复点播其他bug
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java
@@ -148,6 +148,11 @@ public class DeviceChannel { @@ -148,6 +148,11 @@ public class DeviceChannel {
148 */ 148 */
149 private boolean hasAudio; 149 private boolean hasAudio;
150 150
  151 + /**
  152 + * 是否正在播放
  153 + */
  154 + private boolean play;
  155 +
151 public String getChannelId() { 156 public String getChannelId() {
152 return channelId; 157 return channelId;
153 } 158 }
@@ -388,4 +393,12 @@ public class DeviceChannel { @@ -388,4 +393,12 @@ public class DeviceChannel {
388 public void setHasAudio(boolean hasAudio) { 393 public void setHasAudio(boolean hasAudio) {
389 this.hasAudio = hasAudio; 394 this.hasAudio = hasAudio;
390 } 395 }
  396 +
  397 + public boolean isPlay() {
  398 + return play;
  399 + }
  400 +
  401 + public void setPlay(boolean play) {
  402 + this.play = play;
  403 + }
391 } 404 }
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
@@ -38,6 +38,9 @@ public class ZLMRunner implements CommandLineRunner { @@ -38,6 +38,9 @@ public class ZLMRunner implements CommandLineRunner {
38 @Value("${media.secret}") 38 @Value("${media.secret}")
39 private String mediaSecret; 39 private String mediaSecret;
40 40
  41 + @Value("${media.streamNoneReaderDelayMS}")
  42 + private String streamNoneReaderDelayMS;
  43 +
41 @Value("${sip.ip}") 44 @Value("${sip.ip}")
42 private String sipIP; 45 private String sipIP;
43 46
@@ -54,9 +57,10 @@ public class ZLMRunner implements CommandLineRunner { @@ -54,9 +57,10 @@ public class ZLMRunner implements CommandLineRunner {
54 MediaServerConfig mediaServerConfig = getMediaServerConfig(); 57 MediaServerConfig mediaServerConfig = getMediaServerConfig();
55 if (mediaServerConfig != null) { 58 if (mediaServerConfig != null) {
56 logger.info("zlm接入成功..."); 59 logger.info("zlm接入成功...");
57 - storager.updateMediaInfo(mediaServerConfig);  
58 logger.info("设置zlm..."); 60 logger.info("设置zlm...");
59 saveZLMConfig(); 61 saveZLMConfig();
  62 + mediaServerConfig = getMediaServerConfig();
  63 + storager.updateMediaInfo(mediaServerConfig);
60 64
61 } 65 }
62 } 66 }
@@ -79,7 +83,7 @@ public class ZLMRunner implements CommandLineRunner { @@ -79,7 +83,7 @@ public class ZLMRunner implements CommandLineRunner {
79 } catch (InterruptedException e) { 83 } catch (InterruptedException e) {
80 e.printStackTrace(); 84 e.printStackTrace();
81 } 85 }
82 - getMediaServerConfig(); 86 + mediaServerConfig = getMediaServerConfig();
83 } 87 }
84 return mediaServerConfig; 88 return mediaServerConfig;
85 } 89 }
@@ -106,6 +110,7 @@ public class ZLMRunner implements CommandLineRunner { @@ -106,6 +110,7 @@ public class ZLMRunner implements CommandLineRunner {
106 param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrex)); 110 param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrex));
107 param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex)); 111 param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex));
108 param.put("hook.timeoutSec","20"); 112 param.put("hook.timeoutSec","20");
  113 + param.put("general.streamNoneReaderDelayMS",streamNoneReaderDelayMS);
109 114
110 JSONObject responseJSON = zlmresTfulUtils.setServerConfig(param); 115 JSONObject responseJSON = zlmresTfulUtils.setServerConfig(param);
111 116
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
1 package com.genersoft.iot.vmp.storager; 1 package com.genersoft.iot.vmp.storager;
2 2
3 import java.util.List; 3 import java.util.List;
  4 +import java.util.Map;
4 5
5 import com.alibaba.fastjson.JSONObject; 6 import com.alibaba.fastjson.JSONObject;
6 import com.genersoft.iot.vmp.common.PageResult; 7 import com.genersoft.iot.vmp.common.PageResult;
@@ -180,4 +181,6 @@ public interface IVideoManagerStorager { @@ -180,4 +181,6 @@ public interface IVideoManagerStorager {
180 StreamInfo queryPlayBySSRC(String ssrc); 181 StreamInfo queryPlayBySSRC(String ssrc);
181 182
182 StreamInfo queryPlayByDevice(String deviceId, String code); 183 StreamInfo queryPlayByDevice(String deviceId, String code);
  184 +
  185 + Map<String, StreamInfo> queryPlayByDeviceId(String deviceId);
183 } 186 }
src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java
1 package com.genersoft.iot.vmp.storager.jdbc; 1 package com.genersoft.iot.vmp.storager.jdbc;
2 2
3 import java.util.List; 3 import java.util.List;
  4 +import java.util.Map;
4 5
5 import com.genersoft.iot.vmp.common.PageResult; 6 import com.genersoft.iot.vmp.common.PageResult;
6 import com.genersoft.iot.vmp.common.StreamInfo; 7 import com.genersoft.iot.vmp.common.StreamInfo;
@@ -186,4 +187,9 @@ public class VideoManagerJdbcStoragerImpl implements IVideoManagerStorager { @@ -186,4 +187,9 @@ public class VideoManagerJdbcStoragerImpl implements IVideoManagerStorager {
186 public StreamInfo queryPlayByDevice(String deviceId, String code) { 187 public StreamInfo queryPlayByDevice(String deviceId, String code) {
187 return null; 188 return null;
188 } 189 }
  190 +
  191 + @Override
  192 + public Map<String, StreamInfo> queryPlayByDeviceId(String deviceId) {
  193 + return null;
  194 + }
189 } 195 }
src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java
@@ -134,6 +134,8 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { @@ -134,6 +134,8 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
134 134
135 @Override 135 @Override
136 public PageResult queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, String online, int page, int count) { 136 public PageResult queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, String online, int page, int count) {
  137 + // 获取到所有正在播放的流
  138 + Map<String, StreamInfo> stringStreamInfoMap = queryPlayByDeviceId(deviceId);
137 List<DeviceChannel> result = new ArrayList<>(); 139 List<DeviceChannel> result = new ArrayList<>();
138 PageResult pageResult = new PageResult<DeviceChannel>(); 140 PageResult pageResult = new PageResult<DeviceChannel>();
139 String queryContent = "*"; 141 String queryContent = "*";
@@ -154,7 +156,11 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { @@ -154,7 +156,11 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
154 int maxCount = (page + 1 ) * count; 156 int maxCount = (page + 1 ) * count;
155 if (deviceChannelList != null && deviceChannelList.size() > 0 ) { 157 if (deviceChannelList != null && deviceChannelList.size() > 0 ) {
156 for (int i = page * count; i < (pageResult.getTotal() > maxCount ? maxCount : pageResult.getTotal() ); i++) { 158 for (int i = page * count; i < (pageResult.getTotal() > maxCount ? maxCount : pageResult.getTotal() ); i++) {
157 - result.add((DeviceChannel)redis.get((String)deviceChannelList.get(i))); 159 + DeviceChannel deviceChannel = (DeviceChannel)redis.get((String)deviceChannelList.get(i));
  160 + StreamInfo streamInfo = stringStreamInfoMap.get(deviceId + "_" + deviceChannel.getChannelId());
  161 + deviceChannel.setPlay(streamInfo != null);
  162 + if (streamInfo != null) deviceChannel.setSsrc(streamInfo.getSsrc());
  163 + result.add(deviceChannel);
158 } 164 }
159 pageResult.setData(result); 165 pageResult.setData(result);
160 } 166 }
@@ -162,6 +168,8 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { @@ -162,6 +168,8 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
162 return pageResult; 168 return pageResult;
163 } 169 }
164 170
  171 +
  172 +
165 @Override 173 @Override
166 public List<DeviceChannel> queryChannelsByDeviceId(String deviceId) { 174 public List<DeviceChannel> queryChannelsByDeviceId(String deviceId) {
167 List<DeviceChannel> result = new ArrayList<>(); 175 List<DeviceChannel> result = new ArrayList<>();
@@ -231,7 +239,13 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { @@ -231,7 +239,13 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
231 239
232 @Override 240 @Override
233 public DeviceChannel queryChannel(String deviceId, String channelId) { 241 public DeviceChannel queryChannel(String deviceId, String channelId) {
234 - return (DeviceChannel)redis.get(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + "_" + channelId + "_"); 242 + DeviceChannel deviceChannel = null;
  243 + List<Object> deviceChannelList = redis.keys(VideoManagerConstants.CACHEKEY_PREFIX + deviceId +
  244 + "_" + channelId + "*");
  245 + if (deviceChannelList != null && deviceChannelList.size() > 0 ) {
  246 + deviceChannel = (DeviceChannel)redis.get((String)deviceChannelList.get(0));
  247 + }
  248 + return deviceChannel;
235 } 249 }
236 250
237 251
@@ -345,6 +359,12 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { @@ -345,6 +359,12 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
345 @Override 359 @Override
346 public boolean stopPlay(StreamInfo streamInfo) { 360 public boolean stopPlay(StreamInfo streamInfo) {
347 if (streamInfo == null) return false; 361 if (streamInfo == null) return false;
  362 + DeviceChannel deviceChannel = queryChannel(streamInfo.getDeviceID(), streamInfo.getCahnnelId());
  363 + if (deviceChannel != null) {
  364 + deviceChannel.setSsrc(null);
  365 + deviceChannel.setPlay(false);
  366 + updateChannel(streamInfo.getDeviceID(), deviceChannel);
  367 + }
348 return redis.del(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, 368 return redis.del(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX,
349 streamInfo.getSsrc(), 369 streamInfo.getSsrc(),
350 streamInfo.getDeviceID(), 370 streamInfo.getDeviceID(),
@@ -366,7 +386,7 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { @@ -366,7 +386,7 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
366 @Override 386 @Override
367 public StreamInfo queryPlayBySSRC(String ssrc) { 387 public StreamInfo queryPlayBySSRC(String ssrc) {
368 List<Object> playLeys = redis.keys(String.format("%S_%s_*", VideoManagerConstants.PLAYER_PREFIX, ssrc)); 388 List<Object> playLeys = redis.keys(String.format("%S_%s_*", VideoManagerConstants.PLAYER_PREFIX, ssrc));
369 - if (playLeys.size() == 0) return null; 389 + if (playLeys == null || playLeys.size() == 0) return null;
370 return (StreamInfo)redis.get(playLeys.get(0).toString()); 390 return (StreamInfo)redis.get(playLeys.get(0).toString());
371 } 391 }
372 392
@@ -375,6 +395,7 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { @@ -375,6 +395,7 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
375 List<Object> playLeys = redis.keys(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, 395 List<Object> playLeys = redis.keys(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX,
376 deviceId, 396 deviceId,
377 code)); 397 code));
  398 + if (playLeys == null || playLeys.size() == 0) return null;
378 return (StreamInfo)redis.get(playLeys.get(0).toString()); 399 return (StreamInfo)redis.get(playLeys.get(0).toString());
379 } 400 }
380 401
@@ -438,6 +459,19 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { @@ -438,6 +459,19 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
438 } 459 }
439 } 460 }
440 461
  462 + @Override
  463 + public Map<String, StreamInfo> queryPlayByDeviceId(String deviceId) {
  464 + Map<String, StreamInfo> streamInfos = new HashMap<>();
  465 + List<Object> playLeys = redis.keys(String.format("%S_*_%S_*", VideoManagerConstants.PLAYER_PREFIX, deviceId));
  466 + if (playLeys.size() == 0) return streamInfos;
  467 + for (int i = 0; i < playLeys.size(); i++) {
  468 + String key = (String) playLeys.get(i);
  469 + StreamInfo streamInfo = (StreamInfo)redis.get(key);
  470 + streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getCahnnelId(), streamInfo);
  471 + }
  472 + return streamInfos;
  473 + }
  474 +
441 475
442 476
443 } 477 }
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
@@ -41,18 +41,36 @@ public class PlayController { @@ -41,18 +41,36 @@ public class PlayController {
41 public ResponseEntity<String> play(@PathVariable String deviceId,@PathVariable String channelId){ 41 public ResponseEntity<String> play(@PathVariable String deviceId,@PathVariable String channelId){
42 42
43 Device device = storager.queryVideoDevice(deviceId); 43 Device device = storager.queryVideoDevice(deviceId);
44 - StreamInfo streamInfo = cmder.playStreamCmd(device, channelId); 44 + StreamInfo streamInfo = storager.queryPlayByDevice(deviceId, channelId);
  45 +
  46 + if (streamInfo == null) {
  47 + streamInfo = cmder.playStreamCmd(device, channelId);
  48 + }else {
  49 + String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase();
  50 + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
  51 + if (rtpInfo.getBoolean("exist")) {
  52 + return new ResponseEntity<String>(JSON.toJSONString(streamInfo),HttpStatus.OK);
  53 + }else {
  54 + storager.stopPlay(streamInfo);
  55 + streamInfo = cmder.playStreamCmd(device, channelId);
  56 + }
  57 +
  58 + }
  59 + String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase();
45 // 等待推流, TODO 默认超时15s 60 // 等待推流, TODO 默认超时15s
46 boolean lockFlag = true; 61 boolean lockFlag = true;
47 long startTime = System.currentTimeMillis(); 62 long startTime = System.currentTimeMillis();
48 - String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase();  
49 63
50 - // 判断推流是否存在  
51 while (lockFlag) { 64 while (lockFlag) {
52 try { 65 try {
  66 +
53 if (System.currentTimeMillis() - startTime > 15 * 1000) { 67 if (System.currentTimeMillis() - startTime > 15 * 1000) {
  68 + storager.stopPlay(streamInfo);
  69 + return new ResponseEntity<String>("timeout",HttpStatus.OK);
  70 + }else {
54 JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); 71 JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
55 - if (rtpInfo == null){ 72 + Boolean exist = rtpInfo.getBoolean("exist");
  73 + if (rtpInfo == null || !rtpInfo.getBoolean("exist") || streamInfo.getFlv() != null){
56 continue; 74 continue;
57 }else { 75 }else {
58 lockFlag = false; 76 lockFlag = false;
@@ -72,10 +90,9 @@ public class PlayController { @@ -72,10 +90,9 @@ public class PlayController {
72 } 90 }
73 } 91 }
74 }; 92 };
75 -  
76 } 93 }
77 -  
78 Thread.sleep(200); 94 Thread.sleep(200);
  95 + streamInfo = storager.queryPlayByDevice(deviceId, channelId);
79 } catch (InterruptedException e) { 96 } catch (InterruptedException e) {
80 e.printStackTrace(); 97 e.printStackTrace();
81 } 98 }
src/main/resources/application.yml
@@ -43,4 +43,5 @@ media: #zlm服务器的ip与http端口, 重点: 这是http端口 @@ -43,4 +43,5 @@ media: #zlm服务器的ip与http端口, 重点: 这是http端口
43 ip: 192.168.1.20 43 ip: 192.168.1.20
44 port: 9080 44 port: 9080
45 secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc 45 secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
  46 + streamNoneReaderDelayMS: 1800000 # 无人观看多久关闭流
46 47
web_src/src/components/channelList.vue
@@ -56,7 +56,8 @@ @@ -56,7 +56,8 @@
56 </el-table-column> 56 </el-table-column>
57 <el-table-column label="操作" width="240" align="center" fixed="right"> 57 <el-table-column label="操作" width="240" align="center" fixed="right">
58 <template slot-scope="scope"> 58 <template slot-scope="scope">
59 - <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">预览视频</el-button> 59 + <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button>
  60 + <el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="scope.row.play" @click="stopDevicePush(scope.row)">停止</el-button>
60 <el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.parental == 1" @click="changeSubchannel(scope.row)">查看子目录</el-button> 61 <el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.parental == 1" @click="changeSubchannel(scope.row)">查看子目录</el-button>
61 <!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> --> 62 <!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> -->
62 </template> 63 </template>
@@ -198,7 +199,7 @@ @@ -198,7 +199,7 @@
198 message: '请求成功', 199 message: '请求成功',
199 type: 'success' 200 type: 'success'
200 }); 201 });
201 - });; 202 + });
202 }, 203 },
203 //通知设备上传媒体流 204 //通知设备上传媒体流
204 sendDevicePush: function(itemData) { 205 sendDevicePush: function(itemData) {
@@ -212,12 +213,30 @@ @@ -212,12 +213,30 @@
212 method: 'get', 213 method: 'get',
213 url: '/api/play/' + deviceId + '/' + channelId 214 url: '/api/play/' + deviceId + '/' + channelId
214 }).then(function(res) { 215 }).then(function(res) {
  216 + console.log(res.data)
215 let ssrc = res.data.ssrc; 217 let ssrc = res.data.ssrc;
216 that.isLoging = false 218 that.isLoging = false
217 - that.$refs.devicePlayer.play(res.data,deviceId,channelId,itemData.hasAudio); 219 + if (!!ssrc) {
  220 + that.$refs.devicePlayer.play(res.data,deviceId,channelId,itemData.hasAudio);
  221 + that.initData();
  222 + }else {
  223 + that.$message.error(res.data);
  224 + }
218 }).catch(function(e) { 225 }).catch(function(e) {
219 }); 226 });
220 }, 227 },
  228 + stopDevicePush: function(itemData) {
  229 + console.log(itemData)
  230 + var that = this;
  231 + this.$axios({
  232 + method: 'post',
  233 + url: '/api/play/' + itemData.ssrc + '/stop'
  234 + }).then(function(res) {
  235 + console.log(JSON.stringify(res));
  236 + that.initData();
  237 + });
  238 + },
  239 +
221 showDevice: function(){ 240 showDevice: function(){
222 this.$router.push(this.beforeUrl).then(()=>{ 241 this.$router.push(this.beforeUrl).then(()=>{
223 this.initParam(); 242 this.initParam();
web_src/src/components/gb28181/devicePlayer.vue
1 <template> 1 <template>
2 <div id="devicePlayer"> 2 <div id="devicePlayer">
3 - <el-dialog title="视频播放" top="0" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="stop()"> 3 + <el-dialog title="视频播放" top="0" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()">
4 <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :hasaudio="hasaudio" fluent autoplay live ></LivePlayer> 4 <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :hasaudio="hasaudio" fluent autoplay live ></LivePlayer>
5 <div id="shared" style="text-align: right; margin-top: 1rem;"> 5 <div id="shared" style="text-align: right; margin-top: 1rem;">
6 <el-tabs v-model="tabActiveName"> 6 <el-tabs v-model="tabActiveName">
@@ -145,24 +145,11 @@ @@ -145,24 +145,11 @@
145 this.showVideoDialog = true; 145 this.showVideoDialog = true;
146 console.log(this.ssrc); 146 console.log(this.ssrc);
147 }, 147 },
148 - stop: function() { 148 + close: function() {
149 console.log('关闭视频'); 149 console.log('关闭视频');
150 this.$refs.videoPlayer.pause(); 150 this.$refs.videoPlayer.pause();
151 this.videoUrl = ''; 151 this.videoUrl = '';
152 this.showVideoDialog = false; 152 this.showVideoDialog = false;
153 - this.$axios({  
154 - method: 'post',  
155 - url: '/api/play/' + this.ssrc + '/stop'  
156 - }).then(function(res) {  
157 - console.log(JSON.stringify(res));  
158 - });  
159 -  
160 - this.$axios({  
161 - method: 'post',  
162 - url: '/api/playback/' + this.ssrc + '/stop'  
163 - }).then(function(res) {  
164 - console.log(JSON.stringify(res));  
165 - });  
166 }, 153 },
167 copySharedInfo: function(data) { 154 copySharedInfo: function(data) {
168 console.log('复制内容:' + data); 155 console.log('复制内容:' + data);