Commit 15df08964bb459d6d01ec5ef423eae094eac1c72
1 parent
314423bd
新增设备主子码流选择,默认为不开启
Showing
25 changed files
with
901 additions
and
367 deletions
src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java
| 1 | 1 | package com.genersoft.iot.vmp.common; |
| 2 | 2 | |
| 3 | 3 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | 5 | |
| 5 | 6 | /** |
| 6 | 7 | * 记录每次发送invite消息的状态 |
| ... | ... | @@ -123,4 +124,40 @@ public class InviteInfo { |
| 123 | 124 | public void setStreamMode(String streamMode) { |
| 124 | 125 | this.streamMode = streamMode; |
| 125 | 126 | } |
| 127 | + | |
| 128 | + | |
| 129 | + /*=========================设备主子码流逻辑START====================*/ | |
| 130 | + @Schema(description = "是否为子码流(true-是,false-主码流)") | |
| 131 | + private boolean subStream; | |
| 132 | + | |
| 133 | + public boolean isSubStream() { | |
| 134 | + return subStream; | |
| 135 | + } | |
| 136 | + | |
| 137 | + public void setSubStream(boolean subStream) { | |
| 138 | + this.subStream = subStream; | |
| 139 | + } | |
| 140 | + | |
| 141 | + public static InviteInfo getInviteInfo(String deviceId, String channelId,Boolean isSubStream, String stream, SSRCInfo ssrcInfo, | |
| 142 | + String receiveIp, Integer receivePort, String streamMode, | |
| 143 | + InviteSessionType type, InviteSessionStatus status) { | |
| 144 | + InviteInfo inviteInfo = new InviteInfo(); | |
| 145 | + inviteInfo.setDeviceId(deviceId); | |
| 146 | + inviteInfo.setChannelId(channelId); | |
| 147 | + inviteInfo.setStream(stream); | |
| 148 | + inviteInfo.setSsrcInfo(ssrcInfo); | |
| 149 | + inviteInfo.setReceiveIp(receiveIp); | |
| 150 | + inviteInfo.setReceivePort(receivePort); | |
| 151 | + inviteInfo.setStreamMode(streamMode); | |
| 152 | + inviteInfo.setType(type); | |
| 153 | + inviteInfo.setStatus(status); | |
| 154 | + if(isSubStream != null){ | |
| 155 | + inviteInfo.setSubStream(isSubStream); | |
| 156 | + } | |
| 157 | + return inviteInfo; | |
| 158 | + } | |
| 159 | + /*=========================设备主子码流逻辑END====================*/ | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 126 | 163 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
| ... | ... | @@ -528,4 +528,31 @@ public class StreamInfo implements Serializable, Cloneable{ |
| 528 | 528 | } |
| 529 | 529 | return instance; |
| 530 | 530 | } |
| 531 | + | |
| 532 | + | |
| 533 | + /*=========================设备主子码流逻辑START====================*/ | |
| 534 | + @Schema(description = "是否为子码流(true-是,false-主码流)") | |
| 535 | + private boolean subStream; | |
| 536 | + | |
| 537 | + public boolean isSubStream() { | |
| 538 | + return subStream; | |
| 539 | + } | |
| 540 | + | |
| 541 | + public void setSubStream(boolean subStream) { | |
| 542 | + this.subStream = subStream; | |
| 543 | + } | |
| 544 | + | |
| 545 | + public static String getPlayStream(String deviceId,String channelId,boolean isSubStream){ | |
| 546 | + String streamId; | |
| 547 | + if(isSubStream){ | |
| 548 | + streamId = String.format("%s_%s_%s","sub",deviceId, channelId); | |
| 549 | + }else { | |
| 550 | + streamId = String.format("%s_%s_%s","main", deviceId, channelId); | |
| 551 | + } | |
| 552 | + return streamId; | |
| 553 | + } | |
| 554 | + | |
| 555 | + /*=========================设备主子码流逻辑END====================*/ | |
| 556 | + | |
| 557 | + | |
| 531 | 558 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
| 1 | 1 | package com.genersoft.iot.vmp.conf; |
| 2 | 2 | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 3 | 4 | import org.springframework.core.annotation.Order; |
| 4 | 5 | import org.springframework.boot.context.properties.ConfigurationProperties; |
| 5 | 6 | import org.springframework.stereotype.Component; |
| ... | ... | @@ -25,11 +26,11 @@ public class UserSetting { |
| 25 | 26 | |
| 26 | 27 | private int platformPlayTimeout = 60000; |
| 27 | 28 | |
| 28 | - private Boolean interfaceAuthentication = Boolean.TRUE; | |
| 29 | + private Boolean interfaceAuthentication = Boolean.FALSE; | |
| 29 | 30 | |
| 30 | - private Boolean recordPushLive = Boolean.TRUE; | |
| 31 | + private Boolean recordPushLive = Boolean.FALSE; | |
| 31 | 32 | |
| 32 | - private Boolean recordSip = Boolean.TRUE; | |
| 33 | + private Boolean recordSip = Boolean.FALSE; | |
| 33 | 34 | |
| 34 | 35 | private Boolean logInDatebase = Boolean.TRUE; |
| 35 | 36 | |
| ... | ... | @@ -62,6 +63,8 @@ public class UserSetting { |
| 62 | 63 | |
| 63 | 64 | private String thirdPartyGBIdReg = "[\\s\\S]*"; |
| 64 | 65 | |
| 66 | + | |
| 67 | + | |
| 65 | 68 | private List<String> interfaceAuthenticationExcludes = new ArrayList<>(); |
| 66 | 69 | |
| 67 | 70 | private List<String> allowedOrigins = new ArrayList<>(); |
| ... | ... | @@ -295,4 +298,10 @@ public class UserSetting { |
| 295 | 298 | public void setSqlLog(Boolean sqlLog) { |
| 296 | 299 | this.sqlLog = sqlLog; |
| 297 | 300 | } |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 298 | 307 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
| ... | ... | @@ -195,6 +195,8 @@ public class Device { |
| 195 | 195 | private SipTransactionInfo sipTransactionInfo; |
| 196 | 196 | |
| 197 | 197 | |
| 198 | + | |
| 199 | + | |
| 198 | 200 | public String getDeviceId() { |
| 199 | 201 | return deviceId; |
| 200 | 202 | } |
| ... | ... | @@ -461,4 +463,20 @@ public class Device { |
| 461 | 463 | public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { |
| 462 | 464 | this.sipTransactionInfo = sipTransactionInfo; |
| 463 | 465 | } |
| 466 | + | |
| 467 | + /*======================设备主子码流逻辑START=========================*/ | |
| 468 | + @Schema(description = "开启主子码流切换的开关(false-不开启,true-开启)") | |
| 469 | + private boolean switchPrimarySubStream; | |
| 470 | + | |
| 471 | + public boolean isSwitchPrimarySubStream() { | |
| 472 | + return switchPrimarySubStream; | |
| 473 | + } | |
| 474 | + | |
| 475 | + public void setSwitchPrimarySubStream(boolean switchPrimarySubStream) { | |
| 476 | + this.switchPrimarySubStream = switchPrimarySubStream; | |
| 477 | + } | |
| 478 | + | |
| 479 | + /*======================设备主子码流逻辑END=========================*/ | |
| 480 | + | |
| 481 | + | |
| 464 | 482 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
| ... | ... | @@ -153,4 +153,30 @@ public class DeferredResultHolder { |
| 153 | 153 | map.remove(msg.getKey()); |
| 154 | 154 | } |
| 155 | 155 | } |
| 156 | + | |
| 157 | + /*============================设备主子码流逻辑START========================*/ | |
| 158 | + public static String getPlayKey(String deviceId,String channelId,boolean deviceSwitchSubStream,boolean isSubStream){ | |
| 159 | + String key = null; | |
| 160 | + if(deviceSwitchSubStream){ | |
| 161 | + key = CALLBACK_CMD_PLAY + isSubStream + deviceId + channelId; | |
| 162 | + }else { | |
| 163 | + key = CALLBACK_CMD_PLAY +deviceId + channelId; | |
| 164 | + } | |
| 165 | + return key; | |
| 166 | + } | |
| 167 | + | |
| 168 | + public static String getSnapKey(String deviceId,String channelId,boolean deviceSwitchSubStream,boolean isSubStream){ | |
| 169 | + String key = null; | |
| 170 | + if(deviceSwitchSubStream){ | |
| 171 | + key = CALLBACK_CMD_SNAP + isSubStream + deviceId + channelId; | |
| 172 | + }else { | |
| 173 | + key = CALLBACK_CMD_SNAP +deviceId + channelId; | |
| 174 | + } | |
| 175 | + return key; | |
| 176 | + } | |
| 177 | + | |
| 178 | + | |
| 179 | + /*============================设备主子码流逻辑END========================*/ | |
| 180 | + | |
| 181 | + | |
| 156 | 182 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
| ... | ... | @@ -98,7 +98,7 @@ public interface ISIPCommander { |
| 98 | 98 | * @param device 视频设备 |
| 99 | 99 | * @param channelId 预览通道 |
| 100 | 100 | */ |
| 101 | - void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; | |
| 101 | + void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,boolean isSubStream, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; | |
| 102 | 102 | |
| 103 | 103 | /** |
| 104 | 104 | * 请求回放视频流 | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| ... | ... | @@ -266,7 +266,7 @@ public class SIPCommander implements ISIPCommander { |
| 266 | 266 | * @param errorEvent sip错误订阅 |
| 267 | 267 | */ |
| 268 | 268 | @Override |
| 269 | - public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, | |
| 269 | + public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,boolean isSubStream, | |
| 270 | 270 | ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { |
| 271 | 271 | String stream = ssrcInfo.getStream(); |
| 272 | 272 | |
| ... | ... | @@ -341,6 +341,22 @@ public class SIPCommander implements ISIPCommander { |
| 341 | 341 | } |
| 342 | 342 | } |
| 343 | 343 | |
| 344 | + if( device.isSwitchPrimarySubStream() ){ | |
| 345 | + if("TP-LINK".equals(device.getManufacturer())){ | |
| 346 | + if (isSubStream){ | |
| 347 | + content.append("a=streamMode:sub\r\n"); | |
| 348 | + }else { | |
| 349 | + content.append("a=streamMode:main\r\n"); | |
| 350 | + } | |
| 351 | + }else { | |
| 352 | + if (isSubStream){ | |
| 353 | + content.append("a=streamprofile:1\r\n"); | |
| 354 | + }else { | |
| 355 | + content.append("a=streamprofile:0\r\n"); | |
| 356 | + } | |
| 357 | + } | |
| 358 | + } | |
| 359 | + | |
| 344 | 360 | content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc |
| 345 | 361 | // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率 |
| 346 | 362 | // content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 未发现支持此特性的设备 |
| ... | ... | @@ -356,7 +372,11 @@ public class SIPCommander implements ISIPCommander { |
| 356 | 372 | // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值 |
| 357 | 373 | ResponseEvent responseEvent = (ResponseEvent) e.event; |
| 358 | 374 | SIPResponse response = (SIPResponse) responseEvent.getResponse(); |
| 359 | - streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY); | |
| 375 | + if(device.isSwitchPrimarySubStream()){ | |
| 376 | + streamSession.put(device.getDeviceId(), channelId, "switch-play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY); | |
| 377 | + }else { | |
| 378 | + streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY); | |
| 379 | + } | |
| 360 | 380 | okEvent.response(e); |
| 361 | 381 | }); |
| 362 | 382 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
| ... | ... | @@ -142,8 +142,13 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In |
| 142 | 142 | // 可能是设备主动停止 |
| 143 | 143 | Device device = storager.queryVideoDeviceByChannelId(platformGbId); |
| 144 | 144 | if (device != null) { |
| 145 | - storager.stopPlay(device.getDeviceId(), channelId); | |
| 146 | - SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null); | |
| 145 | + SsrcTransaction ssrcTransactionForPlay = null; | |
| 146 | + if (device.isSwitchPrimarySubStream() ) { | |
| 147 | + ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "switch-play", null); | |
| 148 | + } else { | |
| 149 | + storager.stopPlay(device.getDeviceId(), channelId); | |
| 150 | + ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null); | |
| 151 | + } | |
| 147 | 152 | if (ssrcTransactionForPlay != null){ |
| 148 | 153 | if (ssrcTransactionForPlay.getCallId().equals(callIdHeader.getCallId())){ |
| 149 | 154 | // 释放ssrc |
| ... | ... | @@ -153,10 +158,17 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In |
| 153 | 158 | } |
| 154 | 159 | streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlay.getStream()); |
| 155 | 160 | } |
| 156 | - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); | |
| 157 | - | |
| 158 | - if (inviteInfo != null) { | |
| 161 | + InviteInfo inviteInfo = null; | |
| 162 | + if (device.isSwitchPrimarySubStream() ) { | |
| 163 | + String streamType = ssrcTransactionForPlay.getStream().split("_")[0]; | |
| 164 | + boolean isSubStream = "sub".equals(streamType); | |
| 165 | + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); | |
| 166 | + inviteStreamService.removeInviteInfo(inviteInfo.getType(),inviteInfo.getDeviceId(),inviteInfo.getChannelId(),isSubStream,inviteInfo.getStream()); | |
| 167 | + }else { | |
| 168 | + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); | |
| 159 | 169 | inviteStreamService.removeInviteInfo(inviteInfo); |
| 170 | + } | |
| 171 | + if (inviteInfo != null) { | |
| 160 | 172 | if (inviteInfo.getStreamInfo() != null) { |
| 161 | 173 | mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStream()); |
| 162 | 174 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
| ... | ... | @@ -497,7 +497,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 497 | 497 | } |
| 498 | 498 | sendRtpItem.setStreamId(streamId); |
| 499 | 499 | redisCatchStorage.updateSendRTPSever(sendRtpItem); |
| 500 | - playService.play(mediaServerItem, device.getDeviceId(), channelId, ((code, msg, data) -> { | |
| 500 | + playService.play(mediaServerItem, device.getDeviceId(), channelId,false, ((code, msg, data) -> { | |
| 501 | 501 | if (code == InviteErrorCode.SUCCESS.getCode()){ |
| 502 | 502 | hookEvent.run(code, msg, data); |
| 503 | 503 | }else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()){ | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
| ... | ... | @@ -345,10 +345,19 @@ public class ZLMHttpHookListener { |
| 345 | 345 | } |
| 346 | 346 | |
| 347 | 347 | if ("rtp".equals(param.getApp()) && !param.isRegist()) { |
| 348 | - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream()); | |
| 349 | - if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) { | |
| 350 | - inviteStreamService.removeInviteInfo(inviteInfo); | |
| 351 | - storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId()); | |
| 348 | + if(param.getStream().split("_").length == 3){ | |
| 349 | + boolean isSubStream = "sub".equals(param.getStream().split("_")[0]); | |
| 350 | + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream(), isSubStream); | |
| 351 | + if(inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY )){ | |
| 352 | + inviteStreamService.removeInviteInfo(inviteInfo.getType(),inviteInfo.getDeviceId(), | |
| 353 | + inviteInfo.getChannelId(),inviteInfo.isSubStream(),inviteInfo.getStream()); | |
| 354 | + } | |
| 355 | + }else { | |
| 356 | + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream()); | |
| 357 | + if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) { | |
| 358 | + inviteStreamService.removeInviteInfo(inviteInfo); | |
| 359 | + storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId()); | |
| 360 | + } | |
| 352 | 361 | } |
| 353 | 362 | } else { |
| 354 | 363 | if (!"rtp".equals(param.getApp())) { |
| ... | ... | @@ -476,7 +485,16 @@ public class ZLMHttpHookListener { |
| 476 | 485 | Device device = deviceService.getDevice(inviteInfo.getDeviceId()); |
| 477 | 486 | if (device != null) { |
| 478 | 487 | try { |
| 479 | - if (inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()) != null) { | |
| 488 | + InviteInfo info = null; | |
| 489 | + if(device.isSwitchPrimarySubStream()){ | |
| 490 | + boolean isSubStream = "sub".equals(param.getStream().split("_")[0]); | |
| 491 | + info = inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(),isSubStream, inviteInfo.getStream()); | |
| 492 | + }else { | |
| 493 | + info = inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); | |
| 494 | + | |
| 495 | + } | |
| 496 | + | |
| 497 | + if (info != null) { | |
| 480 | 498 | cmder.streamByeCmd(device, inviteInfo.getChannelId(), |
| 481 | 499 | inviteInfo.getStream(), null); |
| 482 | 500 | } |
| ... | ... | @@ -486,9 +504,15 @@ public class ZLMHttpHookListener { |
| 486 | 504 | } |
| 487 | 505 | } |
| 488 | 506 | |
| 489 | - inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), | |
| 490 | - inviteInfo.getChannelId(), inviteInfo.getStream()); | |
| 491 | - storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId()); | |
| 507 | + if(device.isSwitchPrimarySubStream()){ | |
| 508 | + boolean isSubStream = "sub".equals(param.getStream().split("_")[0]); | |
| 509 | + inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), | |
| 510 | + inviteInfo.getChannelId(),isSubStream, inviteInfo.getStream()); | |
| 511 | + }else { | |
| 512 | + inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), | |
| 513 | + inviteInfo.getChannelId(), inviteInfo.getStream()); | |
| 514 | + storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId()); | |
| 515 | + } | |
| 492 | 516 | return ret; |
| 493 | 517 | } |
| 494 | 518 | } else { |
| ... | ... | @@ -541,12 +565,26 @@ public class ZLMHttpHookListener { |
| 541 | 565 | |
| 542 | 566 | if ("rtp".equals(param.getApp())) { |
| 543 | 567 | String[] s = param.getStream().split("_"); |
| 544 | - if (!mediaInfo.isRtpEnable() || s.length != 2) { | |
| 568 | + if (!mediaInfo.isRtpEnable() ) { | |
| 545 | 569 | defaultResult.setResult(HookResult.SUCCESS()); |
| 546 | 570 | return defaultResult; |
| 571 | + }else if(s.length != 2 && s.length != 3 ){ | |
| 572 | + defaultResult.setResult(HookResult.SUCCESS()); | |
| 573 | + return defaultResult; | |
| 574 | + } | |
| 575 | + String deviceId = null; | |
| 576 | + String channelId = null; | |
| 577 | + boolean isSubStream = false; | |
| 578 | + if (s[0].length() < 20) { | |
| 579 | + if ("sub".equals(s[0])) { | |
| 580 | + isSubStream = true; | |
| 581 | + } | |
| 582 | + deviceId = s[1]; | |
| 583 | + channelId = s[2]; | |
| 584 | + } else { | |
| 585 | + deviceId = s[0]; | |
| 586 | + channelId = s[1]; | |
| 547 | 587 | } |
| 548 | - String deviceId = s[0]; | |
| 549 | - String channelId = s[1]; | |
| 550 | 588 | Device device = redisCatchStorage.getDevice(deviceId); |
| 551 | 589 | if (device == null) { |
| 552 | 590 | defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg())); |
| ... | ... | @@ -560,7 +598,7 @@ public class ZLMHttpHookListener { |
| 560 | 598 | logger.info("[ZLM HOOK] 流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); |
| 561 | 599 | |
| 562 | 600 | RequestMessage msg = new RequestMessage(); |
| 563 | - String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId; | |
| 601 | + String key = DeferredResultHolder.getPlayKey(deviceId, channelId, device.isSwitchPrimarySubStream(), isSubStream); | |
| 564 | 602 | boolean exist = resultHolder.exist(key, null); |
| 565 | 603 | msg.setKey(key); |
| 566 | 604 | String uuid = UUID.randomUUID().toString(); |
| ... | ... | @@ -578,7 +616,7 @@ public class ZLMHttpHookListener { |
| 578 | 616 | resultHolder.put(key, uuid, result); |
| 579 | 617 | |
| 580 | 618 | if (!exist) { |
| 581 | - playService.play(mediaInfo, deviceId, channelId, (code, message, data) -> { | |
| 619 | + playService.play(mediaInfo, deviceId, channelId,isSubStream, (code, message, data) -> { | |
| 582 | 620 | msg.setData(new HookResult(code, message)); |
| 583 | 621 | resultHolder.invokeResult(msg); |
| 584 | 622 | }); | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java
| ... | ... | @@ -4,6 +4,8 @@ import com.genersoft.iot.vmp.common.InviteInfo; |
| 4 | 4 | import com.genersoft.iot.vmp.common.InviteSessionType; |
| 5 | 5 | import com.genersoft.iot.vmp.service.bean.ErrorCallback; |
| 6 | 6 | |
| 7 | +import java.util.List; | |
| 8 | + | |
| 7 | 9 | /** |
| 8 | 10 | * 记录国标点播的状态,包括实时预览,下载,录像回放 |
| 9 | 11 | */ |
| ... | ... | @@ -70,4 +72,50 @@ public interface IInviteStreamService { |
| 70 | 72 | * 统计同一个zlm下的国标收流个数 |
| 71 | 73 | */ |
| 72 | 74 | int getStreamInfoCount(String mediaServerId); |
| 75 | + | |
| 76 | + | |
| 77 | + /*======================设备主子码流逻辑START=========================*/ | |
| 78 | + /** | |
| 79 | + * 获取点播的状态信息 | |
| 80 | + */ | |
| 81 | + InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, | |
| 82 | + String deviceId, | |
| 83 | + String channelId,boolean isSubStream); | |
| 84 | + | |
| 85 | + void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId,boolean isSubStream); | |
| 86 | + | |
| 87 | + InviteInfo getInviteInfo(InviteSessionType type, | |
| 88 | + String deviceId, | |
| 89 | + String channelId, | |
| 90 | + boolean isSubStream, | |
| 91 | + String stream); | |
| 92 | + | |
| 93 | + void removeInviteInfo(InviteSessionType type, | |
| 94 | + String deviceId, | |
| 95 | + String channelId, | |
| 96 | + boolean isSubStream, | |
| 97 | + String stream); | |
| 98 | + | |
| 99 | + void once(InviteSessionType type, String deviceId, String channelId,boolean isSubStream, String stream, ErrorCallback<Object> callback); | |
| 100 | + | |
| 101 | + void call(InviteSessionType type, String deviceId, String channelId,boolean isSubStream, String stream, int code, String msg, Object data); | |
| 102 | + | |
| 103 | + void updateInviteInfoSub(InviteInfo inviteInfo); | |
| 104 | + | |
| 105 | + /** | |
| 106 | + * 获取点播的状态信息 | |
| 107 | + */ | |
| 108 | + InviteInfo getInviteInfoByStream(InviteSessionType type, String stream,boolean isSubStream); | |
| 109 | + | |
| 110 | + /** | |
| 111 | + * 获取点播的状态信息 | |
| 112 | + */ | |
| 113 | + List<Object> getInviteInfos(InviteSessionType type, | |
| 114 | + String deviceId, | |
| 115 | + String channelId, | |
| 116 | + String stream); | |
| 117 | + /*======================设备主子码流逻辑END=========================*/ | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 73 | 121 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
| ... | ... | @@ -16,9 +16,9 @@ import java.text.ParseException; |
| 16 | 16 | */ |
| 17 | 17 | public interface IPlayService { |
| 18 | 18 | |
| 19 | - void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, | |
| 19 | + void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,boolean isSubStream, | |
| 20 | 20 | ErrorCallback<Object> callback); |
| 21 | - SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, ErrorCallback<Object> callback); | |
| 21 | + SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId,boolean isSubStream, ErrorCallback<Object> callback); | |
| 22 | 22 | |
| 23 | 23 | MediaServerItem getNewMediaServerItem(Device device); |
| 24 | 24 | |
| ... | ... | @@ -43,5 +43,5 @@ public interface IPlayService { |
| 43 | 43 | |
| 44 | 44 | void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; |
| 45 | 45 | |
| 46 | - void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback); | |
| 46 | + void getSnap(String deviceId, String channelId, String fileName,boolean isSubStream, ErrorCallback errorCallback); | |
| 47 | 47 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
| 1 | 1 | package com.genersoft.iot.vmp.service.impl; |
| 2 | 2 | |
| 3 | +import com.genersoft.iot.vmp.common.InviteSessionType; | |
| 3 | 4 | import com.genersoft.iot.vmp.common.VideoManagerConstants; |
| 4 | 5 | import com.genersoft.iot.vmp.conf.DynamicTask; |
| 5 | 6 | import com.genersoft.iot.vmp.conf.UserSetting; |
| 7 | +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; | |
| 6 | 8 | import com.genersoft.iot.vmp.gb28181.bean.*; |
| 7 | 9 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| 8 | 10 | import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; |
| 9 | 11 | import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask; |
| 10 | 12 | import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask; |
| 11 | 13 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
| 14 | +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; | |
| 12 | 15 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler; |
| 13 | 16 | import com.genersoft.iot.vmp.service.IDeviceChannelService; |
| 14 | 17 | import com.genersoft.iot.vmp.service.IDeviceService; |
| ... | ... | @@ -48,6 +51,8 @@ public class DeviceServiceImpl implements IDeviceService { |
| 48 | 51 | private final static Logger logger = LoggerFactory.getLogger(DeviceServiceImpl.class); |
| 49 | 52 | |
| 50 | 53 | @Autowired |
| 54 | + private SIPCommander cmder; | |
| 55 | + @Autowired | |
| 51 | 56 | private DynamicTask dynamicTask; |
| 52 | 57 | |
| 53 | 58 | @Autowired |
| ... | ... | @@ -130,6 +135,10 @@ public class DeviceServiceImpl implements IDeviceService { |
| 130 | 135 | } |
| 131 | 136 | sync(device); |
| 132 | 137 | }else { |
| 138 | + | |
| 139 | + if (deviceInDb != null) { | |
| 140 | + device.setSwitchPrimarySubStream(deviceInDb.isSwitchPrimarySubStream()); | |
| 141 | + } | |
| 133 | 142 | if(!device.isOnLine()){ |
| 134 | 143 | device.setOnLine(true); |
| 135 | 144 | device.setCreateTime(now); |
| ... | ... | @@ -581,6 +590,20 @@ public class DeviceServiceImpl implements IDeviceService { |
| 581 | 590 | logger.warn("更新设备时未找到设备信息"); |
| 582 | 591 | return; |
| 583 | 592 | } |
| 593 | + if(deviceInStore.isSwitchPrimarySubStream() != device.isSwitchPrimarySubStream()){ | |
| 594 | + //当修改设备的主子码流开关时,需要校验是否存在流,如果存在流则直接关闭 | |
| 595 | + List<SsrcTransaction> ssrcTransactionForAll = streamSession.getSsrcTransactionForAll(device.getDeviceId(), null, null, null); | |
| 596 | + for (SsrcTransaction ssrcTransaction: ssrcTransactionForAll) { | |
| 597 | + try { | |
| 598 | + cmder.streamByeCmd(device, ssrcTransaction.getChannelId(), ssrcTransaction.getStream(), null, null); | |
| 599 | + } catch (InvalidArgumentException | SsrcTransactionNotFoundException | ParseException | SipException e) { | |
| 600 | + throw new RuntimeException(e); | |
| 601 | + } | |
| 602 | + } | |
| 603 | + deviceChannelMapper.clearPlay(device.getDeviceId()); | |
| 604 | + inviteStreamService.clearInviteInfo(device.getDeviceId()); | |
| 605 | + } | |
| 606 | + | |
| 584 | 607 | if (!ObjectUtils.isEmpty(device.getName())) { |
| 585 | 608 | deviceInStore.setName(device.getName()); |
| 586 | 609 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java
| ... | ... | @@ -198,4 +198,164 @@ public class InviteStreamServiceImpl implements IInviteStreamService { |
| 198 | 198 | } |
| 199 | 199 | return count; |
| 200 | 200 | } |
| 201 | + | |
| 202 | + /*======================设备主子码流逻辑START=========================*/ | |
| 203 | + | |
| 204 | + @Override | |
| 205 | + public InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, String deviceId, String channelId, boolean isSubStream) { | |
| 206 | + return getInviteInfo(type, deviceId, channelId,isSubStream, null); | |
| 207 | + } | |
| 208 | + | |
| 209 | + @Override | |
| 210 | + public void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId, boolean isSubStream) { | |
| 211 | + removeInviteInfo(inviteSessionType, deviceId, channelId,isSubStream, null); | |
| 212 | + } | |
| 213 | + | |
| 214 | + @Override | |
| 215 | + public InviteInfo getInviteInfo(InviteSessionType type, String deviceId, String channelId,boolean isSubStream, String stream) { | |
| 216 | + String key = VideoManagerConstants.INVITE_PREFIX + | |
| 217 | + "_" + (type != null ? type : "*") + | |
| 218 | + "_" + (isSubStream ? "sub" : "main") + | |
| 219 | + "_" + (deviceId != null ? deviceId : "*") + | |
| 220 | + "_" + (channelId != null ? channelId : "*") + | |
| 221 | + "_" + (stream != null ? stream : "*"); | |
| 222 | + List<Object> scanResult = RedisUtil.scan(redisTemplate, key); | |
| 223 | + if (scanResult.size() != 1) { | |
| 224 | + return null; | |
| 225 | + } | |
| 226 | + return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0)); | |
| 227 | + } | |
| 228 | + | |
| 229 | + @Override | |
| 230 | + public void removeInviteInfo(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream) { | |
| 231 | + String scanKey = VideoManagerConstants.INVITE_PREFIX + | |
| 232 | + "_" + (type != null ? type : "*") + | |
| 233 | + "_" + (isSubStream ? "sub" : "main") + | |
| 234 | + "_" + (deviceId != null ? deviceId : "*") + | |
| 235 | + "_" + (channelId != null ? channelId : "*") + | |
| 236 | + "_" + (stream != null ? stream : "*"); | |
| 237 | + List<Object> scanResult = RedisUtil.scan(redisTemplate, scanKey); | |
| 238 | + if (scanResult.size() > 0) { | |
| 239 | + for (Object keyObj : scanResult) { | |
| 240 | + String key = (String) keyObj; | |
| 241 | + InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(key); | |
| 242 | + if (inviteInfo == null) { | |
| 243 | + continue; | |
| 244 | + } | |
| 245 | + redisTemplate.delete(key); | |
| 246 | + inviteErrorCallbackMap.remove(buildKey(type, deviceId, channelId, inviteInfo.getStream())); | |
| 247 | + } | |
| 248 | + } | |
| 249 | + } | |
| 250 | + | |
| 251 | + @Override | |
| 252 | + public void once(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream, ErrorCallback<Object> callback) { | |
| 253 | + String key = buildSubStreamKey(type, deviceId, channelId,isSubStream, stream); | |
| 254 | + List<ErrorCallback<Object>> callbacks = inviteErrorCallbackMap.get(key); | |
| 255 | + if (callbacks == null) { | |
| 256 | + callbacks = new CopyOnWriteArrayList<>(); | |
| 257 | + inviteErrorCallbackMap.put(key, callbacks); | |
| 258 | + } | |
| 259 | + callbacks.add(callback); | |
| 260 | + } | |
| 261 | + | |
| 262 | + @Override | |
| 263 | + public void call(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream, int code, String msg, Object data) { | |
| 264 | + String key = buildSubStreamKey(type, deviceId, channelId,isSubStream, stream); | |
| 265 | + List<ErrorCallback<Object>> callbacks = inviteErrorCallbackMap.get(key); | |
| 266 | + if (callbacks == null) { | |
| 267 | + return; | |
| 268 | + } | |
| 269 | + for (ErrorCallback<Object> callback : callbacks) { | |
| 270 | + callback.run(code, msg, data); | |
| 271 | + } | |
| 272 | + inviteErrorCallbackMap.remove(key); | |
| 273 | + } | |
| 274 | + | |
| 275 | + | |
| 276 | + private String buildSubStreamKey(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream) { | |
| 277 | + String key = type + "_" + (isSubStream ? "sub":"main") + "_" + deviceId + "_" + channelId; | |
| 278 | + // 如果ssrc为null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite | |
| 279 | + if (stream != null) { | |
| 280 | + key += ("_" + stream); | |
| 281 | + } | |
| 282 | + return key; | |
| 283 | + } | |
| 284 | + @Override | |
| 285 | + public void updateInviteInfoSub(InviteInfo inviteInfo) { | |
| 286 | + if (inviteInfo == null || (inviteInfo.getDeviceId() == null || inviteInfo.getChannelId() == null)) { | |
| 287 | + logger.warn("[更新Invite信息],参数不全: {}", JSON.toJSON(inviteInfo)); | |
| 288 | + return; | |
| 289 | + } | |
| 290 | + InviteInfo inviteInfoForUpdate = null; | |
| 291 | + | |
| 292 | + if (InviteSessionStatus.ready == inviteInfo.getStatus()) { | |
| 293 | + if (inviteInfo.getDeviceId() == null | |
| 294 | + || inviteInfo.getChannelId() == null | |
| 295 | + || inviteInfo.getType() == null | |
| 296 | + || inviteInfo.getStream() == null | |
| 297 | + ) { | |
| 298 | + return; | |
| 299 | + } | |
| 300 | + inviteInfoForUpdate = inviteInfo; | |
| 301 | + } else { | |
| 302 | + InviteInfo inviteInfoInRedis = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), | |
| 303 | + inviteInfo.getChannelId(),inviteInfo.isSubStream(), inviteInfo.getStream()); | |
| 304 | + if (inviteInfoInRedis == null) { | |
| 305 | + logger.warn("[更新Invite信息],未从缓存中读取到Invite信息: deviceId: {}, channel: {}, stream: {}", | |
| 306 | + inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); | |
| 307 | + return; | |
| 308 | + } | |
| 309 | + if (inviteInfo.getStreamInfo() != null) { | |
| 310 | + inviteInfoInRedis.setStreamInfo(inviteInfo.getStreamInfo()); | |
| 311 | + } | |
| 312 | + if (inviteInfo.getSsrcInfo() != null) { | |
| 313 | + inviteInfoInRedis.setSsrcInfo(inviteInfo.getSsrcInfo()); | |
| 314 | + } | |
| 315 | + if (inviteInfo.getStreamMode() != null) { | |
| 316 | + inviteInfoInRedis.setStreamMode(inviteInfo.getStreamMode()); | |
| 317 | + } | |
| 318 | + if (inviteInfo.getReceiveIp() != null) { | |
| 319 | + inviteInfoInRedis.setReceiveIp(inviteInfo.getReceiveIp()); | |
| 320 | + } | |
| 321 | + if (inviteInfo.getReceivePort() != null) { | |
| 322 | + inviteInfoInRedis.setReceivePort(inviteInfo.getReceivePort()); | |
| 323 | + } | |
| 324 | + if (inviteInfo.getStatus() != null) { | |
| 325 | + inviteInfoInRedis.setStatus(inviteInfo.getStatus()); | |
| 326 | + } | |
| 327 | + | |
| 328 | + inviteInfoForUpdate = inviteInfoInRedis; | |
| 329 | + | |
| 330 | + } | |
| 331 | + String key = VideoManagerConstants.INVITE_PREFIX + | |
| 332 | + "_" + inviteInfoForUpdate.getType() + | |
| 333 | + "_" + (inviteInfoForUpdate.isSubStream() ? "sub":"main") + | |
| 334 | + "_" + inviteInfoForUpdate.getDeviceId() + | |
| 335 | + "_" + inviteInfoForUpdate.getChannelId() + | |
| 336 | + "_" + inviteInfoForUpdate.getStream(); | |
| 337 | + redisTemplate.opsForValue().set(key, inviteInfoForUpdate); | |
| 338 | + } | |
| 339 | + | |
| 340 | + @Override | |
| 341 | + public InviteInfo getInviteInfoByStream(InviteSessionType type, String stream, boolean isSubStream) { | |
| 342 | + return getInviteInfo(type, null, null,isSubStream, stream); | |
| 343 | + } | |
| 344 | + | |
| 345 | + @Override | |
| 346 | + public List<Object> getInviteInfos(InviteSessionType type, String deviceId, String channelId, String stream) { | |
| 347 | + String key = VideoManagerConstants.INVITE_PREFIX + | |
| 348 | + "_" + (type != null ? type : "*") + | |
| 349 | + "_" + (deviceId != null ? deviceId : "*") + | |
| 350 | + "_" + (channelId != null ? channelId : "*") + | |
| 351 | + "_" + (stream != null ? stream : "*"); | |
| 352 | + List<Object> scanResult = RedisUtil.scan(redisTemplate, key); | |
| 353 | + return scanResult; | |
| 354 | + } | |
| 355 | + | |
| 356 | + /*======================设备主子码流逻辑END=========================*/ | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 201 | 361 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
| ... | ... | @@ -115,28 +115,43 @@ public class PlayServiceImpl implements IPlayService { |
| 115 | 115 | |
| 116 | 116 | |
| 117 | 117 | @Override |
| 118 | - public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, ErrorCallback<Object> callback) { | |
| 118 | + public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId,boolean isSubStream, ErrorCallback<Object> callback) { | |
| 119 | 119 | if (mediaServerItem == null) { |
| 120 | 120 | throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm"); |
| 121 | 121 | } |
| 122 | 122 | |
| 123 | 123 | Device device = redisCatchStorage.getDevice(deviceId); |
| 124 | - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); | |
| 125 | - | |
| 124 | + InviteInfo inviteInfo; | |
| 125 | + if(device.isSwitchPrimarySubStream()){ | |
| 126 | + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); | |
| 127 | + }else { | |
| 128 | + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); | |
| 129 | + } | |
| 126 | 130 | if (inviteInfo != null ) { |
| 127 | 131 | if (inviteInfo.getStreamInfo() == null) { |
| 128 | 132 | // 点播发起了但是尚未成功, 仅注册回调等待结果即可 |
| 129 | - inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); | |
| 133 | + if(device.isSwitchPrimarySubStream()){ | |
| 134 | + inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId,isSubStream, null, callback); | |
| 135 | + }else { | |
| 136 | + inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); | |
| 137 | + } | |
| 130 | 138 | return inviteInfo.getSsrcInfo(); |
| 131 | 139 | }else { |
| 132 | 140 | StreamInfo streamInfo = inviteInfo.getStreamInfo(); |
| 133 | 141 | String streamId = streamInfo.getStream(); |
| 134 | 142 | if (streamId == null) { |
| 135 | 143 | callback.run(InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), "点播失败, redis缓存streamId等于null", null); |
| 136 | - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 137 | - InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), | |
| 138 | - "点播失败, redis缓存streamId等于null", | |
| 139 | - null); | |
| 144 | + if(device.isSwitchPrimarySubStream()){ | |
| 145 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, | |
| 146 | + InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), | |
| 147 | + "点播失败, redis缓存streamId等于null", | |
| 148 | + null); | |
| 149 | + }else { | |
| 150 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 151 | + InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), | |
| 152 | + "点播失败, redis缓存streamId等于null", | |
| 153 | + null); | |
| 154 | + } | |
| 140 | 155 | return inviteInfo.getSsrcInfo(); |
| 141 | 156 | } |
| 142 | 157 | String mediaServerId = streamInfo.getMediaServerId(); |
| ... | ... | @@ -145,41 +160,64 @@ public class PlayServiceImpl implements IPlayService { |
| 145 | 160 | Boolean ready = zlmrtpServerFactory.isStreamReady(mediaInfo, "rtp", streamId); |
| 146 | 161 | if (ready != null && ready) { |
| 147 | 162 | callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); |
| 148 | - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 149 | - InviteErrorCode.SUCCESS.getCode(), | |
| 150 | - InviteErrorCode.SUCCESS.getMsg(), | |
| 151 | - streamInfo); | |
| 163 | + if(device.isSwitchPrimarySubStream()){ | |
| 164 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, | |
| 165 | + InviteErrorCode.SUCCESS.getCode(), | |
| 166 | + InviteErrorCode.SUCCESS.getMsg(), | |
| 167 | + streamInfo); | |
| 168 | + }else { | |
| 169 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 170 | + InviteErrorCode.SUCCESS.getCode(), | |
| 171 | + InviteErrorCode.SUCCESS.getMsg(), | |
| 172 | + streamInfo); | |
| 173 | + } | |
| 152 | 174 | return inviteInfo.getSsrcInfo(); |
| 153 | 175 | }else { |
| 154 | 176 | // 点播发起了但是尚未成功, 仅注册回调等待结果即可 |
| 155 | - inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); | |
| 156 | - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); | |
| 157 | - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); | |
| 177 | + if(device.isSwitchPrimarySubStream()) { | |
| 178 | + inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); | |
| 179 | + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); | |
| 180 | + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); | |
| 181 | + }else { | |
| 182 | + inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId,isSubStream, null, callback); | |
| 183 | + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); | |
| 184 | + } | |
| 158 | 185 | } |
| 159 | 186 | } |
| 160 | 187 | } |
| 161 | 188 | |
| 162 | 189 | String streamId = null; |
| 163 | 190 | if (mediaServerItem.isRtpEnable()) { |
| 164 | - streamId = String.format("%s_%s", device.getDeviceId(), channelId); | |
| 191 | + if(device.isSwitchPrimarySubStream()){ | |
| 192 | + streamId = StreamInfo.getPlayStream(deviceId, channelId, isSubStream); | |
| 193 | + }else { | |
| 194 | + streamId = String.format("%s_%s", device.getDeviceId(), channelId); | |
| 195 | + } | |
| 165 | 196 | } |
| 166 | 197 | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam()); |
| 167 | 198 | if (ssrcInfo == null) { |
| 168 | 199 | callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), null); |
| 169 | - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 170 | - InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), | |
| 171 | - InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), | |
| 172 | - null); | |
| 200 | + if(device.isSwitchPrimarySubStream()){ | |
| 201 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, | |
| 202 | + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), | |
| 203 | + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), | |
| 204 | + null); | |
| 205 | + }else { | |
| 206 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 207 | + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), | |
| 208 | + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), | |
| 209 | + null); | |
| 210 | + } | |
| 173 | 211 | return null; |
| 174 | 212 | } |
| 175 | 213 | // TODO 记录点播的状态 |
| 176 | - play(mediaServerItem, ssrcInfo, device, channelId, callback); | |
| 214 | + play(mediaServerItem, ssrcInfo, device, channelId,isSubStream, callback); | |
| 177 | 215 | return ssrcInfo; |
| 178 | 216 | } |
| 179 | 217 | |
| 180 | 218 | |
| 181 | 219 | @Override |
| 182 | - public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, | |
| 220 | + public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,boolean isSubStream, | |
| 183 | 221 | ErrorCallback<Object> callback) { |
| 184 | 222 | |
| 185 | 223 | if (mediaServerItem == null || ssrcInfo == null) { |
| ... | ... | @@ -188,8 +226,11 @@ public class PlayServiceImpl implements IPlayService { |
| 188 | 226 | null); |
| 189 | 227 | return; |
| 190 | 228 | } |
| 191 | - logger.info("[点播开始] deviceId: {}, channelId: {},收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); | |
| 192 | - | |
| 229 | + if( device.isSwitchPrimarySubStream() ){ | |
| 230 | + logger.info("[点播开始] deviceId: {}, channelId: {},码流类型:{},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId,isSubStream ? "辅码流" : "主码流", ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); | |
| 231 | + }else { | |
| 232 | + logger.info("[点播开始] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); | |
| 233 | + } | |
| 193 | 234 | //端口获取失败的ssrcInfo 没有必要发送点播指令 |
| 194 | 235 | if (ssrcInfo.getPort() <= 0) { |
| 195 | 236 | logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo); |
| ... | ... | @@ -198,23 +239,50 @@ public class PlayServiceImpl implements IPlayService { |
| 198 | 239 | streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); |
| 199 | 240 | |
| 200 | 241 | callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null); |
| 201 | - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 202 | - InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null); | |
| 242 | + if(device.isSwitchPrimarySubStream()){ | |
| 243 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, | |
| 244 | + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null); | |
| 245 | + }else { | |
| 246 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 247 | + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null); | |
| 248 | + } | |
| 203 | 249 | return; |
| 204 | 250 | } |
| 205 | 251 | |
| 206 | 252 | // 初始化redis中的invite消息状态 |
| 207 | - InviteInfo inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo, | |
| 208 | - mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, | |
| 209 | - InviteSessionStatus.ready); | |
| 210 | - inviteStreamService.updateInviteInfo(inviteInfo); | |
| 253 | + InviteInfo inviteInfo; | |
| 254 | + | |
| 255 | + if(device.isSwitchPrimarySubStream()){ | |
| 256 | + // 初始化redis中的invite消息状态 | |
| 257 | + inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channelId,isSubStream, ssrcInfo.getStream(), ssrcInfo, | |
| 258 | + mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, | |
| 259 | + InviteSessionStatus.ready); | |
| 260 | + inviteStreamService.updateInviteInfoSub(inviteInfo); | |
| 261 | + }else { | |
| 262 | + // 初始化redis中的invite消息状态 | |
| 263 | + inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo, | |
| 264 | + mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, | |
| 265 | + InviteSessionStatus.ready); | |
| 266 | + inviteStreamService.updateInviteInfo(inviteInfo); | |
| 267 | + } | |
| 211 | 268 | // 超时处理 |
| 212 | 269 | String timeOutTaskKey = UUID.randomUUID().toString(); |
| 213 | 270 | dynamicTask.startDelay(timeOutTaskKey, () -> { |
| 214 | 271 | // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况 |
| 215 | - InviteInfo inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); | |
| 272 | + InviteInfo inviteInfoForTimeOut; | |
| 273 | + if(device.isSwitchPrimarySubStream()){ | |
| 274 | + // 初始化redis中的invite消息状态 | |
| 275 | + inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); | |
| 276 | + }else { | |
| 277 | + // 初始化redis中的invite消息状态 | |
| 278 | + inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); | |
| 279 | + } | |
| 216 | 280 | if (inviteInfoForTimeOut == null || inviteInfoForTimeOut.getStreamInfo() == null) { |
| 217 | - logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getPort(), ssrcInfo.getSsrc()); | |
| 281 | + if( device.isSwitchPrimarySubStream()){ | |
| 282 | + logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},码流类型:{},端口:{}, SSRC: {}", device.getDeviceId(), channelId,isSubStream ? "辅码流" : "主码流", ssrcInfo.getPort(), ssrcInfo.getSsrc()); | |
| 283 | + }else { | |
| 284 | + logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getPort(), ssrcInfo.getSsrc()); | |
| 285 | + } | |
| 218 | 286 | // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 |
| 219 | 287 | // InviteInfo inviteInfoForTimeout = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.play, device.getDeviceId(), channelId); |
| 220 | 288 | // if (inviteInfoForTimeout == null) { |
| ... | ... | @@ -226,10 +294,16 @@ public class PlayServiceImpl implements IPlayService { |
| 226 | 294 | // // TODO 发送cancel |
| 227 | 295 | // } |
| 228 | 296 | callback.run(InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); |
| 229 | - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 230 | - InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); | |
| 297 | + if( device.isSwitchPrimarySubStream()){ | |
| 298 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, | |
| 299 | + InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); | |
| 300 | + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); | |
| 231 | 301 | |
| 232 | - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); | |
| 302 | + }else { | |
| 303 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 304 | + InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); | |
| 305 | + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); | |
| 306 | + } | |
| 233 | 307 | try { |
| 234 | 308 | cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); |
| 235 | 309 | } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { |
| ... | ... | @@ -247,25 +321,42 @@ public class PlayServiceImpl implements IPlayService { |
| 247 | 321 | }, userSetting.getPlayTimeout()); |
| 248 | 322 | |
| 249 | 323 | try { |
| 250 | - cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { | |
| 324 | + cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId,isSubStream, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { | |
| 251 | 325 | logger.info("收到订阅消息: " + response.toJSONString()); |
| 252 | 326 | dynamicTask.stop(timeOutTaskKey); |
| 253 | 327 | // hook响应 |
| 254 | - StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId); | |
| 328 | + StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId,isSubStream); | |
| 255 | 329 | if (streamInfo == null){ |
| 256 | 330 | callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), |
| 257 | 331 | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); |
| 258 | - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 259 | - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), | |
| 260 | - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); | |
| 332 | + if( device.isSwitchPrimarySubStream()){ | |
| 333 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, | |
| 334 | + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), | |
| 335 | + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); | |
| 336 | + }else { | |
| 337 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 338 | + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), | |
| 339 | + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); | |
| 340 | + } | |
| 261 | 341 | return; |
| 262 | 342 | } |
| 263 | 343 | callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); |
| 264 | - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 265 | - InviteErrorCode.SUCCESS.getCode(), | |
| 266 | - InviteErrorCode.SUCCESS.getMsg(), | |
| 267 | - streamInfo); | |
| 268 | - logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId); | |
| 344 | + if( device.isSwitchPrimarySubStream()){ | |
| 345 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, | |
| 346 | + InviteErrorCode.SUCCESS.getCode(), | |
| 347 | + InviteErrorCode.SUCCESS.getMsg(), | |
| 348 | + streamInfo); | |
| 349 | + }else { | |
| 350 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 351 | + InviteErrorCode.SUCCESS.getCode(), | |
| 352 | + InviteErrorCode.SUCCESS.getMsg(), | |
| 353 | + streamInfo); | |
| 354 | + } | |
| 355 | + if( device.isSwitchPrimarySubStream() ){ | |
| 356 | + logger.info("[点播成功] deviceId: {}, channelId: {},码流类型:{}", device.getDeviceId(), channelId,isSubStream ? "辅码流" : "主码流"); | |
| 357 | + }else { | |
| 358 | + logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId); | |
| 359 | + } | |
| 269 | 360 | String streamUrl; |
| 270 | 361 | if (mediaServerItemInuse.getRtspPort() != 0) { |
| 271 | 362 | streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", ssrcInfo.getStream()); |
| ... | ... | @@ -321,9 +412,15 @@ public class PlayServiceImpl implements IPlayService { |
| 321 | 412 | |
| 322 | 413 | callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), |
| 323 | 414 | InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); |
| 324 | - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 325 | - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), | |
| 326 | - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); | |
| 415 | + if(device.isSwitchPrimarySubStream()){ | |
| 416 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, | |
| 417 | + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), | |
| 418 | + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); | |
| 419 | + }else { | |
| 420 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 421 | + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), | |
| 422 | + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); | |
| 423 | + } | |
| 327 | 424 | } |
| 328 | 425 | } |
| 329 | 426 | return; |
| ... | ... | @@ -340,9 +437,15 @@ public class PlayServiceImpl implements IPlayService { |
| 340 | 437 | |
| 341 | 438 | callback.run(InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getCode(), |
| 342 | 439 | InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getMsg(), null); |
| 343 | - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 344 | - InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getCode(), | |
| 345 | - InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getMsg(), null); | |
| 440 | + if( device.isSwitchPrimarySubStream()){ | |
| 441 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, | |
| 442 | + InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getCode(), | |
| 443 | + InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getMsg(), null); | |
| 444 | + }else { | |
| 445 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 446 | + InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getCode(), | |
| 447 | + InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getMsg(), null); | |
| 448 | + } | |
| 346 | 449 | |
| 347 | 450 | return; |
| 348 | 451 | } |
| ... | ... | @@ -358,21 +461,34 @@ public class PlayServiceImpl implements IPlayService { |
| 358 | 461 | logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString()); |
| 359 | 462 | dynamicTask.stop(timeOutTaskKey); |
| 360 | 463 | // hook响应 |
| 361 | - StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId); | |
| 464 | + StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId,isSubStream); | |
| 362 | 465 | if (streamInfo == null){ |
| 363 | 466 | callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), |
| 364 | 467 | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); |
| 365 | - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 366 | - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), | |
| 367 | - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); | |
| 468 | + if( device.isSwitchPrimarySubStream()){ | |
| 469 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, | |
| 470 | + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), | |
| 471 | + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); | |
| 472 | + }else { | |
| 473 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 474 | + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), | |
| 475 | + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); | |
| 476 | + } | |
| 368 | 477 | return; |
| 369 | 478 | } |
| 370 | 479 | callback.run(InviteErrorCode.SUCCESS.getCode(), |
| 371 | 480 | InviteErrorCode.SUCCESS.getMsg(), streamInfo); |
| 372 | - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 373 | - InviteErrorCode.SUCCESS.getCode(), | |
| 374 | - InviteErrorCode.SUCCESS.getMsg(), | |
| 375 | - streamInfo); | |
| 481 | + if( device.isSwitchPrimarySubStream()){ | |
| 482 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, | |
| 483 | + InviteErrorCode.SUCCESS.getCode(), | |
| 484 | + InviteErrorCode.SUCCESS.getMsg(), | |
| 485 | + streamInfo); | |
| 486 | + }else { | |
| 487 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 488 | + InviteErrorCode.SUCCESS.getCode(), | |
| 489 | + InviteErrorCode.SUCCESS.getMsg(), | |
| 490 | + streamInfo); | |
| 491 | + } | |
| 376 | 492 | }); |
| 377 | 493 | return; |
| 378 | 494 | } |
| ... | ... | @@ -395,9 +511,15 @@ public class PlayServiceImpl implements IPlayService { |
| 395 | 511 | |
| 396 | 512 | callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), |
| 397 | 513 | "下级自定义了ssrc,重新设置收流信息失败", null); |
| 398 | - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 399 | - InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), | |
| 400 | - "下级自定义了ssrc,重新设置收流信息失败", null); | |
| 514 | + if( device.isSwitchPrimarySubStream()){ | |
| 515 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, | |
| 516 | + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), | |
| 517 | + "下级自定义了ssrc,重新设置收流信息失败", null); | |
| 518 | + }else { | |
| 519 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 520 | + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), | |
| 521 | + "下级自定义了ssrc,重新设置收流信息失败", null); | |
| 522 | + } | |
| 401 | 523 | |
| 402 | 524 | }else { |
| 403 | 525 | ssrcInfo.setSsrc(ssrcInResponse); |
| ... | ... | @@ -408,7 +530,11 @@ public class PlayServiceImpl implements IPlayService { |
| 408 | 530 | logger.info("[点播消息] 收到invite 200, 下级自定义了ssrc, 但是当前模式无需修正"); |
| 409 | 531 | } |
| 410 | 532 | } |
| 411 | - inviteStreamService.updateInviteInfo(inviteInfo); | |
| 533 | + if(device.isSwitchPrimarySubStream()){ | |
| 534 | + inviteStreamService.updateInviteInfoSub(inviteInfo); | |
| 535 | + }else { | |
| 536 | + inviteStreamService.updateInviteInfo(inviteInfo); | |
| 537 | + } | |
| 412 | 538 | }, (event) -> { |
| 413 | 539 | dynamicTask.stop(timeOutTaskKey); |
| 414 | 540 | mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); |
| ... | ... | @@ -419,11 +545,19 @@ public class PlayServiceImpl implements IPlayService { |
| 419 | 545 | |
| 420 | 546 | callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_ERROR.getCode(), |
| 421 | 547 | String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null); |
| 422 | - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 423 | - InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), | |
| 424 | - String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null); | |
| 548 | + if( device.isSwitchPrimarySubStream()){ | |
| 549 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, | |
| 550 | + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), | |
| 551 | + String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null); | |
| 425 | 552 | |
| 426 | - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); | |
| 553 | + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); | |
| 554 | + }else { | |
| 555 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 556 | + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), | |
| 557 | + String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null); | |
| 558 | + | |
| 559 | + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); | |
| 560 | + } | |
| 427 | 561 | }); |
| 428 | 562 | } catch (InvalidArgumentException | SipException | ParseException e) { |
| 429 | 563 | |
| ... | ... | @@ -437,27 +571,51 @@ public class PlayServiceImpl implements IPlayService { |
| 437 | 571 | |
| 438 | 572 | callback.run(InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), |
| 439 | 573 | InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); |
| 440 | - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 441 | - InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), | |
| 442 | - InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); | |
| 574 | + if( device.isSwitchPrimarySubStream()){ | |
| 575 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, | |
| 576 | + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), | |
| 577 | + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); | |
| 578 | + | |
| 579 | + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); | |
| 580 | + }else { | |
| 581 | + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, | |
| 582 | + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), | |
| 583 | + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); | |
| 443 | 584 | |
| 444 | - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); | |
| 585 | + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); | |
| 586 | + } | |
| 445 | 587 | } |
| 446 | 588 | } |
| 447 | 589 | |
| 448 | - private StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId) { | |
| 449 | - StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId); | |
| 590 | + private StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId,boolean isSubStream) { | |
| 591 | + StreamInfo streamInfo = null; | |
| 592 | + Device device = redisCatchStorage.getDevice(deviceId); | |
| 593 | + if( device.isSwitchPrimarySubStream() ){ | |
| 594 | + streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId,isSubStream); | |
| 595 | + }else { | |
| 596 | + streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId); | |
| 597 | + } | |
| 450 | 598 | if (streamInfo != null) { |
| 451 | - DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); | |
| 452 | - if (deviceChannel != null) { | |
| 453 | - deviceChannel.setStreamId(streamInfo.getStream()); | |
| 454 | - storager.startPlay(deviceId, channelId, streamInfo.getStream()); | |
| 599 | + InviteInfo inviteInfo; | |
| 600 | + if(device.isSwitchPrimarySubStream()){ | |
| 601 | + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); | |
| 602 | + }else { | |
| 603 | + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); | |
| 604 | + if (deviceChannel != null) { | |
| 605 | + deviceChannel.setStreamId(streamInfo.getStream()); | |
| 606 | + storager.startPlay(deviceId, channelId, streamInfo.getStream()); | |
| 607 | + } | |
| 608 | + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); | |
| 455 | 609 | } |
| 456 | - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); | |
| 457 | 610 | if (inviteInfo != null) { |
| 458 | 611 | inviteInfo.setStatus(InviteSessionStatus.ok); |
| 459 | 612 | inviteInfo.setStreamInfo(streamInfo); |
| 460 | - inviteStreamService.updateInviteInfo(inviteInfo); | |
| 613 | + if(device.isSwitchPrimarySubStream()){ | |
| 614 | + inviteStreamService.updateInviteInfoSub(inviteInfo); | |
| 615 | + }else { | |
| 616 | + inviteStreamService.updateInviteInfo(inviteInfo); | |
| 617 | + } | |
| 618 | + | |
| 461 | 619 | } |
| 462 | 620 | } |
| 463 | 621 | return streamInfo; |
| ... | ... | @@ -994,6 +1152,7 @@ public class PlayServiceImpl implements IPlayService { |
| 994 | 1152 | return streamInfo; |
| 995 | 1153 | } |
| 996 | 1154 | |
| 1155 | + | |
| 997 | 1156 | @Override |
| 998 | 1157 | public void zlmServerOffline(String mediaServerId) { |
| 999 | 1158 | // 处理正在向上推流的上级平台 |
| ... | ... | @@ -1130,14 +1289,18 @@ public class PlayServiceImpl implements IPlayService { |
| 1130 | 1289 | } |
| 1131 | 1290 | |
| 1132 | 1291 | @Override |
| 1133 | - public void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback) { | |
| 1292 | + public void getSnap(String deviceId, String channelId, String fileName,boolean isSubStream, ErrorCallback errorCallback) { | |
| 1134 | 1293 | Device device = deviceService.getDevice(deviceId); |
| 1135 | 1294 | if (device == null) { |
| 1136 | 1295 | errorCallback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), null); |
| 1137 | 1296 | return; |
| 1138 | 1297 | } |
| 1139 | - | |
| 1140 | - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); | |
| 1298 | + InviteInfo inviteInfo; | |
| 1299 | + if(device.isSwitchPrimarySubStream()){ | |
| 1300 | + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); | |
| 1301 | + }else { | |
| 1302 | + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); | |
| 1303 | + } | |
| 1141 | 1304 | if (inviteInfo != null) { |
| 1142 | 1305 | if (inviteInfo.getStreamInfo() != null) { |
| 1143 | 1306 | // 已存在线直接截图 |
| ... | ... | @@ -1163,11 +1326,11 @@ public class PlayServiceImpl implements IPlayService { |
| 1163 | 1326 | } |
| 1164 | 1327 | |
| 1165 | 1328 | MediaServerItem newMediaServerItem = getNewMediaServerItem(device); |
| 1166 | - play(newMediaServerItem, deviceId, channelId, (code, msg, data)->{ | |
| 1329 | + play(newMediaServerItem, deviceId, channelId,isSubStream, (code, msg, data)->{ | |
| 1167 | 1330 | if (code == InviteErrorCode.SUCCESS.getCode()) { |
| 1168 | 1331 | InviteInfo inviteInfoForPlay = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); |
| 1169 | 1332 | if (inviteInfoForPlay != null && inviteInfoForPlay.getStreamInfo() != null) { |
| 1170 | - getSnap(deviceId, channelId, fileName, errorCallback); | |
| 1333 | + getSnap(deviceId, channelId, fileName,isSubStream, errorCallback); | |
| 1171 | 1334 | }else { |
| 1172 | 1335 | errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); |
| 1173 | 1336 | } |
| ... | ... | @@ -1177,4 +1340,17 @@ public class PlayServiceImpl implements IPlayService { |
| 1177 | 1340 | }); |
| 1178 | 1341 | } |
| 1179 | 1342 | |
| 1343 | + | |
| 1344 | + /*======================设备主子码流逻辑START=========================*/ | |
| 1345 | + public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId,boolean isSubStream) { | |
| 1346 | + String streamId = resonse.getString("stream"); | |
| 1347 | + JSONArray tracks = resonse.getJSONArray("tracks"); | |
| 1348 | + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem, "rtp", streamId, tracks, null); | |
| 1349 | + streamInfo.setDeviceID(deviceId); | |
| 1350 | + streamInfo.setChannelId(channelId); | |
| 1351 | + streamInfo.setSubStream(isSubStream); | |
| 1352 | + return streamInfo; | |
| 1353 | + } | |
| 1354 | + /*======================设备主子码流逻辑END=========================*/ | |
| 1355 | + | |
| 1180 | 1356 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
| ... | ... | @@ -450,4 +450,11 @@ public interface DeviceChannelMapper { |
| 450 | 450 | |
| 451 | 451 | @Select("select count(1) from wvp_device_channel") |
| 452 | 452 | int getAllChannelCount(); |
| 453 | + | |
| 454 | + | |
| 455 | + /*=================设备主子码流逻辑START==============*/ | |
| 456 | + @Update(value = {"UPDATE wvp_device_channel SET stream_id=null WHERE device_id=#{deviceId}"}) | |
| 457 | + void clearPlay(String deviceId); | |
| 458 | + /*=================设备主子码流逻辑END==============*/ | |
| 459 | + | |
| 453 | 460 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
| ... | ... | @@ -43,6 +43,7 @@ public interface DeviceMapper { |
| 43 | 43 | "tree_type," + |
| 44 | 44 | "on_line," + |
| 45 | 45 | "media_server_id," + |
| 46 | + "switch_primary_sub_stream," + | |
| 46 | 47 | "(SELECT count(0) FROM wvp_device_channel WHERE device_id=wvp_device.device_id) as channel_count "+ |
| 47 | 48 | " FROM wvp_device WHERE device_id = #{deviceId}") |
| 48 | 49 | Device getDeviceByDeviceId(String deviceId); |
| ... | ... | @@ -161,6 +162,7 @@ public interface DeviceMapper { |
| 161 | 162 | "tree_type,"+ |
| 162 | 163 | "on_line,"+ |
| 163 | 164 | "media_server_id,"+ |
| 165 | + "switch_primary_sub_stream switchPrimarySubStream,"+ | |
| 164 | 166 | "(SELECT count(0) FROM wvp_device_channel WHERE device_id=de.device_id) as channel_count " + |
| 165 | 167 | "FROM wvp_device de" + |
| 166 | 168 | "<if test=\"onLine != null\"> where on_line=${onLine}</if>"+ |
| ... | ... | @@ -253,6 +255,7 @@ public interface DeviceMapper { |
| 253 | 255 | "<if test=\"asMessageChannel != null\">, as_message_channel=#{asMessageChannel}</if>" + |
| 254 | 256 | "<if test=\"geoCoordSys != null\">, geo_coord_sys=#{geoCoordSys}</if>" + |
| 255 | 257 | "<if test=\"treeType != null\">, tree_type=#{treeType}</if>" + |
| 258 | + "<if test=\"switchPrimarySubStream != null\">, switch_primary_sub_stream=#{switchPrimarySubStream}</if>" + | |
| 256 | 259 | "<if test=\"mediaServerId != null\">, media_server_id=#{mediaServerId}</if>" + |
| 257 | 260 | "WHERE device_id=#{deviceId}"+ |
| 258 | 261 | " </script>"}) |
| ... | ... | @@ -271,7 +274,8 @@ public interface DeviceMapper { |
| 271 | 274 | "geo_coord_sys,"+ |
| 272 | 275 | "tree_type,"+ |
| 273 | 276 | "on_line,"+ |
| 274 | - "media_server_id"+ | |
| 277 | + "media_server_id,"+ | |
| 278 | + "switch_primary_sub_stream"+ | |
| 275 | 279 | ") VALUES (" + |
| 276 | 280 | "#{deviceId}," + |
| 277 | 281 | "#{name}," + |
| ... | ... | @@ -285,7 +289,8 @@ public interface DeviceMapper { |
| 285 | 289 | "#{geoCoordSys}," + |
| 286 | 290 | "#{treeType}," + |
| 287 | 291 | "#{onLine}," + |
| 288 | - "#{mediaServerId}" + | |
| 292 | + "#{mediaServerId}," + | |
| 293 | + "#{switchPrimarySubStream}" + | |
| 289 | 294 | ")") |
| 290 | 295 | void addCustomDevice(Device device); |
| 291 | 296 | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
| ... | ... | @@ -88,16 +88,17 @@ public class PlayController { |
| 88 | 88 | @Operation(summary = "开始点播") |
| 89 | 89 | @Parameter(name = "deviceId", description = "设备国标编号", required = true) |
| 90 | 90 | @Parameter(name = "channelId", description = "通道国标编号", required = true) |
| 91 | + @Parameter(name = "isSubStream", description = "是否子码流(true-子码流,false-主码流),默认为false", required = true) | |
| 91 | 92 | @GetMapping("/start/{deviceId}/{channelId}") |
| 92 | 93 | public DeferredResult<WVPResult<StreamContent>> play(HttpServletRequest request, @PathVariable String deviceId, |
| 93 | - @PathVariable String channelId) { | |
| 94 | + @PathVariable String channelId,boolean isSubStream) { | |
| 94 | 95 | |
| 95 | 96 | // 获取可用的zlm |
| 96 | 97 | Device device = storager.queryVideoDevice(deviceId); |
| 97 | 98 | MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); |
| 98 | 99 | |
| 99 | 100 | RequestMessage requestMessage = new RequestMessage(); |
| 100 | - String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId; | |
| 101 | + String key = DeferredResultHolder.getPlayKey(deviceId,channelId,device.isSwitchPrimarySubStream(),isSubStream); | |
| 101 | 102 | requestMessage.setKey(key); |
| 102 | 103 | String uuid = UUID.randomUUID().toString(); |
| 103 | 104 | requestMessage.setId(uuid); |
| ... | ... | @@ -116,7 +117,7 @@ public class PlayController { |
| 116 | 117 | // 录像查询以channelId作为deviceId查询 |
| 117 | 118 | resultHolder.put(key, uuid, result); |
| 118 | 119 | |
| 119 | - playService.play(newMediaServerItem, deviceId, channelId, (code, msg, data) -> { | |
| 120 | + playService.play(newMediaServerItem, deviceId, channelId,isSubStream, (code, msg, data) -> { | |
| 120 | 121 | WVPResult<StreamContent> wvpResult = new WVPResult<>(); |
| 121 | 122 | if (code == InviteErrorCode.SUCCESS.getCode()) { |
| 122 | 123 | wvpResult.setCode(ErrorCode.SUCCESS.getCode()); |
| ... | ... | @@ -142,8 +143,9 @@ public class PlayController { |
| 142 | 143 | @Operation(summary = "停止点播") |
| 143 | 144 | @Parameter(name = "deviceId", description = "设备国标编号", required = true) |
| 144 | 145 | @Parameter(name = "channelId", description = "通道国标编号", required = true) |
| 146 | + @Parameter(name = "isSubStream", description = "是否子码流(true-子码流,false-主码流),默认为false", required = true) | |
| 145 | 147 | @GetMapping("/stop/{deviceId}/{channelId}") |
| 146 | - public JSONObject playStop(@PathVariable String deviceId, @PathVariable String channelId) { | |
| 148 | + public JSONObject playStop(@PathVariable String deviceId, @PathVariable String channelId,boolean isSubStream) { | |
| 147 | 149 | |
| 148 | 150 | logger.debug(String.format("设备预览/回放停止API调用,streamId:%s_%s", deviceId, channelId )); |
| 149 | 151 | |
| ... | ... | @@ -156,7 +158,12 @@ public class PlayController { |
| 156 | 158 | throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备[" + deviceId + "]不存在"); |
| 157 | 159 | } |
| 158 | 160 | |
| 159 | - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); | |
| 161 | + InviteInfo inviteInfo =null; | |
| 162 | + if(device.isSwitchPrimarySubStream()){ | |
| 163 | + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); | |
| 164 | + }else { | |
| 165 | + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); | |
| 166 | + } | |
| 160 | 167 | if (inviteInfo == null) { |
| 161 | 168 | throw new ControllerException(ErrorCode.ERROR100.getCode(), "点播未找到"); |
| 162 | 169 | } |
| ... | ... | @@ -169,12 +176,17 @@ public class PlayController { |
| 169 | 176 | throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); |
| 170 | 177 | } |
| 171 | 178 | } |
| 172 | - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); | |
| 179 | + if(device.isSwitchPrimarySubStream()){ | |
| 180 | + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); | |
| 181 | + }else { | |
| 182 | + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); | |
| 183 | + storager.stopPlay(deviceId, channelId); | |
| 184 | + } | |
| 173 | 185 | |
| 174 | - storager.stopPlay(deviceId, channelId); | |
| 175 | 186 | JSONObject json = new JSONObject(); |
| 176 | 187 | json.put("deviceId", deviceId); |
| 177 | 188 | json.put("channelId", channelId); |
| 189 | + json.put("isSubStream", isSubStream); | |
| 178 | 190 | return json; |
| 179 | 191 | } |
| 180 | 192 | |
| ... | ... | @@ -341,14 +353,16 @@ public class PlayController { |
| 341 | 353 | @Operation(summary = "获取截图") |
| 342 | 354 | @Parameter(name = "deviceId", description = "设备国标编号", required = true) |
| 343 | 355 | @Parameter(name = "channelId", description = "通道国标编号", required = true) |
| 356 | + @Parameter(name = "isSubStream", description = "是否子码流(true-子码流,false-主码流),默认为false", required = true) | |
| 344 | 357 | @GetMapping("/snap") |
| 345 | - public DeferredResult<String> getSnap(String deviceId, String channelId) { | |
| 358 | + public DeferredResult<String> getSnap(String deviceId, String channelId,boolean isSubStream) { | |
| 346 | 359 | if (logger.isDebugEnabled()) { |
| 347 | 360 | logger.debug("获取截图: {}/{}", deviceId, channelId); |
| 348 | 361 | } |
| 349 | 362 | |
| 363 | + Device device = storager.queryVideoDevice(deviceId); | |
| 350 | 364 | DeferredResult<String> result = new DeferredResult<>(3 * 1000L); |
| 351 | - String key = DeferredResultHolder.CALLBACK_CMD_SNAP + deviceId; | |
| 365 | + String key = DeferredResultHolder.getSnapKey(deviceId,channelId,device.isSwitchPrimarySubStream(),isSubStream); | |
| 352 | 366 | String uuid = UUID.randomUUID().toString(); |
| 353 | 367 | resultHolder.put(key, uuid, result); |
| 354 | 368 | |
| ... | ... | @@ -357,7 +371,7 @@ public class PlayController { |
| 357 | 371 | message.setId(uuid); |
| 358 | 372 | |
| 359 | 373 | String fileName = deviceId + "_" + channelId + "_" + DateUtil.getNowForUrl() + "jpg"; |
| 360 | - playService.getSnap(deviceId, channelId, fileName, (code, msg, data) -> { | |
| 374 | + playService.getSnap(deviceId, channelId, fileName,isSubStream, (code, msg, data) -> { | |
| 361 | 375 | if (code == InviteErrorCode.SUCCESS.getCode()) { |
| 362 | 376 | message.setData(data); |
| 363 | 377 | }else { | ... | ... |
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java
| ... | ... | @@ -122,7 +122,7 @@ public class ApiStreamController { |
| 122 | 122 | MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); |
| 123 | 123 | |
| 124 | 124 | |
| 125 | - playService.play(newMediaServerItem, serial, code, (errorCode, msg, data) -> { | |
| 125 | + playService.play(newMediaServerItem, serial, code,false, (errorCode, msg, data) -> { | |
| 126 | 126 | if (errorCode == InviteErrorCode.SUCCESS.getCode()) { |
| 127 | 127 | InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, serial, code); |
| 128 | 128 | if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { | ... | ... |
src/main/resources/application-dev.yml
| 1 | 1 | spring: |
| 2 | + thymeleaf: | |
| 3 | + cache: false | |
| 2 | 4 | # [可选]上传文件大小限制 |
| 3 | 5 | servlet: |
| 4 | 6 | multipart: |
| ... | ... | @@ -11,18 +13,18 @@ spring: |
| 11 | 13 | # [必须修改] 端口号 |
| 12 | 14 | port: 6379 |
| 13 | 15 | # [可选] 数据库 DB |
| 14 | - database: 6 | |
| 16 | + database: 7 | |
| 15 | 17 | # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 |
| 16 | - password: face2020 | |
| 18 | + password: | |
| 17 | 19 | # [可选] 超时时间 |
| 18 | 20 | timeout: 10000 |
| 19 | 21 | # mysql数据源 |
| 20 | 22 | datasource: |
| 21 | 23 | type: com.zaxxer.hikari.HikariDataSource |
| 22 | 24 | driver-class-name: com.mysql.cj.jdbc.Driver |
| 23 | - url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true | |
| 25 | + url: jdbc:mysql://127.0.0.1:3306/test_gb-89wulian?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true | |
| 24 | 26 | username: root |
| 25 | - password: 123456 | |
| 27 | + password: root | |
| 26 | 28 | hikari: |
| 27 | 29 | connection-timeout: 20000 # 是客户端等待连接池连接的最大毫秒数 |
| 28 | 30 | initialSize: 10 # 连接池初始化连接数 |
| ... | ... | @@ -30,11 +32,19 @@ spring: |
| 30 | 32 | minimum-idle: 5 # 连接池最小空闲连接数 |
| 31 | 33 | idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位) |
| 32 | 34 | max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位) |
| 33 | - | |
| 34 | - | |
| 35 | 35 | #[可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 |
| 36 | 36 | server: |
| 37 | - port: 18080 | |
| 37 | + port: 18978 | |
| 38 | + # [可选] HTTPS配置, 默认不开启 | |
| 39 | + ssl: | |
| 40 | + # [可选] 是否开启HTTPS访问 | |
| 41 | + enabled: false | |
| 42 | + # [可选] 证书文件路径,放置在resource/目录下即可,修改xxx为文件名 | |
| 43 | + key-store: classpath:test.monitor.89iot.cn.jks | |
| 44 | + # [可选] 证书密码 | |
| 45 | + key-store-password: gpf64qmw | |
| 46 | + # [可选] 证书类型, 默认为jks,根据实际修改 | |
| 47 | + key-store-type: JKS | |
| 38 | 48 | |
| 39 | 49 | # 作为28181服务器的配置 |
| 40 | 50 | sip: |
| ... | ... | @@ -42,26 +52,36 @@ sip: |
| 42 | 52 | # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4 |
| 43 | 53 | # 如果不明白,就使用0.0.0.0,大部分情况都是可以的 |
| 44 | 54 | # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。 |
| 45 | - ip: 192.168.41.16 | |
| 55 | + ip: 192.168.1.18 | |
| 46 | 56 | # [可选] 28181服务监听的端口 |
| 47 | - port: 5060 | |
| 57 | + port: 8116 | |
| 48 | 58 | # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) |
| 49 | 59 | # 后两位为行业编码,定义参照附录D.3 |
| 50 | 60 | # 3701020049标识山东济南历下区 信息行业接入 |
| 51 | 61 | # [可选] |
| 52 | - domain: 4401020049 | |
| 62 | + domain: 4101050000 | |
| 53 | 63 | # [可选] |
| 54 | - id: 44010200492000000001 | |
| 64 | + id: 41010500002000000001 | |
| 55 | 65 | # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 |
| 56 | - password: admin123 | |
| 66 | + password: bajiuwulian1006 | |
| 67 | + # 是否存储alarm信息 | |
| 68 | + alarm: true | |
| 57 | 69 | |
| 58 | 70 | #zlm 默认服务器配置 |
| 59 | 71 | media: |
| 60 | - id: FQ3TF8yT83wh5Wvz | |
| 72 | + id: 89wulian-one | |
| 61 | 73 | # [必须修改] zlm服务器的内网IP |
| 62 | - ip: 192.168.41.16 | |
| 74 | + ip: 192.168.1.18 | |
| 63 | 75 | # [必须修改] zlm服务器的http.port |
| 64 | - http-port: 8091 | |
| 76 | + http-port: 80 | |
| 77 | + # [可选] 返回流地址时的ip,置空使用 media.ip | |
| 78 | + stream-ip: 192.168.1.18 | |
| 79 | + # [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip | |
| 80 | + sdp-ip: 192.168.1.18 | |
| 81 | + # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip | |
| 82 | + hook-ip: 192.168.1.18 | |
| 83 | + # [可选] zlm服务器的http.sslport, 置空使用zlm配置文件配置 | |
| 84 | + http-ssl-port: 443 | |
| 65 | 85 | # [可选] zlm服务器的hook.admin_params=secret |
| 66 | 86 | secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc |
| 67 | 87 | # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 |
| ... | ... | @@ -69,11 +89,24 @@ media: |
| 69 | 89 | # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 |
| 70 | 90 | enable: true |
| 71 | 91 | # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 |
| 72 | - port-range: 30000,30500 # 端口范围 | |
| 92 | + port-range: 50000,50300 # 端口范围 | |
| 73 | 93 | # [可选] 国标级联在此范围内选择端口发送媒体流, |
| 74 | - send-port-range: 30000,30500 # 端口范围 | |
| 94 | + send-port-range: 50000,50300 # 端口范围 | |
| 75 | 95 | # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 |
| 76 | 96 | record-assist-port: 18081 |
| 97 | +# [根据业务需求配置] | |
| 98 | +user-settings: | |
| 99 | + # 点播/录像回放 等待超时时间,单位:毫秒 | |
| 100 | + play-timeout: 180000 | |
| 101 | + # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true | |
| 102 | + auto-apply-play: true | |
| 103 | + # 设备/通道状态变化时发送消息 | |
| 104 | + device-status-notify: true | |
| 105 | + # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个 | |
| 106 | + allowed-origins: | |
| 107 | + - http://localhost:8080 | |
| 108 | + - http://127.0.0.1:8080 | |
| 77 | 109 | # [可选] 日志配置, 一般不需要改 |
| 78 | 110 | logging: |
| 79 | 111 | config: classpath:logback-spring-local.xml |
| 112 | + | ... | ... |
src/main/resources/application.yml
web_src/config/index.js
| ... | ... | @@ -12,14 +12,14 @@ module.exports = { |
| 12 | 12 | assetsPublicPath: '/', |
| 13 | 13 | proxyTable: { |
| 14 | 14 | '/debug': { |
| 15 | - target: 'http://localhost:18080', | |
| 15 | + target: 'http://localhost:18978', | |
| 16 | 16 | changeOrigin: true, |
| 17 | 17 | pathRewrite: { |
| 18 | 18 | '^/debug': '/' |
| 19 | 19 | } |
| 20 | 20 | }, |
| 21 | 21 | '/static/snap': { |
| 22 | - target: 'http://localhost:18080', | |
| 22 | + target: 'http://localhost:18978', | |
| 23 | 23 | changeOrigin: true, |
| 24 | 24 | // pathRewrite: { |
| 25 | 25 | // '^/static/snap': '/static/snap' | ... | ... |
web_src/package-lock.json
| ... | ... | @@ -184,15 +184,19 @@ |
| 184 | 184 | } |
| 185 | 185 | }, |
| 186 | 186 | "node_modules/ajv": { |
| 187 | - "version": "5.5.2", | |
| 188 | - "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-5.5.2.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-5.5.2.tgz", | |
| 189 | - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", | |
| 187 | + "version": "6.12.6", | |
| 188 | + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", | |
| 189 | + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", | |
| 190 | 190 | "dev": true, |
| 191 | 191 | "dependencies": { |
| 192 | - "co": "^4.6.0", | |
| 193 | - "fast-deep-equal": "^1.0.0", | |
| 192 | + "fast-deep-equal": "^3.1.1", | |
| 194 | 193 | "fast-json-stable-stringify": "^2.0.0", |
| 195 | - "json-schema-traverse": "^0.3.0" | |
| 194 | + "json-schema-traverse": "^0.4.1", | |
| 195 | + "uri-js": "^4.2.2" | |
| 196 | + }, | |
| 197 | + "funding": { | |
| 198 | + "type": "github", | |
| 199 | + "url": "https://github.com/sponsors/epoberezkin" | |
| 196 | 200 | } |
| 197 | 201 | }, |
| 198 | 202 | "node_modules/ajv-keywords": { |
| ... | ... | @@ -2111,8 +2115,8 @@ |
| 2111 | 2115 | }, |
| 2112 | 2116 | "node_modules/co": { |
| 2113 | 2117 | "version": "4.6.0", |
| 2114 | - "resolved": "https://registry.npm.taobao.org/co/download/co-4.6.0.tgz", | |
| 2115 | - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", | |
| 2118 | + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", | |
| 2119 | + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", | |
| 2116 | 2120 | "dev": true, |
| 2117 | 2121 | "engines": { |
| 2118 | 2122 | "iojs": ">= 1.0.0", |
| ... | ... | @@ -4620,9 +4624,9 @@ |
| 4620 | 4624 | } |
| 4621 | 4625 | }, |
| 4622 | 4626 | "node_modules/fast-deep-equal": { |
| 4623 | - "version": "1.1.0", | |
| 4624 | - "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-1.1.0.tgz", | |
| 4625 | - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", | |
| 4627 | + "version": "3.1.3", | |
| 4628 | + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", | |
| 4629 | + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", | |
| 4626 | 4630 | "dev": true |
| 4627 | 4631 | }, |
| 4628 | 4632 | "node_modules/fast-json-stable-stringify": { |
| ... | ... | @@ -4665,30 +4669,6 @@ |
| 4665 | 4669 | "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" |
| 4666 | 4670 | } |
| 4667 | 4671 | }, |
| 4668 | - "node_modules/file-loader/node_modules/ajv": { | |
| 4669 | - "version": "6.12.5", | |
| 4670 | - "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", | |
| 4671 | - "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", | |
| 4672 | - "dev": true, | |
| 4673 | - "dependencies": { | |
| 4674 | - "fast-deep-equal": "^3.1.1", | |
| 4675 | - "fast-json-stable-stringify": "^2.0.0", | |
| 4676 | - "json-schema-traverse": "^0.4.1", | |
| 4677 | - "uri-js": "^4.2.2" | |
| 4678 | - } | |
| 4679 | - }, | |
| 4680 | - "node_modules/file-loader/node_modules/fast-deep-equal": { | |
| 4681 | - "version": "3.1.3", | |
| 4682 | - "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", | |
| 4683 | - "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", | |
| 4684 | - "dev": true | |
| 4685 | - }, | |
| 4686 | - "node_modules/file-loader/node_modules/json-schema-traverse": { | |
| 4687 | - "version": "0.4.1", | |
| 4688 | - "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", | |
| 4689 | - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", | |
| 4690 | - "dev": true | |
| 4691 | - }, | |
| 4692 | 4672 | "node_modules/file-loader/node_modules/schema-utils": { |
| 4693 | 4673 | "version": "0.4.7", |
| 4694 | 4674 | "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-0.4.7.tgz?cache=0&sync_timestamp=1601922251376&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-0.4.7.tgz", |
| ... | ... | @@ -6126,9 +6106,9 @@ |
| 6126 | 6106 | "dev": true |
| 6127 | 6107 | }, |
| 6128 | 6108 | "node_modules/json-schema-traverse": { |
| 6129 | - "version": "0.3.1", | |
| 6130 | - "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.3.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.3.1.tgz", | |
| 6131 | - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", | |
| 6109 | + "version": "0.4.1", | |
| 6110 | + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", | |
| 6111 | + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", | |
| 6132 | 6112 | "dev": true |
| 6133 | 6113 | }, |
| 6134 | 6114 | "node_modules/json-stringify-pretty-compact": { |
| ... | ... | @@ -8770,30 +8750,6 @@ |
| 8770 | 8750 | "node": ">= 4" |
| 8771 | 8751 | } |
| 8772 | 8752 | }, |
| 8773 | - "node_modules/postcss-loader/node_modules/ajv": { | |
| 8774 | - "version": "6.12.5", | |
| 8775 | - "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", | |
| 8776 | - "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", | |
| 8777 | - "dev": true, | |
| 8778 | - "dependencies": { | |
| 8779 | - "fast-deep-equal": "^3.1.1", | |
| 8780 | - "fast-json-stable-stringify": "^2.0.0", | |
| 8781 | - "json-schema-traverse": "^0.4.1", | |
| 8782 | - "uri-js": "^4.2.2" | |
| 8783 | - } | |
| 8784 | - }, | |
| 8785 | - "node_modules/postcss-loader/node_modules/fast-deep-equal": { | |
| 8786 | - "version": "3.1.3", | |
| 8787 | - "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", | |
| 8788 | - "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", | |
| 8789 | - "dev": true | |
| 8790 | - }, | |
| 8791 | - "node_modules/postcss-loader/node_modules/json-schema-traverse": { | |
| 8792 | - "version": "0.4.1", | |
| 8793 | - "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", | |
| 8794 | - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", | |
| 8795 | - "dev": true | |
| 8796 | - }, | |
| 8797 | 8753 | "node_modules/postcss-loader/node_modules/schema-utils": { |
| 8798 | 8754 | "version": "0.4.7", |
| 8799 | 8755 | "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-0.4.7.tgz?cache=0&sync_timestamp=1601922251376&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-0.4.7.tgz", |
| ... | ... | @@ -11500,6 +11456,30 @@ |
| 11500 | 11456 | "node": ">= 4.3 < 5.0.0 || >= 5.10" |
| 11501 | 11457 | } |
| 11502 | 11458 | }, |
| 11459 | + "node_modules/schema-utils/node_modules/ajv": { | |
| 11460 | + "version": "5.5.2", | |
| 11461 | + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", | |
| 11462 | + "integrity": "sha512-Ajr4IcMXq/2QmMkEmSvxqfLN5zGmJ92gHXAeOXq1OekoH2rfDNsgdDoL2f7QaRCy7G/E6TpxBVdRuNraMztGHw==", | |
| 11463 | + "dev": true, | |
| 11464 | + "dependencies": { | |
| 11465 | + "co": "^4.6.0", | |
| 11466 | + "fast-deep-equal": "^1.0.0", | |
| 11467 | + "fast-json-stable-stringify": "^2.0.0", | |
| 11468 | + "json-schema-traverse": "^0.3.0" | |
| 11469 | + } | |
| 11470 | + }, | |
| 11471 | + "node_modules/schema-utils/node_modules/fast-deep-equal": { | |
| 11472 | + "version": "1.1.0", | |
| 11473 | + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", | |
| 11474 | + "integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==", | |
| 11475 | + "dev": true | |
| 11476 | + }, | |
| 11477 | + "node_modules/schema-utils/node_modules/json-schema-traverse": { | |
| 11478 | + "version": "0.3.1", | |
| 11479 | + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", | |
| 11480 | + "integrity": "sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==", | |
| 11481 | + "dev": true | |
| 11482 | + }, | |
| 11503 | 11483 | "node_modules/select": { |
| 11504 | 11484 | "version": "1.1.2", |
| 11505 | 11485 | "resolved": "https://registry.npm.taobao.org/select/download/select-1.1.2.tgz", |
| ... | ... | @@ -12721,36 +12701,12 @@ |
| 12721 | 12701 | "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" |
| 12722 | 12702 | } |
| 12723 | 12703 | }, |
| 12724 | - "node_modules/uglifyjs-webpack-plugin/node_modules/ajv": { | |
| 12725 | - "version": "6.12.5", | |
| 12726 | - "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", | |
| 12727 | - "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", | |
| 12728 | - "dev": true, | |
| 12729 | - "dependencies": { | |
| 12730 | - "fast-deep-equal": "^3.1.1", | |
| 12731 | - "fast-json-stable-stringify": "^2.0.0", | |
| 12732 | - "json-schema-traverse": "^0.4.1", | |
| 12733 | - "uri-js": "^4.2.2" | |
| 12734 | - } | |
| 12735 | - }, | |
| 12736 | 12704 | "node_modules/uglifyjs-webpack-plugin/node_modules/commander": { |
| 12737 | 12705 | "version": "2.13.0", |
| 12738 | 12706 | "resolved": "https://registry.npm.taobao.org/commander/download/commander-2.13.0.tgz?cache=0&sync_timestamp=1598576136669&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcommander%2Fdownload%2Fcommander-2.13.0.tgz", |
| 12739 | 12707 | "integrity": "sha1-aWS8pnaF33wfFDDFhPB9dZeIW5w=", |
| 12740 | 12708 | "dev": true |
| 12741 | 12709 | }, |
| 12742 | - "node_modules/uglifyjs-webpack-plugin/node_modules/fast-deep-equal": { | |
| 12743 | - "version": "3.1.3", | |
| 12744 | - "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", | |
| 12745 | - "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", | |
| 12746 | - "dev": true | |
| 12747 | - }, | |
| 12748 | - "node_modules/uglifyjs-webpack-plugin/node_modules/json-schema-traverse": { | |
| 12749 | - "version": "0.4.1", | |
| 12750 | - "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", | |
| 12751 | - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", | |
| 12752 | - "dev": true | |
| 12753 | - }, | |
| 12754 | 12710 | "node_modules/uglifyjs-webpack-plugin/node_modules/schema-utils": { |
| 12755 | 12711 | "version": "0.4.7", |
| 12756 | 12712 | "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-0.4.7.tgz?cache=0&sync_timestamp=1601922251376&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-0.4.7.tgz", |
| ... | ... | @@ -14082,24 +14038,6 @@ |
| 14082 | 14038 | "source-map": "~0.6.1" |
| 14083 | 14039 | } |
| 14084 | 14040 | }, |
| 14085 | - "node_modules/webpack/node_modules/ajv": { | |
| 14086 | - "version": "6.12.5", | |
| 14087 | - "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", | |
| 14088 | - "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", | |
| 14089 | - "dev": true, | |
| 14090 | - "dependencies": { | |
| 14091 | - "fast-deep-equal": "^3.1.1", | |
| 14092 | - "fast-json-stable-stringify": "^2.0.0", | |
| 14093 | - "json-schema-traverse": "^0.4.1", | |
| 14094 | - "uri-js": "^4.2.2" | |
| 14095 | - } | |
| 14096 | - }, | |
| 14097 | - "node_modules/webpack/node_modules/fast-deep-equal": { | |
| 14098 | - "version": "3.1.3", | |
| 14099 | - "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", | |
| 14100 | - "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", | |
| 14101 | - "dev": true | |
| 14102 | - }, | |
| 14103 | 14041 | "node_modules/webpack/node_modules/has-flag": { |
| 14104 | 14042 | "version": "2.0.0", |
| 14105 | 14043 | "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-2.0.0.tgz", |
| ... | ... | @@ -14109,12 +14047,6 @@ |
| 14109 | 14047 | "node": ">=0.10.0" |
| 14110 | 14048 | } |
| 14111 | 14049 | }, |
| 14112 | - "node_modules/webpack/node_modules/json-schema-traverse": { | |
| 14113 | - "version": "0.4.1", | |
| 14114 | - "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", | |
| 14115 | - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", | |
| 14116 | - "dev": true | |
| 14117 | - }, | |
| 14118 | 14050 | "node_modules/webpack/node_modules/source-map": { |
| 14119 | 14051 | "version": "0.5.7", |
| 14120 | 14052 | "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", |
| ... | ... | @@ -14551,15 +14483,15 @@ |
| 14551 | 14483 | } |
| 14552 | 14484 | }, |
| 14553 | 14485 | "ajv": { |
| 14554 | - "version": "5.5.2", | |
| 14555 | - "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-5.5.2.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-5.5.2.tgz", | |
| 14556 | - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", | |
| 14486 | + "version": "6.12.6", | |
| 14487 | + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", | |
| 14488 | + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", | |
| 14557 | 14489 | "dev": true, |
| 14558 | 14490 | "requires": { |
| 14559 | - "co": "^4.6.0", | |
| 14560 | - "fast-deep-equal": "^1.0.0", | |
| 14491 | + "fast-deep-equal": "^3.1.1", | |
| 14561 | 14492 | "fast-json-stable-stringify": "^2.0.0", |
| 14562 | - "json-schema-traverse": "^0.3.0" | |
| 14493 | + "json-schema-traverse": "^0.4.1", | |
| 14494 | + "uri-js": "^4.2.2" | |
| 14563 | 14495 | } |
| 14564 | 14496 | }, |
| 14565 | 14497 | "ajv-keywords": { |
| ... | ... | @@ -16303,8 +16235,8 @@ |
| 16303 | 16235 | }, |
| 16304 | 16236 | "co": { |
| 16305 | 16237 | "version": "4.6.0", |
| 16306 | - "resolved": "https://registry.npm.taobao.org/co/download/co-4.6.0.tgz", | |
| 16307 | - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", | |
| 16238 | + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", | |
| 16239 | + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", | |
| 16308 | 16240 | "dev": true |
| 16309 | 16241 | }, |
| 16310 | 16242 | "coa": { |
| ... | ... | @@ -18423,9 +18355,9 @@ |
| 18423 | 18355 | } |
| 18424 | 18356 | }, |
| 18425 | 18357 | "fast-deep-equal": { |
| 18426 | - "version": "1.1.0", | |
| 18427 | - "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-1.1.0.tgz", | |
| 18428 | - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", | |
| 18358 | + "version": "3.1.3", | |
| 18359 | + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", | |
| 18360 | + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", | |
| 18429 | 18361 | "dev": true |
| 18430 | 18362 | }, |
| 18431 | 18363 | "fast-json-stable-stringify": { |
| ... | ... | @@ -18459,30 +18391,6 @@ |
| 18459 | 18391 | "schema-utils": "^0.4.5" |
| 18460 | 18392 | }, |
| 18461 | 18393 | "dependencies": { |
| 18462 | - "ajv": { | |
| 18463 | - "version": "6.12.5", | |
| 18464 | - "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", | |
| 18465 | - "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", | |
| 18466 | - "dev": true, | |
| 18467 | - "requires": { | |
| 18468 | - "fast-deep-equal": "^3.1.1", | |
| 18469 | - "fast-json-stable-stringify": "^2.0.0", | |
| 18470 | - "json-schema-traverse": "^0.4.1", | |
| 18471 | - "uri-js": "^4.2.2" | |
| 18472 | - } | |
| 18473 | - }, | |
| 18474 | - "fast-deep-equal": { | |
| 18475 | - "version": "3.1.3", | |
| 18476 | - "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", | |
| 18477 | - "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", | |
| 18478 | - "dev": true | |
| 18479 | - }, | |
| 18480 | - "json-schema-traverse": { | |
| 18481 | - "version": "0.4.1", | |
| 18482 | - "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", | |
| 18483 | - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", | |
| 18484 | - "dev": true | |
| 18485 | - }, | |
| 18486 | 18394 | "schema-utils": { |
| 18487 | 18395 | "version": "0.4.7", |
| 18488 | 18396 | "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-0.4.7.tgz?cache=0&sync_timestamp=1601922251376&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-0.4.7.tgz", |
| ... | ... | @@ -19648,9 +19556,9 @@ |
| 19648 | 19556 | "dev": true |
| 19649 | 19557 | }, |
| 19650 | 19558 | "json-schema-traverse": { |
| 19651 | - "version": "0.3.1", | |
| 19652 | - "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.3.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.3.1.tgz", | |
| 19653 | - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", | |
| 19559 | + "version": "0.4.1", | |
| 19560 | + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", | |
| 19561 | + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", | |
| 19654 | 19562 | "dev": true |
| 19655 | 19563 | }, |
| 19656 | 19564 | "json-stringify-pretty-compact": { |
| ... | ... | @@ -21822,30 +21730,6 @@ |
| 21822 | 21730 | "schema-utils": "^0.4.0" |
| 21823 | 21731 | }, |
| 21824 | 21732 | "dependencies": { |
| 21825 | - "ajv": { | |
| 21826 | - "version": "6.12.5", | |
| 21827 | - "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", | |
| 21828 | - "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", | |
| 21829 | - "dev": true, | |
| 21830 | - "requires": { | |
| 21831 | - "fast-deep-equal": "^3.1.1", | |
| 21832 | - "fast-json-stable-stringify": "^2.0.0", | |
| 21833 | - "json-schema-traverse": "^0.4.1", | |
| 21834 | - "uri-js": "^4.2.2" | |
| 21835 | - } | |
| 21836 | - }, | |
| 21837 | - "fast-deep-equal": { | |
| 21838 | - "version": "3.1.3", | |
| 21839 | - "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", | |
| 21840 | - "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", | |
| 21841 | - "dev": true | |
| 21842 | - }, | |
| 21843 | - "json-schema-traverse": { | |
| 21844 | - "version": "0.4.1", | |
| 21845 | - "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", | |
| 21846 | - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", | |
| 21847 | - "dev": true | |
| 21848 | - }, | |
| 21849 | 21733 | "schema-utils": { |
| 21850 | 21734 | "version": "0.4.7", |
| 21851 | 21735 | "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-0.4.7.tgz?cache=0&sync_timestamp=1601922251376&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-0.4.7.tgz", |
| ... | ... | @@ -24097,6 +23981,32 @@ |
| 24097 | 23981 | "dev": true, |
| 24098 | 23982 | "requires": { |
| 24099 | 23983 | "ajv": "^5.0.0" |
| 23984 | + }, | |
| 23985 | + "dependencies": { | |
| 23986 | + "ajv": { | |
| 23987 | + "version": "5.5.2", | |
| 23988 | + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", | |
| 23989 | + "integrity": "sha512-Ajr4IcMXq/2QmMkEmSvxqfLN5zGmJ92gHXAeOXq1OekoH2rfDNsgdDoL2f7QaRCy7G/E6TpxBVdRuNraMztGHw==", | |
| 23990 | + "dev": true, | |
| 23991 | + "requires": { | |
| 23992 | + "co": "^4.6.0", | |
| 23993 | + "fast-deep-equal": "^1.0.0", | |
| 23994 | + "fast-json-stable-stringify": "^2.0.0", | |
| 23995 | + "json-schema-traverse": "^0.3.0" | |
| 23996 | + } | |
| 23997 | + }, | |
| 23998 | + "fast-deep-equal": { | |
| 23999 | + "version": "1.1.0", | |
| 24000 | + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", | |
| 24001 | + "integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==", | |
| 24002 | + "dev": true | |
| 24003 | + }, | |
| 24004 | + "json-schema-traverse": { | |
| 24005 | + "version": "0.3.1", | |
| 24006 | + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", | |
| 24007 | + "integrity": "sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==", | |
| 24008 | + "dev": true | |
| 24009 | + } | |
| 24100 | 24010 | } |
| 24101 | 24011 | }, |
| 24102 | 24012 | "select": { |
| ... | ... | @@ -25116,36 +25026,12 @@ |
| 25116 | 25026 | "worker-farm": "^1.5.2" |
| 25117 | 25027 | }, |
| 25118 | 25028 | "dependencies": { |
| 25119 | - "ajv": { | |
| 25120 | - "version": "6.12.5", | |
| 25121 | - "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", | |
| 25122 | - "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", | |
| 25123 | - "dev": true, | |
| 25124 | - "requires": { | |
| 25125 | - "fast-deep-equal": "^3.1.1", | |
| 25126 | - "fast-json-stable-stringify": "^2.0.0", | |
| 25127 | - "json-schema-traverse": "^0.4.1", | |
| 25128 | - "uri-js": "^4.2.2" | |
| 25129 | - } | |
| 25130 | - }, | |
| 25131 | 25029 | "commander": { |
| 25132 | 25030 | "version": "2.13.0", |
| 25133 | 25031 | "resolved": "https://registry.npm.taobao.org/commander/download/commander-2.13.0.tgz?cache=0&sync_timestamp=1598576136669&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcommander%2Fdownload%2Fcommander-2.13.0.tgz", |
| 25134 | 25032 | "integrity": "sha1-aWS8pnaF33wfFDDFhPB9dZeIW5w=", |
| 25135 | 25033 | "dev": true |
| 25136 | 25034 | }, |
| 25137 | - "fast-deep-equal": { | |
| 25138 | - "version": "3.1.3", | |
| 25139 | - "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", | |
| 25140 | - "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", | |
| 25141 | - "dev": true | |
| 25142 | - }, | |
| 25143 | - "json-schema-traverse": { | |
| 25144 | - "version": "0.4.1", | |
| 25145 | - "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", | |
| 25146 | - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", | |
| 25147 | - "dev": true | |
| 25148 | - }, | |
| 25149 | 25035 | "schema-utils": { |
| 25150 | 25036 | "version": "0.4.7", |
| 25151 | 25037 | "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-0.4.7.tgz?cache=0&sync_timestamp=1601922251376&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-0.4.7.tgz", |
| ... | ... | @@ -25846,36 +25732,12 @@ |
| 25846 | 25732 | "yargs": "^8.0.2" |
| 25847 | 25733 | }, |
| 25848 | 25734 | "dependencies": { |
| 25849 | - "ajv": { | |
| 25850 | - "version": "6.12.5", | |
| 25851 | - "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", | |
| 25852 | - "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", | |
| 25853 | - "dev": true, | |
| 25854 | - "requires": { | |
| 25855 | - "fast-deep-equal": "^3.1.1", | |
| 25856 | - "fast-json-stable-stringify": "^2.0.0", | |
| 25857 | - "json-schema-traverse": "^0.4.1", | |
| 25858 | - "uri-js": "^4.2.2" | |
| 25859 | - } | |
| 25860 | - }, | |
| 25861 | - "fast-deep-equal": { | |
| 25862 | - "version": "3.1.3", | |
| 25863 | - "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", | |
| 25864 | - "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", | |
| 25865 | - "dev": true | |
| 25866 | - }, | |
| 25867 | 25735 | "has-flag": { |
| 25868 | 25736 | "version": "2.0.0", |
| 25869 | 25737 | "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-2.0.0.tgz", |
| 25870 | 25738 | "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", |
| 25871 | 25739 | "dev": true |
| 25872 | 25740 | }, |
| 25873 | - "json-schema-traverse": { | |
| 25874 | - "version": "0.4.1", | |
| 25875 | - "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", | |
| 25876 | - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", | |
| 25877 | - "dev": true | |
| 25878 | - }, | |
| 25879 | 25741 | "source-map": { |
| 25880 | 25742 | "version": "0.5.7", |
| 25881 | 25743 | "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", | ... | ... |
web_src/src/components/channelList.vue
| ... | ... | @@ -26,6 +26,12 @@ |
| 26 | 26 | <el-option label="在线" value="true"></el-option> |
| 27 | 27 | <el-option label="离线" value="false"></el-option> |
| 28 | 28 | </el-select> |
| 29 | + 清晰度: | |
| 30 | + <el-select size="mini" style="margin-right: 1rem;" @change="search" v-model="isSubStream" placeholder="请选择" | |
| 31 | + default-first-option> | |
| 32 | + <el-option label="原画" :value="false"></el-option> | |
| 33 | + <el-option label="流畅" :value="true"></el-option> | |
| 34 | + </el-select> | |
| 29 | 35 | </div> |
| 30 | 36 | <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button> |
| 31 | 37 | <el-button v-if="showTree" icon="iconfont icon-list" circle size="mini" @click="switchList()"></el-button> |
| ... | ... | @@ -146,6 +152,7 @@ export default { |
| 146 | 152 | searchSrt: "", |
| 147 | 153 | channelType: "", |
| 148 | 154 | online: "", |
| 155 | + isSubStream: false, | |
| 149 | 156 | winHeight: window.innerHeight - 200, |
| 150 | 157 | currentPage: 1, |
| 151 | 158 | count: 15, |
| ... | ... | @@ -237,7 +244,10 @@ export default { |
| 237 | 244 | let that = this; |
| 238 | 245 | this.$axios({ |
| 239 | 246 | method: 'get', |
| 240 | - url: '/api/play/start/' + deviceId + '/' + channelId | |
| 247 | + url: '/api/play/start/' + deviceId + '/' + channelId, | |
| 248 | + params:{ | |
| 249 | + isSubStream: this.isSubStream | |
| 250 | + } | |
| 241 | 251 | }).then(function (res) { |
| 242 | 252 | console.log(res) |
| 243 | 253 | that.isLoging = false; |
| ... | ... | @@ -277,7 +287,10 @@ export default { |
| 277 | 287 | var that = this; |
| 278 | 288 | this.$axios({ |
| 279 | 289 | method: 'get', |
| 280 | - url: '/api/play/stop/' + this.deviceId + "/" + itemData.channelId | |
| 290 | + url: '/api/play/stop/' + this.deviceId + "/" + itemData.channelId, | |
| 291 | + params:{ | |
| 292 | + isSubStream: this.isSubStream | |
| 293 | + } | |
| 281 | 294 | }).then(function (res) { |
| 282 | 295 | that.initData(); |
| 283 | 296 | }).catch(function (error) { | ... | ... |
web_src/src/components/dialog/deviceEdit.vue
| ... | ... | @@ -64,6 +64,12 @@ |
| 64 | 64 | <el-form-item v-if="form.subscribeCycleForMobilePosition > 0" label="移动位置报送间隔" prop="subscribeCycleForCatalog" > |
| 65 | 65 | <el-input v-model="form.mobilePositionSubmissionInterval" clearable ></el-input> |
| 66 | 66 | </el-form-item> |
| 67 | + <el-form-item label="主子码流开关" prop="switchPrimarySubStream" > | |
| 68 | + <el-select v-model="form.switchPrimarySubStream" style="float: left; width: 100%" > | |
| 69 | + <el-option key="true" label="开启" :value="true"></el-option> | |
| 70 | + <el-option key="false" label="关闭" :value="false"></el-option> | |
| 71 | + </el-select> | |
| 72 | + </el-form-item> | |
| 67 | 73 | <el-form-item label="其他选项"> |
| 68 | 74 | <el-checkbox label="SSRC校验" v-model="form.ssrcCheck" style="float: left"></el-checkbox> |
| 69 | 75 | <el-checkbox label="作为消息通道" v-model="form.asMessageChannel" style="float: left"></el-checkbox> | ... | ... |