From fa62ab9a0143433a5d058ab3229a37e4a9a0f696 Mon Sep 17 00:00:00 2001 From: 648540858 <648540858@qq.com> Date: 星期二, 20 六月 2023 14:16:46 +0800 Subject: [PATCH] Merge pull request #893 from sxh-netizen/wvp-28181-2.0 --- src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java | 18 + src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java | 24 + src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java | 27 + src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java | 2 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java | 2 src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java | 9 web_src/src/components/dialog/deviceEdit.vue | 6 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java | 22 src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java | 7 src/main/resources/application-dev.yml | 67 ++ src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java | 160 ++++++++ src/main/java/com/genersoft/iot/vmp/service/IPlayService.java | 6 src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java | 25 + web_src/config/index.js | 4 src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java | 390 +++++++++++++++------ src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java | 37 ++ src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java | 48 ++ src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java | 26 + src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java | 2 web_src/src/components/channelList.vue | 17 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java | 97 +++- sql/2.6.6-2.6.7更新.sql | 3 src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java | 45 +- src/main/resources/application.yml | 2 src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java | 5 25 files changed, 846 insertions(+), 205 deletions(-) diff --git "a/sql/2.6.6-2.6.7\346\233\264\346\226\260.sql" "b/sql/2.6.6-2.6.7\346\233\264\346\226\260.sql" index 09732b3..285d573 100755 --- "a/sql/2.6.6-2.6.7\346\233\264\346\226\260.sql" +++ "b/sql/2.6.6-2.6.7\346\233\264\346\226\260.sql" @@ -7,6 +7,7 @@ alter table device add mediaServerId varchar(50) default null; - +ALTER TABLE device + ADD COLUMN `switchPrimarySubStream` bit(1) NOT NULL DEFAULT b'0' COMMENT '寮�鍚富瀛愮爜娴佸垏鎹㈢殑寮�鍏筹紙0-涓嶅紑鍚紝1-寮�鍚級鐜板湪宸茬煡鏀寔璁惧涓� 澶у崕銆乀P鈥斺�擫INK鍏ㄧ郴璁惧' AFTER `keepalive_interval_time` diff --git a/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java b/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java index 9fe43f7..029b7ca 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java @@ -1,6 +1,7 @@ package com.genersoft.iot.vmp.common; import com.genersoft.iot.vmp.service.bean.SSRCInfo; +import io.swagger.v3.oas.annotations.media.Schema; /** * 璁板綍姣忔鍙戦�乮nvite娑堟伅鐨勭姸鎬� @@ -123,4 +124,40 @@ public void setStreamMode(String streamMode) { this.streamMode = streamMode; } + + + /*=========================璁惧涓诲瓙鐮佹祦閫昏緫START====================*/ + @Schema(description = "鏄惁涓哄瓙鐮佹祦(true-鏄紝false-涓荤爜娴�)") + private boolean subStream; + + public boolean isSubStream() { + return subStream; + } + + public void setSubStream(boolean subStream) { + this.subStream = subStream; + } + + public static InviteInfo getInviteInfo(String deviceId, String channelId,Boolean isSubStream, String stream, SSRCInfo ssrcInfo, + String receiveIp, Integer receivePort, String streamMode, + InviteSessionType type, InviteSessionStatus status) { + InviteInfo inviteInfo = new InviteInfo(); + inviteInfo.setDeviceId(deviceId); + inviteInfo.setChannelId(channelId); + inviteInfo.setStream(stream); + inviteInfo.setSsrcInfo(ssrcInfo); + inviteInfo.setReceiveIp(receiveIp); + inviteInfo.setReceivePort(receivePort); + inviteInfo.setStreamMode(streamMode); + inviteInfo.setType(type); + inviteInfo.setStatus(status); + if(isSubStream != null){ + inviteInfo.setSubStream(isSubStream); + } + return inviteInfo; + } + /*=========================璁惧涓诲瓙鐮佹祦閫昏緫END====================*/ + + + } diff --git a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java index a501daf..5bda636 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java @@ -528,4 +528,31 @@ } return instance; } + + + /*=========================璁惧涓诲瓙鐮佹祦閫昏緫START====================*/ + @Schema(description = "鏄惁涓哄瓙鐮佹祦(true-鏄紝false-涓荤爜娴�)") + private boolean subStream; + + public boolean isSubStream() { + return subStream; + } + + public void setSubStream(boolean subStream) { + this.subStream = subStream; + } + + public static String getPlayStream(String deviceId,String channelId,boolean isSubStream){ + String streamId; + if(isSubStream){ + streamId = String.format("%s_%s_%s","sub",deviceId, channelId); + }else { + streamId = String.format("%s_%s_%s","main", deviceId, channelId); + } + return streamId; + } + + /*=========================璁惧涓诲瓙鐮佹祦閫昏緫END====================*/ + + } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java index 515f3e7..bd998c7 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java @@ -1,5 +1,6 @@ package com.genersoft.iot.vmp.conf; +import io.swagger.v3.oas.annotations.media.Schema; import org.springframework.core.annotation.Order; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @@ -25,11 +26,11 @@ private int platformPlayTimeout = 60000; - private Boolean interfaceAuthentication = Boolean.TRUE; + private Boolean interfaceAuthentication = Boolean.FALSE; - private Boolean recordPushLive = Boolean.TRUE; + private Boolean recordPushLive = Boolean.FALSE; - private Boolean recordSip = Boolean.TRUE; + private Boolean recordSip = Boolean.FALSE; private Boolean logInDatebase = Boolean.TRUE; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java index 0ef0629..1318c59 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java @@ -189,6 +189,8 @@ private SipTransactionInfo sipTransactionInfo; + + public String getDeviceId() { return deviceId; } @@ -447,4 +449,20 @@ public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { this.sipTransactionInfo = sipTransactionInfo; } + + /*======================璁惧涓诲瓙鐮佹祦閫昏緫START=========================*/ + @Schema(description = "寮�鍚富瀛愮爜娴佸垏鎹㈢殑寮�鍏筹紙false-涓嶅紑鍚紝true-寮�鍚級") + private boolean switchPrimarySubStream; + + public boolean isSwitchPrimarySubStream() { + return switchPrimarySubStream; + } + + public void setSwitchPrimarySubStream(boolean switchPrimarySubStream) { + this.switchPrimarySubStream = switchPrimarySubStream; + } + + /*======================璁惧涓诲瓙鐮佹祦閫昏緫END=========================*/ + + } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java index 2175a45..5677c95 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java @@ -155,4 +155,30 @@ map.remove(msg.getKey()); } } + + /*============================璁惧涓诲瓙鐮佹祦閫昏緫START========================*/ + public static String getPlayKey(String deviceId,String channelId,boolean deviceSwitchSubStream,boolean isSubStream){ + String key = null; + if(deviceSwitchSubStream){ + key = CALLBACK_CMD_PLAY + isSubStream + deviceId + channelId; + }else { + key = CALLBACK_CMD_PLAY +deviceId + channelId; + } + return key; + } + + public static String getSnapKey(String deviceId,String channelId,boolean deviceSwitchSubStream,boolean isSubStream){ + String key = null; + if(deviceSwitchSubStream){ + key = CALLBACK_CMD_SNAP + isSubStream + deviceId + channelId; + }else { + key = CALLBACK_CMD_SNAP +deviceId + channelId; + } + return key; + } + + + /*============================璁惧涓诲瓙鐮佹祦閫昏緫END========================*/ + + } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java index d684056..344e16f 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java @@ -98,7 +98,7 @@ * @param device 瑙嗛璁惧 * @param channelId 棰勮閫氶亾 */ - void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + 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; /** * 璇锋眰鍥炴斁瑙嗛娴� diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index 1ce072f..ccf8151 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java @@ -266,7 +266,7 @@ * @param errorEvent sip閿欒璁㈤槄 */ @Override - public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + public 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 { String stream = ssrcInfo.getStream(); @@ -341,6 +341,22 @@ } } + if( device.isSwitchPrimarySubStream() ){ + if("TP-LINK".equals(device.getManufacturer())){ + if (isSubStream){ + content.append("a=streamMode:sub\r\n"); + }else { + content.append("a=streamMode:main\r\n"); + } + }else { + if (isSubStream){ + content.append("a=streamprofile:1\r\n"); + }else { + content.append("a=streamprofile:0\r\n"); + } + } + } + content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc // f瀛楁:f= v/缂栫爜鏍煎紡/鍒嗚鲸鐜�/甯х巼/鐮佺巼绫诲瀷/鐮佺巼澶у皬a/缂栫爜鏍煎紡/鐮佺巼澶у皬/閲囨牱鐜� // content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 鏈彂鐜版敮鎸佹鐗规�х殑璁惧 @@ -356,7 +372,11 @@ // 杩欓噷涓轰緥閬垮厤涓�涓�氶亾鐨勭偣鎾彧鏈変竴涓猚allID杩欎釜鍙傛暟浣跨敤涓�涓浐瀹氬�� ResponseEvent responseEvent = (ResponseEvent) e.event; SIPResponse response = (SIPResponse) responseEvent.getResponse(); - streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY); + if(device.isSwitchPrimarySubStream()){ + streamSession.put(device.getDeviceId(), channelId, "switch-play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY); + }else { + streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY); + } okEvent.response(e); }); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java index 66a1ce0..43e1ef8 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java @@ -142,8 +142,13 @@ // 鍙兘鏄澶囦富鍔ㄥ仠姝� Device device = storager.queryVideoDeviceByChannelId(platformGbId); if (device != null) { - storager.stopPlay(device.getDeviceId(), channelId); - SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null); + SsrcTransaction ssrcTransactionForPlay = null; + if (device.isSwitchPrimarySubStream() ) { + ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "switch-play", null); + } else { + storager.stopPlay(device.getDeviceId(), channelId); + ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null); + } if (ssrcTransactionForPlay != null){ if (ssrcTransactionForPlay.getCallId().equals(callIdHeader.getCallId())){ // 閲婃斁ssrc @@ -153,10 +158,17 @@ } streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlay.getStream()); } - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); - - if (inviteInfo != null) { + InviteInfo inviteInfo = null; + if (device.isSwitchPrimarySubStream() ) { + String streamType = ssrcTransactionForPlay.getStream().split("_")[0]; + boolean isSubStream = "sub".equals(streamType); + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); + inviteStreamService.removeInviteInfo(inviteInfo.getType(),inviteInfo.getDeviceId(),inviteInfo.getChannelId(),isSubStream,inviteInfo.getStream()); + }else { + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); inviteStreamService.removeInviteInfo(inviteInfo); + } + if (inviteInfo != null) { if (inviteInfo.getStreamInfo() != null) { mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStream()); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java index 635c3ca..2d28a21 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java @@ -489,7 +489,7 @@ } sendRtpItem.setStreamId(streamId); redisCatchStorage.updateSendRTPSever(sendRtpItem); - playService.play(mediaServerItem, device.getDeviceId(), channelId, ((code, msg, data) -> { + playService.play(mediaServerItem, device.getDeviceId(), channelId,false, ((code, msg, data) -> { if (code == InviteErrorCode.SUCCESS.getCode()){ hookEvent.run(code, msg, data); }else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()){ diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java index aa07e09..3810eca 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java @@ -289,6 +289,7 @@ @ResponseBody @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8") public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) { + if (param.isRegist()) { logger.info("[ZLM HOOK] 娴佹敞鍐�, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); } else { @@ -310,11 +311,13 @@ List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks(); // TODO 閲嶆瀯姝ゅ閫昏緫 + boolean isPush = false; if (param.isRegist()) { // 澶勭悊娴佹敞鍐岀殑閴存潈淇℃伅 if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal() || param.getOriginType() == OriginType.RTSP_PUSH.ordinal() || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) { + isPush = true; StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream()); if (streamAuthorityInfo == null) { streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param); @@ -328,7 +331,7 @@ redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream()); } - if ("rtmp".equals(param.getSchema())) { + if ("rtsp".equals(param.getSchema())) { // 鏇存柊娴佸獟浣撹礋杞戒俊鎭� if (param.isRegist()) { mediaServerService.addCount(param.getMediaServerId()); @@ -342,10 +345,19 @@ } if ("rtp".equals(param.getApp()) && !param.isRegist()) { - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream()); - if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) { - inviteStreamService.removeInviteInfo(inviteInfo); - storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId()); + if(param.getStream().split("_").length == 3){ + boolean isSubStream = "sub".equals(param.getStream().split("_")[0]); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream(), isSubStream); + if(inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY )){ + inviteStreamService.removeInviteInfo(inviteInfo.getType(),inviteInfo.getDeviceId(), + inviteInfo.getChannelId(),inviteInfo.isSubStream(),inviteInfo.getStream()); + } + }else { + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream()); + if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) { + inviteStreamService.removeInviteInfo(inviteInfo); + storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId()); + } } } else { if (!"rtp".equals(param.getApp())) { @@ -360,8 +372,6 @@ StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaInfo, param.getApp(), param.getStream(), tracks, callId); param.setStreamInfo(new StreamContent(streamInfoByAppAndStream)); - // 濡傛灉鏄媺娴佷唬鐞嗕骇鐢熺殑锛屼笉闇�瑕佸啓鍏ユ帹娴� - redisCatchStorage.addStream(mediaInfo, type, param.getApp(), param.getStream(), param); if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal() || param.getOriginType() == OriginType.RTMP_PUSH.ordinal() @@ -450,6 +460,11 @@ InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream()); // 鐐规挱 if (inviteInfo != null) { + // 褰曞儚涓嬭浇 + if (inviteInfo.getType() == InviteSessionType.DOWNLOAD) { + ret.put("close", false); + return ret; + } // 鏀跺埌鏃犱汉瑙傜湅璇存槑娴佷篃娌℃湁鍦ㄥ線涓婄骇鎺ㄩ�� if (redisCatchStorage.isChannelSendingRTP(inviteInfo.getChannelId())) { List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByChnnelId( @@ -467,27 +482,33 @@ } } } + Device device = deviceService.getDevice(inviteInfo.getDeviceId()); + if (device != null) { + try { + InviteInfo info = null; + if(device.isSwitchPrimarySubStream()){ + boolean isSubStream = "sub".equals(param.getStream().split("_")[0]); + info = inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(),isSubStream, inviteInfo.getStream()); + }else { + info = inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); - if (userSetting.getStreamOnDemand()) { - // 褰曞儚涓嬭浇 - if (inviteInfo.getType() == InviteSessionType.DOWNLOAD) { - ret.put("close", false); - return ret; - } - - Device device = deviceService.getDevice(inviteInfo.getDeviceId()); - if (device != null) { - try { - if (inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()) != null) { - cmder.streamByeCmd(device, inviteInfo.getChannelId(), - inviteInfo.getStream(), null); - } - } catch (InvalidArgumentException | ParseException | SipException | - SsrcTransactionNotFoundException e) { - logger.error("[鏃犱汉瑙傜湅]鐐规挱锛� 鍙戦�丅YE澶辫触 {}", e.getMessage()); } - } + if (info != null) { + cmder.streamByeCmd(device, inviteInfo.getChannelId(), + inviteInfo.getStream(), null); + } + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + logger.error("[鏃犱汉瑙傜湅]鐐规挱锛� 鍙戦�丅YE澶辫触 {}", e.getMessage()); + } + } + + if(device.isSwitchPrimarySubStream()){ + boolean isSubStream = "sub".equals(param.getStream().split("_")[0]); + inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), + inviteInfo.getChannelId(),isSubStream, inviteInfo.getStream()); + }else { inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId()); @@ -499,7 +520,7 @@ // 鎷夋祦浠g悊 StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream()); if (streamProxyItem != null) { - if (streamProxyItem.isEnableRemoveNoneReader()) { + if (streamProxyItem.isEnableDisableNoneReader()) { // 鏃犱汉瑙傜湅鑷姩绉婚櫎 ret.put("close", true); streamProxyService.del(param.getApp(), param.getStream()); @@ -544,12 +565,26 @@ if ("rtp".equals(param.getApp())) { String[] s = param.getStream().split("_"); - if (!mediaInfo.isRtpEnable() || s.length != 2) { + if (!mediaInfo.isRtpEnable() ) { + defaultResult.setResult(HookResult.SUCCESS()); + return defaultResult; + }else if(s.length != 2 && s.length != 3 ){ defaultResult.setResult(HookResult.SUCCESS()); return defaultResult; } - String deviceId = s[0]; - String channelId = s[1]; + String deviceId = null; + String channelId = null; + boolean isSubStream = false; + if (s[0].length() < 20) { + if ("sub".equals(s[0])) { + isSubStream = true; + } + deviceId = s[1]; + channelId = s[2]; + } else { + deviceId = s[0]; + channelId = s[1]; + } Device device = redisCatchStorage.getDevice(deviceId); if (device == null) { defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg())); @@ -563,7 +598,7 @@ logger.info("[ZLM HOOK] 娴佹湭鎵惧埌, 鍙戣捣鑷姩鐐规挱锛歿}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); RequestMessage msg = new RequestMessage(); - String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId; + String key = DeferredResultHolder.getPlayKey(deviceId, channelId, device.isSwitchPrimarySubStream(), isSubStream); boolean exist = resultHolder.exist(key, null); msg.setKey(key); String uuid = UUID.randomUUID().toString(); @@ -581,7 +616,7 @@ resultHolder.put(key, uuid, result); if (!exist) { - playService.play(mediaInfo, deviceId, channelId, (code, message, data) -> { + playService.play(mediaInfo, deviceId, channelId,isSubStream, (code, message, data) -> { msg.setData(new HookResult(code, message)); resultHolder.invokeResult(msg); }); diff --git a/src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java b/src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java index 852a408..1036efd 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java @@ -4,6 +4,8 @@ import com.genersoft.iot.vmp.common.InviteSessionType; import com.genersoft.iot.vmp.service.bean.ErrorCallback; +import java.util.List; + /** * 璁板綍鍥芥爣鐐规挱鐨勭姸鎬侊紝鍖呮嫭瀹炴椂棰勮锛屼笅杞斤紝褰曞儚鍥炴斁 */ @@ -70,4 +72,50 @@ * 缁熻鍚屼竴涓獄lm涓嬬殑鍥芥爣鏀舵祦涓暟 */ int getStreamInfoCount(String mediaServerId); + + + /*======================璁惧涓诲瓙鐮佹祦閫昏緫START=========================*/ + /** + * 鑾峰彇鐐规挱鐨勭姸鎬佷俊鎭� + */ + InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, + String deviceId, + String channelId,boolean isSubStream); + + void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId,boolean isSubStream); + + InviteInfo getInviteInfo(InviteSessionType type, + String deviceId, + String channelId, + boolean isSubStream, + String stream); + + void removeInviteInfo(InviteSessionType type, + String deviceId, + String channelId, + boolean isSubStream, + String stream); + + void once(InviteSessionType type, String deviceId, String channelId,boolean isSubStream, String stream, ErrorCallback<Object> callback); + + void call(InviteSessionType type, String deviceId, String channelId,boolean isSubStream, String stream, int code, String msg, Object data); + + void updateInviteInfoSub(InviteInfo inviteInfo); + + /** + * 鑾峰彇鐐规挱鐨勭姸鎬佷俊鎭� + */ + InviteInfo getInviteInfoByStream(InviteSessionType type, String stream,boolean isSubStream); + + /** + * 鑾峰彇鐐规挱鐨勭姸鎬佷俊鎭� + */ + List<Object> getInviteInfos(InviteSessionType type, + String deviceId, + String channelId, + String stream); + /*======================璁惧涓诲瓙鐮佹祦閫昏緫END=========================*/ + + + } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java index 5162411..988326e 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java @@ -16,9 +16,9 @@ */ public interface IPlayService { - void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,boolean isSubStream, ErrorCallback<Object> callback); - SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, ErrorCallback<Object> callback); + SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId,boolean isSubStream, ErrorCallback<Object> callback); MediaServerItem getNewMediaServerItem(Device device); @@ -43,5 +43,5 @@ void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; - void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback); + void getSnap(String deviceId, String channelId, String fileName,boolean isSubStream, ErrorCallback errorCallback); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java index 0adbf9d..fe98d06 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java @@ -1,14 +1,17 @@ package com.genersoft.iot.vmp.service.impl; +import com.genersoft.iot.vmp.common.InviteSessionType; import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask; import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler; import com.genersoft.iot.vmp.service.IDeviceChannelService; import com.genersoft.iot.vmp.service.IDeviceService; @@ -47,6 +50,8 @@ private final static Logger logger = LoggerFactory.getLogger(DeviceServiceImpl.class); + @Autowired + private SIPCommander cmder; @Autowired private DynamicTask dynamicTask; @@ -131,6 +136,10 @@ } sync(device); }else { + + if (deviceInDb != null) { + device.setSwitchPrimarySubStream(deviceInDb.isSwitchPrimarySubStream()); + } if(!device.isOnLine()){ device.setOnLine(true); device.setCreateTime(now); @@ -460,6 +469,22 @@ logger.warn("鏇存柊璁惧鏃舵湭鎵惧埌璁惧淇℃伅"); return; } + if(deviceInStore.isSwitchPrimarySubStream() != device.isSwitchPrimarySubStream()){ + //褰撲慨鏀硅澶囩殑涓诲瓙鐮佹祦寮�鍏虫椂锛岄渶瑕佹牎楠屾槸鍚﹀瓨鍦ㄦ祦锛屽鏋滃瓨鍦ㄦ祦鍒欑洿鎺ュ叧闂� + List<SsrcTransaction> ssrcTransactionForAll = streamSession.getSsrcTransactionForAll(device.getDeviceId(), null, null, null); + if(ssrcTransactionForAll != null){ + for (SsrcTransaction ssrcTransaction: ssrcTransactionForAll) { + try { + cmder.streamByeCmd(device, ssrcTransaction.getChannelId(), ssrcTransaction.getStream(), null, null); + } catch (InvalidArgumentException | SsrcTransactionNotFoundException | ParseException | SipException e) { + throw new RuntimeException(e); + } + } + } + deviceChannelMapper.clearPlay(device.getDeviceId()); + inviteStreamService.clearInviteInfo(device.getDeviceId()); + } + if (!ObjectUtils.isEmpty(device.getName())) { deviceInStore.setName(device.getName()); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java index 2fb6816..ed73dd1 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java @@ -198,4 +198,164 @@ } return count; } + + /*======================璁惧涓诲瓙鐮佹祦閫昏緫START=========================*/ + + @Override + public InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, String deviceId, String channelId, boolean isSubStream) { + return getInviteInfo(type, deviceId, channelId,isSubStream, null); + } + + @Override + public void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId, boolean isSubStream) { + removeInviteInfo(inviteSessionType, deviceId, channelId,isSubStream, null); + } + + @Override + public InviteInfo getInviteInfo(InviteSessionType type, String deviceId, String channelId,boolean isSubStream, String stream) { + String key = VideoManagerConstants.INVITE_PREFIX + + "_" + (type != null ? type : "*") + + "_" + (isSubStream ? "sub" : "main") + + "_" + (deviceId != null ? deviceId : "*") + + "_" + (channelId != null ? channelId : "*") + + "_" + (stream != null ? stream : "*"); + List<Object> scanResult = RedisUtil.scan(redisTemplate, key); + if (scanResult.size() != 1) { + return null; + } + return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0)); + } + + @Override + public void removeInviteInfo(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream) { + String scanKey = VideoManagerConstants.INVITE_PREFIX + + "_" + (type != null ? type : "*") + + "_" + (isSubStream ? "sub" : "main") + + "_" + (deviceId != null ? deviceId : "*") + + "_" + (channelId != null ? channelId : "*") + + "_" + (stream != null ? stream : "*"); + List<Object> scanResult = RedisUtil.scan(redisTemplate, scanKey); + if (scanResult.size() > 0) { + for (Object keyObj : scanResult) { + String key = (String) keyObj; + InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(key); + if (inviteInfo == null) { + continue; + } + redisTemplate.delete(key); + inviteErrorCallbackMap.remove(buildKey(type, deviceId, channelId, inviteInfo.getStream())); + } + } + } + + @Override + public void once(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream, ErrorCallback<Object> callback) { + String key = buildSubStreamKey(type, deviceId, channelId,isSubStream, stream); + List<ErrorCallback<Object>> callbacks = inviteErrorCallbackMap.get(key); + if (callbacks == null) { + callbacks = new CopyOnWriteArrayList<>(); + inviteErrorCallbackMap.put(key, callbacks); + } + callbacks.add(callback); + } + + @Override + public void call(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream, int code, String msg, Object data) { + String key = buildSubStreamKey(type, deviceId, channelId,isSubStream, stream); + List<ErrorCallback<Object>> callbacks = inviteErrorCallbackMap.get(key); + if (callbacks == null) { + return; + } + for (ErrorCallback<Object> callback : callbacks) { + callback.run(code, msg, data); + } + inviteErrorCallbackMap.remove(key); + } + + + private String buildSubStreamKey(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream) { + String key = type + "_" + (isSubStream ? "sub":"main") + "_" + deviceId + "_" + channelId; + // 濡傛灉ssrc涓簄ull閭d箞鍙互瀹炵幇涓�涓�氶亾鍙兘涓�娆℃搷浣滐紝ssrc涓嶄负null鍒欏彲浠ユ敮鎸佷竴涓�氶亾澶氭invite + if (stream != null) { + key += ("_" + stream); + } + return key; + } + @Override + public void updateInviteInfoSub(InviteInfo inviteInfo) { + if (inviteInfo == null || (inviteInfo.getDeviceId() == null || inviteInfo.getChannelId() == null)) { + logger.warn("[鏇存柊Invite淇℃伅]锛屽弬鏁颁笉鍏細 {}", JSON.toJSON(inviteInfo)); + return; + } + InviteInfo inviteInfoForUpdate = null; + + if (InviteSessionStatus.ready == inviteInfo.getStatus()) { + if (inviteInfo.getDeviceId() == null + || inviteInfo.getChannelId() == null + || inviteInfo.getType() == null + || inviteInfo.getStream() == null + ) { + return; + } + inviteInfoForUpdate = inviteInfo; + } else { + InviteInfo inviteInfoInRedis = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), + inviteInfo.getChannelId(),inviteInfo.isSubStream(), inviteInfo.getStream()); + if (inviteInfoInRedis == null) { + logger.warn("[鏇存柊Invite淇℃伅]锛屾湭浠庣紦瀛樹腑璇诲彇鍒癐nvite淇℃伅锛� deviceId: {}, channel: {}, stream: {}", + inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); + return; + } + if (inviteInfo.getStreamInfo() != null) { + inviteInfoInRedis.setStreamInfo(inviteInfo.getStreamInfo()); + } + if (inviteInfo.getSsrcInfo() != null) { + inviteInfoInRedis.setSsrcInfo(inviteInfo.getSsrcInfo()); + } + if (inviteInfo.getStreamMode() != null) { + inviteInfoInRedis.setStreamMode(inviteInfo.getStreamMode()); + } + if (inviteInfo.getReceiveIp() != null) { + inviteInfoInRedis.setReceiveIp(inviteInfo.getReceiveIp()); + } + if (inviteInfo.getReceivePort() != null) { + inviteInfoInRedis.setReceivePort(inviteInfo.getReceivePort()); + } + if (inviteInfo.getStatus() != null) { + inviteInfoInRedis.setStatus(inviteInfo.getStatus()); + } + + inviteInfoForUpdate = inviteInfoInRedis; + + } + String key = VideoManagerConstants.INVITE_PREFIX + + "_" + inviteInfoForUpdate.getType() + + "_" + (inviteInfoForUpdate.isSubStream() ? "sub":"main") + + "_" + inviteInfoForUpdate.getDeviceId() + + "_" + inviteInfoForUpdate.getChannelId() + + "_" + inviteInfoForUpdate.getStream(); + redisTemplate.opsForValue().set(key, inviteInfoForUpdate); + } + + @Override + public InviteInfo getInviteInfoByStream(InviteSessionType type, String stream, boolean isSubStream) { + return getInviteInfo(type, null, null,isSubStream, stream); + } + + @Override + public List<Object> getInviteInfos(InviteSessionType type, String deviceId, String channelId, String stream) { + String key = VideoManagerConstants.INVITE_PREFIX + + "_" + (type != null ? type : "*") + + "_" + (deviceId != null ? deviceId : "*") + + "_" + (channelId != null ? channelId : "*") + + "_" + (stream != null ? stream : "*"); + List<Object> scanResult = RedisUtil.scan(redisTemplate, key); + return scanResult; + } + + /*======================璁惧涓诲瓙鐮佹祦閫昏緫END=========================*/ + + + + } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java index 3907895..cebfa1d 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java @@ -18,7 +18,6 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; -import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; @@ -116,28 +115,43 @@ @Override - public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, ErrorCallback<Object> callback) { + public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId,boolean isSubStream, ErrorCallback<Object> callback) { if (mediaServerItem == null) { throw new ControllerException(ErrorCode.ERROR100.getCode(), "鏈壘鍒板彲鐢ㄧ殑zlm"); } Device device = redisCatchStorage.getDevice(deviceId); - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); - + InviteInfo inviteInfo; + if(device.isSwitchPrimarySubStream()){ + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); + }else { + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + } if (inviteInfo != null ) { if (inviteInfo.getStreamInfo() == null) { // 鐐规挱鍙戣捣浜嗕絾鏄皻鏈垚鍔�, 浠呮敞鍐屽洖璋冪瓑寰呯粨鏋滃嵆鍙� - inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); + if(device.isSwitchPrimarySubStream()){ + inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId,isSubStream, null, callback); + }else { + inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); + } return inviteInfo.getSsrcInfo(); }else { StreamInfo streamInfo = inviteInfo.getStreamInfo(); String streamId = streamInfo.getStream(); if (streamId == null) { callback.run(InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), "鐐规挱澶辫触锛� redis缂撳瓨streamId绛変簬null", null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), - "鐐规挱澶辫触锛� redis缂撳瓨streamId绛変簬null", - null); + if(device.isSwitchPrimarySubStream()){ + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, + InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), + "鐐规挱澶辫触锛� redis缂撳瓨streamId绛変簬null", + null); + }else { + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), + "鐐规挱澶辫触锛� redis缂撳瓨streamId绛変簬null", + null); + } return inviteInfo.getSsrcInfo(); } String mediaServerId = streamInfo.getMediaServerId(); @@ -146,41 +160,64 @@ Boolean ready = zlmrtpServerFactory.isStreamReady(mediaInfo, "rtp", streamId); if (ready != null && ready) { callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.SUCCESS.getCode(), - InviteErrorCode.SUCCESS.getMsg(), - streamInfo); + if(device.isSwitchPrimarySubStream()){ + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, + InviteErrorCode.SUCCESS.getCode(), + InviteErrorCode.SUCCESS.getMsg(), + streamInfo); + }else { + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.SUCCESS.getCode(), + InviteErrorCode.SUCCESS.getMsg(), + streamInfo); + } return inviteInfo.getSsrcInfo(); }else { // 鐐规挱鍙戣捣浜嗕絾鏄皻鏈垚鍔�, 浠呮敞鍐屽洖璋冪瓑寰呯粨鏋滃嵆鍙� - inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + if(device.isSwitchPrimarySubStream()) { + inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + }else { + inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId,isSubStream, null, callback); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); + } } } } String streamId = null; if (mediaServerItem.isRtpEnable()) { - streamId = String.format("%s_%s", device.getDeviceId(), channelId); + if(device.isSwitchPrimarySubStream()){ + streamId = StreamInfo.getPlayStream(deviceId, channelId, isSubStream); + }else { + streamId = String.format("%s_%s", device.getDeviceId(), channelId); + } } SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam()); if (ssrcInfo == null) { callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), - InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), - null); + if(device.isSwitchPrimarySubStream()){ + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), + null); + }else { + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), + null); + } return null; } // TODO 璁板綍鐐规挱鐨勭姸鎬� - play(mediaServerItem, ssrcInfo, device, channelId, callback); + play(mediaServerItem, ssrcInfo, device, channelId,isSubStream, callback); return ssrcInfo; } @Override - public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,boolean isSubStream, ErrorCallback<Object> callback) { if (mediaServerItem == null || ssrcInfo == null) { @@ -189,21 +226,11 @@ null); return; } - logger.info("\r\n" + - " [鐐规挱寮�濮媇 \r\n" + - "deviceId : {}, \r\n" + - "channelId : {},\r\n" + - "鏀舵祦绔彛 : {}, \r\n" + - "鏀舵祦妯″紡 : {}, \r\n" + - "SSRC : {}, \r\n" + - "SSRC鏍¢獙 锛歿}", - device.getDeviceId(), - channelId, - ssrcInfo.getPort(), - device.getStreamMode(), - ssrcInfo.getSsrc(), - device.isSsrcCheck()); - + if( device.isSwitchPrimarySubStream() ){ + logger.info("[鐐规挱寮�濮媇 deviceId: {}, channelId: {},鐮佹祦绫诲瀷锛歿},鏀舵祦绔彛锛� {}, 鏀舵祦妯″紡锛歿}, SSRC: {}, SSRC鏍¢獙锛歿}", device.getDeviceId(), channelId,isSubStream ? "杈呯爜娴�" : "涓荤爜娴�", ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); + }else { + logger.info("[鐐规挱寮�濮媇 deviceId: {}, channelId: {},鏀舵祦绔彛锛� {}, 鏀舵祦妯″紡锛歿}, SSRC: {}, SSRC鏍¢獙锛歿}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); + } //绔彛鑾峰彇澶辫触鐨剆srcInfo 娌℃湁蹇呰鍙戦�佺偣鎾寚浠� if (ssrcInfo.getPort() <= 0) { logger.info("[鐐规挱绔彛鍒嗛厤寮傚父]锛宒eviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo); @@ -212,23 +239,50 @@ streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "鐐规挱绔彛鍒嗛厤寮傚父", null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "鐐规挱绔彛鍒嗛厤寮傚父", null); + if(device.isSwitchPrimarySubStream()){ + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "鐐规挱绔彛鍒嗛厤寮傚父", null); + }else { + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "鐐规挱绔彛鍒嗛厤寮傚父", null); + } return; } // 鍒濆鍖杛edis涓殑invite娑堟伅鐘舵�� - InviteInfo inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo, - mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, - InviteSessionStatus.ready); - inviteStreamService.updateInviteInfo(inviteInfo); + InviteInfo inviteInfo; + + if(device.isSwitchPrimarySubStream()){ + // 鍒濆鍖杛edis涓殑invite娑堟伅鐘舵�� + inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channelId,isSubStream, ssrcInfo.getStream(), ssrcInfo, + mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, + InviteSessionStatus.ready); + inviteStreamService.updateInviteInfoSub(inviteInfo); + }else { + // 鍒濆鍖杛edis涓殑invite娑堟伅鐘舵�� + inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo, + mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, + InviteSessionStatus.ready); + inviteStreamService.updateInviteInfo(inviteInfo); + } // 瓒呮椂澶勭悊 String timeOutTaskKey = UUID.randomUUID().toString(); dynamicTask.startDelay(timeOutTaskKey, () -> { // 鎵ц瓒呮椂浠诲姟鏃舵煡璇㈡槸鍚﹀凡缁忔垚鍔燂紝鎴愬姛浜嗗垯涓嶆墽琛岃秴鏃朵换鍔★紝闃叉瓒呮椂浠诲姟鍙栨秷澶辫触鐨勬儏鍐� - InviteInfo inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + InviteInfo inviteInfoForTimeOut; + if(device.isSwitchPrimarySubStream()){ + // 鍒濆鍖杛edis涓殑invite娑堟伅鐘舵�� + inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); + }else { + // 鍒濆鍖杛edis涓殑invite娑堟伅鐘舵�� + inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + } if (inviteInfoForTimeOut == null || inviteInfoForTimeOut.getStreamInfo() == null) { - logger.info("[鐐规挱瓒呮椂] 鏀舵祦瓒呮椂 deviceId: {}, channelId: {}锛岀鍙o細{}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getPort(), ssrcInfo.getSsrc()); + if( device.isSwitchPrimarySubStream()){ + logger.info("[鐐规挱瓒呮椂] 鏀舵祦瓒呮椂 deviceId: {}, channelId: {},鐮佹祦绫诲瀷锛歿}锛岀鍙o細{}, SSRC: {}", device.getDeviceId(), channelId,isSubStream ? "杈呯爜娴�" : "涓荤爜娴�", ssrcInfo.getPort(), ssrcInfo.getSsrc()); + }else { + logger.info("[鐐规挱瓒呮椂] 鏀舵祦瓒呮椂 deviceId: {}, channelId: {}锛岀鍙o細{}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getPort(), ssrcInfo.getSsrc()); + } // 鐐规挱瓒呮椂鍥炲BYE 鍚屾椂閲婃斁ssrc浠ュ強姝ゆ鐐规挱鐨勮祫婧� // InviteInfo inviteInfoForTimeout = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.play, device.getDeviceId(), channelId); // if (inviteInfoForTimeout == null) { @@ -240,10 +294,16 @@ // // TODO 鍙戦�乧ancel // } callback.run(InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); + if( device.isSwitchPrimarySubStream()){ + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, + InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + }else { + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + } try { cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { @@ -261,25 +321,42 @@ }, userSetting.getPlayTimeout()); try { - cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { + cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId,isSubStream, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { logger.info("鏀跺埌璁㈤槄娑堟伅锛� " + response.toJSONString()); dynamicTask.stop(timeOutTaskKey); // hook鍝嶅簲 - StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId); + StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId,isSubStream); if (streamInfo == null){ callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + if( device.isSwitchPrimarySubStream()){ + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + }else { + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + } return; } callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.SUCCESS.getCode(), - InviteErrorCode.SUCCESS.getMsg(), - streamInfo); - logger.info("[鐐规挱鎴愬姛] deviceId: {}, channelId: {}", device.getDeviceId(), channelId); + if( device.isSwitchPrimarySubStream()){ + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, + InviteErrorCode.SUCCESS.getCode(), + InviteErrorCode.SUCCESS.getMsg(), + streamInfo); + }else { + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.SUCCESS.getCode(), + InviteErrorCode.SUCCESS.getMsg(), + streamInfo); + } + if( device.isSwitchPrimarySubStream() ){ + logger.info("[鐐规挱鎴愬姛] deviceId: {}, channelId: {},鐮佹祦绫诲瀷锛歿}", device.getDeviceId(), channelId,isSubStream ? "杈呯爜娴�" : "涓荤爜娴�"); + }else { + logger.info("[鐐规挱鎴愬姛] deviceId: {}, channelId: {}", device.getDeviceId(), channelId); + } String streamUrl; if (mediaServerItemInuse.getRtspPort() != 0) { streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", ssrcInfo.getStream()); @@ -298,16 +375,17 @@ ResponseEvent responseEvent = (ResponseEvent) event.event; String contentString = new String(responseEvent.getResponse().getRawContent()); // 鑾峰彇ssrc - String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); - + int ssrcIndex = contentString.indexOf("y="); // 妫�鏌ユ槸鍚︽湁y瀛楁 - if (ssrcInResponse != null) { + if (ssrcIndex >= 0) { + //ssrc瑙勫畾闀垮害涓�10瀛楄妭锛屼笉鍙栦綑涓嬮暱搴︿互閬垮厤鍚庣画杩樻湁鈥渇=鈥濆瓧娈� TODO 鍚庣画瀵逛笉瑙勮寖鐨勯潪10浣峴src鍏煎 + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12).trim(); // 鏌ヨ鍒皊src涓嶄竴鑷翠笖寮�鍚簡ssrc鏍¢獙鍒欓渶瑕侀拡瀵瑰鐞� if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { + String substring = contentString.substring(0, contentString.indexOf("y=")); try { - Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); - SessionDescription sdp = gb28181Sdp.getBaseSdb(); + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); int port = -1; Vector mediaDescriptions = sdp.getMediaDescriptions(true); for (Object description : mediaDescriptions) { @@ -334,21 +412,24 @@ callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); + if(device.isSwitchPrimarySubStream()){ + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); + }else { + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); + } } } return; } logger.info("[鐐规挱娑堟伅] 鏀跺埌invite 200, 鍙戠幇涓嬬骇鑷畾涔変簡ssrc: {}", ssrcInResponse); - if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { logger.info("[鐐规挱娑堟伅] SSRC淇 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); - // 閲婃斁ssrc mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - // 鍗曠鍙fā寮弒treamId涔熸湁鍙樺寲锛岄噸鏂拌缃洃鍚嵆鍙� if (!mediaServerItem.isRtpEnable()) { // 娣诲姞璁㈤槄 @@ -361,21 +442,34 @@ logger.info("[ZLM HOOK] ssrc淇鍚庢敹鍒拌闃呮秷鎭細 " + response.toJSONString()); dynamicTask.stop(timeOutTaskKey); // hook鍝嶅簲 - StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId); + StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId,isSubStream); if (streamInfo == null){ callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + if( device.isSwitchPrimarySubStream()){ + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + }else { + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + } return; } callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.SUCCESS.getCode(), - InviteErrorCode.SUCCESS.getMsg(), - streamInfo); + if( device.isSwitchPrimarySubStream()){ + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, + InviteErrorCode.SUCCESS.getCode(), + InviteErrorCode.SUCCESS.getMsg(), + streamInfo); + }else { + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.SUCCESS.getCode(), + InviteErrorCode.SUCCESS.getMsg(), + streamInfo); + } }); return; } @@ -391,14 +485,22 @@ } dynamicTask.stop(timeOutTaskKey); + // 閲婃斁ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), "涓嬬骇鑷畾涔変簡ssrc,閲嶆柊璁剧疆鏀舵祦淇℃伅澶辫触", null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), - "涓嬬骇鑷畾涔変簡ssrc,閲嶆柊璁剧疆鏀舵祦淇℃伅澶辫触", null); + if( device.isSwitchPrimarySubStream()){ + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), + "涓嬬骇鑷畾涔変簡ssrc,閲嶆柊璁剧疆鏀舵祦淇℃伅澶辫触", null); + }else { + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), + "涓嬬骇鑷畾涔変簡ssrc,閲嶆柊璁剧疆鏀舵祦淇℃伅澶辫触", null); + } }else { ssrcInfo.setSsrc(ssrcInResponse); @@ -409,7 +511,11 @@ logger.info("[鐐规挱娑堟伅] 鏀跺埌invite 200, 涓嬬骇鑷畾涔変簡ssrc, 浣嗘槸褰撳墠妯″紡鏃犻渶淇"); } } - inviteStreamService.updateInviteInfo(inviteInfo); + if(device.isSwitchPrimarySubStream()){ + inviteStreamService.updateInviteInfoSub(inviteInfo); + }else { + inviteStreamService.updateInviteInfo(inviteInfo); + } }, (event) -> { dynamicTask.stop(timeOutTaskKey); mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); @@ -420,11 +526,19 @@ callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_ERROR.getCode(), String.format("鐐规挱澶辫触锛� 閿欒鐮侊細 %s, %s", event.statusCode, event.msg), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), - String.format("鐐规挱澶辫触锛� 閿欒鐮侊細 %s, %s", event.statusCode, event.msg), null); + if( device.isSwitchPrimarySubStream()){ + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), + String.format("鐐规挱澶辫触锛� 閿欒鐮侊細 %s, %s", event.statusCode, event.msg), null); - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); + }else { + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), + String.format("鐐规挱澶辫触锛� 閿欒鐮侊細 %s, %s", event.statusCode, event.msg), null); + + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + } }); } catch (InvalidArgumentException | SipException | ParseException e) { @@ -438,27 +552,51 @@ callback.run(InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), - InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); + if( device.isSwitchPrimarySubStream()){ + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); + }else { + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); + + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); + } } } - private StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId) { - StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId); + private StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId,boolean isSubStream) { + StreamInfo streamInfo = null; + Device device = redisCatchStorage.getDevice(deviceId); + if( device.isSwitchPrimarySubStream() ){ + streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId,isSubStream); + }else { + streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId); + } if (streamInfo != null) { - DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); - if (deviceChannel != null) { - deviceChannel.setStreamId(streamInfo.getStream()); - storager.startPlay(deviceId, channelId, streamInfo.getStream()); + InviteInfo inviteInfo; + if(device.isSwitchPrimarySubStream()){ + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); + }else { + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); + if (deviceChannel != null) { + deviceChannel.setStreamId(streamInfo.getStream()); + storager.startPlay(deviceId, channelId, streamInfo.getStream()); + } + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); } - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); if (inviteInfo != null) { inviteInfo.setStatus(InviteSessionStatus.ok); inviteInfo.setStreamInfo(streamInfo); - inviteStreamService.updateInviteInfo(inviteInfo); + if(device.isSwitchPrimarySubStream()){ + inviteStreamService.updateInviteInfoSub(inviteInfo); + }else { + inviteStreamService.updateInviteInfo(inviteInfo); + } + } } return streamInfo; @@ -607,16 +745,17 @@ ResponseEvent responseEvent = (ResponseEvent) eventResult.event; String contentString = new String(responseEvent.getResponse().getRawContent()); // 鑾峰彇ssrc - String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); - + int ssrcIndex = contentString.indexOf("y="); // 妫�鏌ユ槸鍚︽湁y瀛楁 - if (ssrcInResponse != null) { + if (ssrcIndex >= 0) { + //ssrc瑙勫畾闀垮害涓�10瀛楄妭锛屼笉鍙栦綑涓嬮暱搴︿互閬垮厤鍚庣画杩樻湁鈥渇=鈥濆瓧娈� TODO 鍚庣画瀵逛笉瑙勮寖鐨勯潪10浣峴src鍏煎 + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); // 鏌ヨ鍒皊src涓嶄竴鑷翠笖寮�鍚簡ssrc鏍¢獙鍒欓渶瑕侀拡瀵瑰鐞� if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { + String substring = contentString.substring(0, contentString.indexOf("y=")); try { - Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); - SessionDescription sdp = gb28181Sdp.getBaseSdb(); + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); int port = -1; Vector mediaDescriptions = sdp.getMediaDescriptions(true); for (Object description : mediaDescriptions) { @@ -684,6 +823,8 @@ } dynamicTask.stop(playBackTimeOutTaskKey); + // 閲婃斁ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); @@ -799,15 +940,17 @@ ResponseEvent responseEvent = (ResponseEvent) eventResult.event; String contentString = new String(responseEvent.getResponse().getRawContent()); // 鑾峰彇ssrc - String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); + int ssrcIndex = contentString.indexOf("y="); // 妫�鏌ユ槸鍚︽湁y瀛楁 - if (ssrcInResponse != null) { + if (ssrcIndex >= 0) { + //ssrc瑙勫畾闀垮害涓�10瀛楄妭锛屼笉鍙栦綑涓嬮暱搴︿互閬垮厤鍚庣画杩樻湁鈥渇=鈥濆瓧娈� TODO 鍚庣画瀵逛笉瑙勮寖鐨勯潪10浣峴src鍏煎 + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); // 鏌ヨ鍒皊src涓嶄竴鑷翠笖寮�鍚簡ssrc鏍¢獙鍒欓渶瑕侀拡瀵瑰鐞� if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { + String substring = contentString.substring(0, contentString.indexOf("y=")); try { - Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); - SessionDescription sdp = gb28181Sdp.getBaseSdb(); + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); int port = -1; Vector mediaDescriptions = sdp.getMediaDescriptions(true); for (Object description : mediaDescriptions) { @@ -872,6 +1015,8 @@ } dynamicTask.stop(downLoadTimeOutTaskKey); + // 閲婃斁ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); @@ -971,6 +1116,7 @@ streamInfo.setChannelId(channelId); return streamInfo; } + @Override public void zlmServerOffline(String mediaServerId) { @@ -1108,14 +1254,18 @@ } @Override - public void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback) { + public void getSnap(String deviceId, String channelId, String fileName,boolean isSubStream, ErrorCallback errorCallback) { Device device = deviceService.getDevice(deviceId); if (device == null) { errorCallback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), null); return; } - - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + InviteInfo inviteInfo; + if(device.isSwitchPrimarySubStream()){ + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); + }else { + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + } if (inviteInfo != null) { if (inviteInfo.getStreamInfo() != null) { // 宸插瓨鍦ㄧ嚎鐩存帴鎴浘 @@ -1130,10 +1280,9 @@ // 璇锋眰鎴浘 logger.info("[璇锋眰鎴浘]: " + fileName); zlmresTfulUtils.getSnap(mediaServerItemInuse, streamUrl, 15, 1, path, fileName); - String filePath = path + File.separator + fileName; File snapFile = new File(path + File.separator + fileName); if (snapFile.exists()) { - errorCallback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), filePath); + errorCallback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), snapFile.getAbsoluteFile()); }else { errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); } @@ -1142,11 +1291,11 @@ } MediaServerItem newMediaServerItem = getNewMediaServerItem(device); - play(newMediaServerItem, deviceId, channelId, (code, msg, data)->{ + play(newMediaServerItem, deviceId, channelId,isSubStream, (code, msg, data)->{ if (code == InviteErrorCode.SUCCESS.getCode()) { InviteInfo inviteInfoForPlay = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); if (inviteInfoForPlay != null && inviteInfoForPlay.getStreamInfo() != null) { - getSnap(deviceId, channelId, fileName, errorCallback); + getSnap(deviceId, channelId, fileName,isSubStream, errorCallback); }else { errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); } @@ -1156,4 +1305,17 @@ }); } + + /*======================璁惧涓诲瓙鐮佹祦閫昏緫START=========================*/ + public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId,boolean isSubStream) { + String streamId = resonse.getString("stream"); + JSONArray tracks = resonse.getJSONArray("tracks"); + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem, "rtp", streamId, tracks, null); + streamInfo.setDeviceID(deviceId); + streamInfo.setChannelId(channelId); + streamInfo.setSubStream(isSubStream); + return streamInfo; + } + /*======================璁惧涓诲瓙鐮佹祦閫昏緫END=========================*/ + } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java index bd0d74f..333a3af 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java @@ -451,6 +451,10 @@ @Select("select count(1) from wvp_device_channel") int getAllChannelCount(); + // 璁惧涓诲瓙鐮佹祦閫昏緫START + @Update(value = {"UPDATE wvp_device_channel SET stream_id=null WHERE device_id=#{deviceId}"}) + void clearPlay(String deviceId); + // 璁惧涓诲瓙鐮佹祦閫昏緫END @Select(value = {" <script>" + "select * " + "from device_channel " + @@ -460,4 +464,5 @@ " <if test='onlyCatalog == true '> and parental = 1 </if>" + " </script>"}) List<DeviceChannel> getSubChannelsByDeviceId(String deviceId, String parentId, boolean onlyCatalog); + } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java index 12e550f..96773fe 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java @@ -42,6 +42,7 @@ "geo_coord_sys," + "on_line," + "media_server_id," + + "switch_primary_sub_stream," + "(SELECT count(0) FROM wvp_device_channel WHERE device_id=wvp_device.device_id) as channel_count "+ " FROM wvp_device WHERE device_id = #{deviceId}") Device getDeviceByDeviceId(String deviceId); @@ -157,6 +158,7 @@ "geo_coord_sys,"+ "on_line,"+ "media_server_id,"+ + "switch_primary_sub_stream switchPrimarySubStream,"+ "(SELECT count(0) FROM wvp_device_channel WHERE device_id=de.device_id) as channel_count " + "FROM wvp_device de" + "<if test=\"onLine != null\"> where on_line=${onLine}</if>"+ @@ -246,6 +248,7 @@ "<if test=\"ssrcCheck != null\">, ssrc_check=#{ssrcCheck}</if>" + "<if test=\"asMessageChannel != null\">, as_message_channel=#{asMessageChannel}</if>" + "<if test=\"geoCoordSys != null\">, geo_coord_sys=#{geoCoordSys}</if>" + + "<if test=\"switchPrimarySubStream != null\">, switch_primary_sub_stream=#{switchPrimarySubStream}</if>" + "<if test=\"mediaServerId != null\">, media_server_id=#{mediaServerId}</if>" + "WHERE device_id=#{deviceId}"+ " </script>"}) @@ -263,7 +266,8 @@ "as_message_channel,"+ "geo_coord_sys,"+ "on_line,"+ - "media_server_id"+ + "media_server_id,"+ + "switch_primary_sub_stream"+ ") VALUES (" + "#{deviceId}," + "#{name}," + @@ -276,7 +280,8 @@ "#{asMessageChannel}," + "#{geoCoordSys}," + "#{onLine}," + - "#{mediaServerId}" + + "#{mediaServerId}," + + "#{switchPrimarySubStream}" + ")") void addCustomDevice(Device device); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java index 200a7d9..0cf4874 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java @@ -26,7 +26,6 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; -import com.genersoft.iot.vmp.vmanager.bean.SnapPath; import com.genersoft.iot.vmp.vmanager.bean.StreamContent; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import io.swagger.v3.oas.annotations.Operation; @@ -41,7 +40,6 @@ import javax.servlet.http.HttpServletRequest; import javax.sip.InvalidArgumentException; import javax.sip.SipException; -import java.io.File; import java.text.ParseException; import java.util.List; import java.util.UUID; @@ -90,16 +88,17 @@ @Operation(summary = "寮�濮嬬偣鎾�") @Parameter(name = "deviceId", description = "璁惧鍥芥爣缂栧彿", required = true) @Parameter(name = "channelId", description = "閫氶亾鍥芥爣缂栧彿", required = true) + @Parameter(name = "isSubStream", description = "鏄惁瀛愮爜娴侊紙true-瀛愮爜娴侊紝false-涓荤爜娴侊級锛岄粯璁や负false", required = true) @GetMapping("/start/{deviceId}/{channelId}") public DeferredResult<WVPResult<StreamContent>> play(HttpServletRequest request, @PathVariable String deviceId, - @PathVariable String channelId) { + @PathVariable String channelId,boolean isSubStream) { // 鑾峰彇鍙敤鐨剒lm Device device = storager.queryVideoDevice(deviceId); MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); RequestMessage requestMessage = new RequestMessage(); - String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId; + String key = DeferredResultHolder.getPlayKey(deviceId,channelId,device.isSwitchPrimarySubStream(),isSubStream); requestMessage.setKey(key); String uuid = UUID.randomUUID().toString(); requestMessage.setId(uuid); @@ -118,7 +117,7 @@ // 褰曞儚鏌ヨ浠hannelId浣滀负deviceId鏌ヨ resultHolder.put(key, uuid, result); - playService.play(newMediaServerItem, deviceId, channelId, (code, msg, data) -> { + playService.play(newMediaServerItem, deviceId, channelId,isSubStream, (code, msg, data) -> { WVPResult<StreamContent> wvpResult = new WVPResult<>(); if (code == InviteErrorCode.SUCCESS.getCode()) { wvpResult.setCode(ErrorCode.SUCCESS.getCode()); @@ -144,8 +143,9 @@ @Operation(summary = "鍋滄鐐规挱") @Parameter(name = "deviceId", description = "璁惧鍥芥爣缂栧彿", required = true) @Parameter(name = "channelId", description = "閫氶亾鍥芥爣缂栧彿", required = true) + @Parameter(name = "isSubStream", description = "鏄惁瀛愮爜娴侊紙true-瀛愮爜娴侊紝false-涓荤爜娴侊級锛岄粯璁や负false", required = true) @GetMapping("/stop/{deviceId}/{channelId}") - public JSONObject playStop(@PathVariable String deviceId, @PathVariable String channelId) { + public JSONObject playStop(@PathVariable String deviceId, @PathVariable String channelId,boolean isSubStream) { logger.debug(String.format("璁惧棰勮/鍥炴斁鍋滄API璋冪敤锛宻treamId锛�%s_%s", deviceId, channelId )); @@ -158,7 +158,12 @@ throw new ControllerException(ErrorCode.ERROR100.getCode(), "璁惧[" + deviceId + "]涓嶅瓨鍦�"); } - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + InviteInfo inviteInfo =null; + if(device.isSwitchPrimarySubStream()){ + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); + }else { + inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + } if (inviteInfo == null) { throw new ControllerException(ErrorCode.ERROR100.getCode(), "鐐规挱鏈壘鍒�"); } @@ -171,12 +176,17 @@ throw new ControllerException(ErrorCode.ERROR100.getCode(), "鍛戒护鍙戦�佸け璐�: " + e.getMessage()); } } - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + if(device.isSwitchPrimarySubStream()){ + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); + }else { + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + storager.stopPlay(deviceId, channelId); + } - storager.stopPlay(deviceId, channelId); JSONObject json = new JSONObject(); json.put("deviceId", deviceId); json.put("channelId", channelId); + json.put("isSubStream", isSubStream); return json; } @@ -343,30 +353,27 @@ @Operation(summary = "鑾峰彇鎴浘") @Parameter(name = "deviceId", description = "璁惧鍥芥爣缂栧彿", required = true) @Parameter(name = "channelId", description = "閫氶亾鍥芥爣缂栧彿", required = true) + @Parameter(name = "isSubStream", description = "鏄惁瀛愮爜娴侊紙true-瀛愮爜娴侊紝false-涓荤爜娴侊級锛岄粯璁や负false", required = true) @GetMapping("/snap") - public DeferredResult<String> getSnap(HttpServletRequest request, String deviceId, String channelId) { + public DeferredResult<String> getSnap(String deviceId, String channelId,boolean isSubStream) { if (logger.isDebugEnabled()) { logger.debug("鑾峰彇鎴浘: {}/{}", deviceId, channelId); } + Device device = storager.queryVideoDevice(deviceId); DeferredResult<String> result = new DeferredResult<>(3 * 1000L); - String key = DeferredResultHolder.CALLBACK_CMD_SNAP + deviceId; + String key = DeferredResultHolder.getSnapKey(deviceId,channelId,device.isSwitchPrimarySubStream(),isSubStream); String uuid = UUID.randomUUID().toString(); resultHolder.put(key, uuid, result); RequestMessage message = new RequestMessage(); message.setKey(key); message.setId(uuid); - String nowForUrl = DateUtil.getNowForUrl(); - String fileName = deviceId + "_" + channelId + "_" + nowForUrl + ".jpg"; - playService.getSnap(deviceId, channelId, fileName, (code, msg, data) -> { + String fileName = deviceId + "_" + channelId + "_" + DateUtil.getNowForUrl() + "jpg"; + playService.getSnap(deviceId, channelId, fileName,isSubStream, (code, msg, data) -> { if (code == InviteErrorCode.SUCCESS.getCode()) { - File snapFile = new File((String)data); - String fileNameForUrl = deviceId + "/" + channelId + "?mark=" + nowForUrl; - String uri = request.getRequestURL().toString().replace(request.getRequestURI(), "/api/device/query/snap/" + fileNameForUrl); - SnapPath snapPath = SnapPath.getInstance((String) data, snapFile.getAbsolutePath(), uri); - message.setData(snapPath); + message.setData(data); }else { message.setData(WVPResult.fail(code, msg)); } diff --git a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java index 556adf6..29c776a 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java +++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java @@ -122,7 +122,7 @@ MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); - playService.play(newMediaServerItem, serial, code, (errorCode, msg, data) -> { + playService.play(newMediaServerItem, serial, code,false, (errorCode, msg, data) -> { if (errorCode == InviteErrorCode.SUCCESS.getCode()) { InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, serial, code); if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 04f7742..f5404e2 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,4 +1,6 @@ spring: + thymeleaf: + cache: false # [鍙�塢涓婁紶鏂囦欢澶у皬闄愬埗 servlet: multipart: @@ -11,18 +13,18 @@ # [蹇呴』淇敼] 绔彛鍙� port: 6379 # [鍙�塢 鏁版嵁搴� DB - database: 6 + database: 7 # [鍙�塢 璁块棶瀵嗙爜,鑻ヤ綘鐨剅edis鏈嶅姟鍣ㄦ病鏈夎缃瘑鐮侊紝灏变笉闇�瑕佺敤瀵嗙爜鍘昏繛鎺� - password: face2020 + password: # [鍙�塢 瓒呮椂鏃堕棿 timeout: 10000 # mysql鏁版嵁婧� datasource: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true + url: jdbc:mysql://127.0.0.1:3306/test_gb-89wulian?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true username: root - password: 123456 + password: root hikari: connection-timeout: 20000 # 鏄鎴风绛夊緟杩炴帴姹犺繛鎺ョ殑鏈�澶ф绉掓暟 initialSize: 10 # 杩炴帴姹犲垵濮嬪寲杩炴帴鏁� @@ -30,11 +32,19 @@ minimum-idle: 5 # 杩炴帴姹犳渶灏忕┖闂茶繛鎺ユ暟 idle-timeout: 300000 # 鍏佽杩炴帴鍦ㄨ繛鎺ユ睜涓┖闂茬殑鏈�闀挎椂闂达紙浠ユ绉掍负鍗曚綅锛� max-lifetime: 1200000 # 鏄睜涓繛鎺ュ叧闂悗鐨勬渶闀跨敓鍛藉懆鏈燂紙浠ユ绉掍负鍗曚綅) - - #[鍙�塢 WVP鐩戝惉鐨凥TTP绔彛, 缃戦〉鍜屾帴鍙h皟鐢ㄩ兘鏄繖涓鍙� server: - port: 18080 + port: 18978 + # [鍙�塢 HTTPS閰嶇疆锛� 榛樿涓嶅紑鍚� + ssl: + # [鍙�塢 鏄惁寮�鍚疕TTPS璁块棶 + enabled: false + # [鍙�塢 璇佷功鏂囦欢璺緞锛屾斁缃湪resource/鐩綍涓嬪嵆鍙紝淇敼xxx涓烘枃浠跺悕 + key-store: classpath:test.monitor.89iot.cn.jks + # [鍙�塢 璇佷功瀵嗙爜 + key-store-password: gpf64qmw + # [鍙�塢 璇佷功绫诲瀷锛� 榛樿涓簀ks锛屾牴鎹疄闄呬慨鏀� + key-store-type: JKS # 浣滀负28181鏈嶅姟鍣ㄧ殑閰嶇疆 sip: @@ -42,26 +52,36 @@ # 濡傛灉瑕佺洃鍚寮犵綉鍗★紝鍙互浣跨敤閫楀彿鍒嗛殧澶氫釜IP锛� 渚嬪锛� 192.168.1.4,10.0.0.4 # 濡傛灉涓嶆槑鐧斤紝灏变娇鐢�0.0.0.0锛屽ぇ閮ㄥ垎鎯呭喌閮芥槸鍙互鐨� # 璇蜂笉瑕佷娇鐢�127.0.0.1锛屼换浣曞寘鎷琹ocalhost鍦ㄥ唴鐨勫煙鍚嶉兘鏄笉鍙互鐨勩�� - ip: 192.168.41.16 + ip: 192.168.1.18 # [鍙�塢 28181鏈嶅姟鐩戝惉鐨勭鍙� - port: 5060 + port: 8116 # 鏍规嵁鍥芥爣6.1.2涓瀹氾紝domain瀹滈噰鐢↖D缁熶竴缂栫爜鐨勫墠鍗佷綅缂栫爜銆傚浗鏍囬檮褰旸涓畾涔夊墠8浣嶄负涓績缂栫爜锛堢敱鐪佺骇銆佸競绾с�佸尯绾с�佸熀灞傜紪鍙风粍鎴愶紝鍙傜収GB/T 2260-2007锛� # 鍚庝袱浣嶄负琛屼笟缂栫爜锛屽畾涔夊弬鐓ч檮褰旸.3 # 3701020049鏍囪瘑灞变笢娴庡崡鍘嗕笅鍖� 淇℃伅琛屼笟鎺ュ叆 # [鍙�塢 - domain: 4401020049 + domain: 4101050000 # [鍙�塢 - id: 44010200492000000001 + id: 41010500002000000001 # [鍙�塢 榛樿璁惧璁よ瘉瀵嗙爜锛屽悗缁墿灞曚娇鐢ㄨ澶囧崟鐙瘑鐮�, 绉婚櫎瀵嗙爜灏嗕笉杩涜鏍¢獙 - password: admin123 + password: bajiuwulian1006 + # 鏄惁瀛樺偍alarm淇℃伅 + alarm: true #zlm 榛樿鏈嶅姟鍣ㄩ厤缃� media: - id: FQ3TF8yT83wh5Wvz + id: 89wulian-one # [蹇呴』淇敼] zlm鏈嶅姟鍣ㄧ殑鍐呯綉IP - ip: 192.168.41.16 + ip: 192.168.1.18 # [蹇呴』淇敼] zlm鏈嶅姟鍣ㄧ殑http.port - http-port: 8091 + http-port: 80 + # [鍙�塢 杩斿洖娴佸湴鍧�鏃剁殑ip锛岀疆绌轰娇鐢� media.ip + stream-ip: 192.168.1.18 + # [鍙�塢 wvp鍦ㄥ浗鏍囦俊浠や腑浣跨敤鐨刬p锛屾ip涓烘憚鍍忔満鍙互璁块棶鍒扮殑ip锛� 缃┖浣跨敤 media.ip + sdp-ip: 192.168.1.18 + # [鍙�塢 zlm鏈嶅姟鍣ㄧ殑hook鎵�浣跨敤鐨処P, 榛樿浣跨敤sip.ip + hook-ip: 192.168.1.18 + # [鍙�塢 zlm鏈嶅姟鍣ㄧ殑http.sslport, 缃┖浣跨敤zlm閰嶇疆鏂囦欢閰嶇疆 + http-ssl-port: 443 # [鍙�塢 zlm鏈嶅姟鍣ㄧ殑hook.admin_params=secret secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc # 鍚敤澶氱鍙fā寮�, 澶氱鍙fā寮忎娇鐢ㄧ鍙e尯鍒嗘瘡璺祦锛屽吋瀹规�ф洿濂姐�� 鍗曠鍙d娇鐢ㄦ祦鐨剆src鍖哄垎锛� 鐐规挱瓒呮椂寤鸿浣跨敤澶氱鍙f祴璇� @@ -69,11 +89,24 @@ # [鍙�塢 鏄惁鍚敤澶氱鍙fā寮�, 寮�鍚悗浼氬湪portRange鑼冨洿鍐呴�夋嫨绔彛鐢ㄤ簬濯掍綋娴佷紶杈� enable: true # [鍙�塢 鍦ㄦ鑼冨洿鍐呴�夋嫨绔彛鐢ㄤ簬濯掍綋娴佷紶杈�, 蹇呴』鎻愬墠鍦▃lm涓婇厤缃灞炴�э紝涓嶇劧鑷姩閰嶇疆姝ゅ睘鎬у彲鑳戒笉鎴愬姛 - port-range: 30000,30500 # 绔彛鑼冨洿 + port-range: 50000,50300 # 绔彛鑼冨洿 # [鍙�塢 鍥芥爣绾ц仈鍦ㄦ鑼冨洿鍐呴�夋嫨绔彛鍙戦�佸獟浣撴祦, - send-port-range: 30000,30500 # 绔彛鑼冨洿 + send-port-range: 50000,50300 # 绔彛鑼冨洿 # 褰曞儚杈呭姪鏈嶅姟锛� 閮ㄧ讲姝ゆ湇鍔″彲浠ュ疄鐜皕lm褰曞儚鐨勭鐞嗕笌涓嬭浇锛� 0 琛ㄧず涓嶄娇鐢� record-assist-port: 18081 +# [鏍规嵁涓氬姟闇�姹傞厤缃甝 +user-settings: + # 鐐规挱/褰曞儚鍥炴斁 绛夊緟瓒呮椂鏃堕棿,鍗曚綅锛氭绉� + play-timeout: 180000 + # [鍙�塢 鑷姩鐐规挱锛� 浣跨敤鍥哄畾娴佸湴鍧�杩涜鎾斁鏃讹紝濡傛灉鏈偣鎾垯鑷姩杩涜鐐规挱, 闇�瑕乺tp.enable=true + auto-apply-play: true + # 璁惧/閫氶亾鐘舵�佸彉鍖栨椂鍙戦�佹秷鎭� + device-status-notify: true + # 璺ㄥ煙閰嶇疆锛岄厤缃綘璁块棶鍓嶇椤甸潰鐨勫湴鍧�鍗冲彲锛� 鍙互閰嶇疆澶氫釜 + allowed-origins: + - http://localhost:8080 + - http://127.0.0.1:8080 # [鍙�塢 鏃ュ織閰嶇疆, 涓�鑸笉闇�瑕佹敼 logging: config: classpath:logback-spring-local.xml + diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3f47844..80de5ef 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -2,4 +2,4 @@ application: name: wvp profiles: - active: local \ No newline at end of file + active: dev \ No newline at end of file diff --git a/web_src/config/index.js b/web_src/config/index.js index b1e1cbe..a7b6846 100644 --- a/web_src/config/index.js +++ b/web_src/config/index.js @@ -12,14 +12,14 @@ assetsPublicPath: '/', proxyTable: { '/debug': { - target: 'http://localhost:18080', + target: 'http://localhost:18978', changeOrigin: true, pathRewrite: { '^/debug': '/' } }, '/static/snap': { - target: 'http://localhost:18080', + target: 'http://localhost:18978', changeOrigin: true, // pathRewrite: { // '^/static/snap': '/static/snap' diff --git a/web_src/src/components/channelList.vue b/web_src/src/components/channelList.vue index 55cbc98..a8e8433 100644 --- a/web_src/src/components/channelList.vue +++ b/web_src/src/components/channelList.vue @@ -26,6 +26,12 @@ <el-option label="鍦ㄧ嚎" value="true"></el-option> <el-option label="绂荤嚎" value="false"></el-option> </el-select> + 娓呮櫚搴�: + <el-select size="mini" style="margin-right: 1rem;" @change="search" v-model="isSubStream" placeholder="璇烽�夋嫨" + default-first-option> + <el-option label="鍘熺敾" :value="false"></el-option> + <el-option label="娴佺晠" :value="true"></el-option> + </el-select> </div> <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button> <el-button v-if="showTree" icon="iconfont icon-list" circle size="mini" @click="switchList()"></el-button> @@ -146,6 +152,7 @@ searchSrt: "", channelType: "", online: "", + isSubStream: false, winHeight: window.innerHeight - 200, currentPage: 1, count: 15, @@ -237,7 +244,10 @@ let that = this; this.$axios({ method: 'get', - url: '/api/play/start/' + deviceId + '/' + channelId + url: '/api/play/start/' + deviceId + '/' + channelId, + params:{ + isSubStream: this.isSubStream + } }).then(function (res) { console.log(res) that.isLoging = false; @@ -277,7 +287,10 @@ var that = this; this.$axios({ method: 'get', - url: '/api/play/stop/' + this.deviceId + "/" + itemData.channelId + url: '/api/play/stop/' + this.deviceId + "/" + itemData.channelId, + params:{ + isSubStream: this.isSubStream + } }).then(function (res) { that.initData(); }).catch(function (error) { diff --git a/web_src/src/components/dialog/deviceEdit.vue b/web_src/src/components/dialog/deviceEdit.vue index 5591183..d833b13 100644 --- a/web_src/src/components/dialog/deviceEdit.vue +++ b/web_src/src/components/dialog/deviceEdit.vue @@ -58,6 +58,12 @@ <el-form-item v-if="form.subscribeCycleForMobilePosition > 0" label="绉诲姩浣嶇疆鎶ラ�侀棿闅�" prop="subscribeCycleForCatalog" > <el-input v-model="form.mobilePositionSubmissionInterval" clearable ></el-input> </el-form-item> + <el-form-item label="涓诲瓙鐮佹祦寮�鍏�" prop="switchPrimarySubStream" > + <el-select v-model="form.switchPrimarySubStream" style="float: left; width: 100%" > + <el-option key="true" label="寮�鍚�" :value="true"></el-option> + <el-option key="false" label="鍏抽棴" :value="false"></el-option> + </el-select> + </el-form-item> <el-form-item label="鍏朵粬閫夐」"> <el-checkbox label="SSRC鏍¢獙" v-model="form.ssrcCheck" style="float: left"></el-checkbox> <el-checkbox label="浣滀负娑堟伅閫氶亾" v-model="form.asMessageChannel" style="float: left"></el-checkbox> -- Gitblit v1.8.0