From 44d216100b45c3337c593ee82ee68e7e0f35d24b Mon Sep 17 00:00:00 2001 From: Lawrence <1934378145@qq.com> Date: 星期三, 16 十二月 2020 20:29:19 +0800 Subject: [PATCH] 与master分支同步 --- src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java | 2 src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java | 137 ++--- src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java | 119 +++-- src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java | 56 ++ src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java | 6 src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java | 4 web_src/package-lock.json | 8 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java | 51 +- web_src/index.html | 2 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java | 90 ++++ src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java | 90 ++++ src/main/resources/application-dev.yml | 94 ++++ web_src/build/webpack.dev.conf.js | 5 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java | 17 web_src/src/components/gb28181/player.vue | 57 ++ src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java | 34 + web_src/src/components/videoList.vue | 2 src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java | 13 web_src/src/components/gb28181/devicePlayer.vue | 222 ++++++--- web_src/package.json | 2 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java | 2 src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java | 117 +--- src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java | 7 web_src/build/webpack.prod.conf.js | 5 src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java | 2 src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java | 6 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java | 57 +- web_src/src/router/index.js | 2 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java | 5 src/main/resources/application.yml | 65 -- 30 files changed, 829 insertions(+), 450 deletions(-) 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 1dd3a85..53bda91 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java @@ -5,12 +5,18 @@ public class StreamInfo { private String ssrc; + private String streamId; private String deviceID; private String cahnnelId; private String flv; private String ws_flv; - private String rtmp; + private String fmp4; + private String ws_fmp4; private String hls; + private String ws_hls; + private String ts; + private String ws_ts; + private String rtmp; private String rtsp; private JSONArray tracks; @@ -85,4 +91,52 @@ public void setTracks(JSONArray tracks) { this.tracks = tracks; } + + public String getFmp4() { + return fmp4; + } + + public void setFmp4(String fmp4) { + this.fmp4 = fmp4; + } + + public String getWs_fmp4() { + return ws_fmp4; + } + + public void setWs_fmp4(String ws_fmp4) { + this.ws_fmp4 = ws_fmp4; + } + + public String getWs_hls() { + return ws_hls; + } + + public void setWs_hls(String ws_hls) { + this.ws_hls = ws_hls; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getWs_ts() { + return ws_ts; + } + + public void setWs_ts(String ws_ts) { + this.ws_ts = ws_ts; + } + + public String getStreamId() { + return streamId; + } + + public void setStreamId(String streamId) { + this.streamId = streamId; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java index 9b091e6..54c0711 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java @@ -113,6 +113,7 @@ */ @Override public void processRequest(RequestEvent evt) { + logger.debug(evt.getRequest().toString()); // 鐢变簬jainsip鏄崟绾跨▼绋嬪簭锛屼负鎻愰珮鎬ц兘骞跺彂澶勭悊 processThreadPool.execute(() -> { processorFactory.createRequestProcessor(evt).process(); @@ -122,6 +123,7 @@ @Override public void processResponse(ResponseEvent evt) { Response response = evt.getResponse(); + logger.debug(evt.getResponse().toString()); int status = response.getStatusCode(); if (((status >= 200) && (status < 300)) || status == 401) { // Success! ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java index ec7ff88..b9bb5b1 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java @@ -32,7 +32,7 @@ public void onApplicationEvent(OnlineEvent event) { if (logger.isDebugEnabled()) { - logger.debug("璁惧绂荤嚎浜嬩欢瑙﹀彂锛宒eviceId锛�" + event.getDeviceId() + ",from:" + event.getFrom()); + logger.debug("璁惧涓婄嚎浜嬩欢瑙﹀彂锛宒eviceId锛�" + event.getDeviceId() + ",from:" + event.getFrom()); } String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + event.getDeviceId(); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java index 4a4a538..c69faf9 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java @@ -17,13 +17,11 @@ private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>(); public String createPlaySsrc(){ - String ssrc = SsrcUtil.getPlaySsrc(); - return ssrc; + return SsrcUtil.getPlaySsrc(); } public String createPlayBackSsrc(){ - String ssrc = SsrcUtil.getPlayBackSsrc(); - return ssrc; + return SsrcUtil.getPlayBackSsrc(); } public void put(String ssrc,ClientTransaction transaction){ 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 7c5cbc1..0c1e63d 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 @@ -22,6 +22,8 @@ public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO"; + public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY"; + private Map<String, DeferredResult> map = new HashMap<String, DeferredResult>(); public void put(String key, DeferredResult result) { 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 32cc06e..d3f36cd 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 @@ -2,6 +2,7 @@ import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; /** @@ -20,7 +21,7 @@ * @param upDown 闀滃ご涓婄Щ涓嬬Щ 0:鍋滄 1:涓婄Щ 2:涓嬬Щ * @param moveSpeed 闀滃ご绉诲姩閫熷害 */ - public boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown); + boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown); /** * 浜戝彴鏂瑰悜鏀炬帶鍒� @@ -31,7 +32,7 @@ * @param upDown 闀滃ご涓婄Щ涓嬬Щ 0:鍋滄 1:涓婄Щ 2:涓嬬Щ * @param moveSpeed 闀滃ご绉诲姩閫熷害 */ - public boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed); + boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed); /** * 浜戝彴缂╂斁鎺у埗锛屼娇鐢ㄩ厤缃枃浠朵腑鐨勯粯璁ら暅澶寸缉鏀鹃�熷害 @@ -40,7 +41,7 @@ * @param channelId 棰勮閫氶亾 * @param inOut 闀滃ご鏀惧ぇ缂╁皬 0:鍋滄 1:缂╁皬 2:鏀惧ぇ */ - public boolean ptzZoomCmd(Device device,String channelId,int inOut); + boolean ptzZoomCmd(Device device,String channelId,int inOut); /** * 浜戝彴缂╂斁鎺у埗 @@ -50,7 +51,7 @@ * @param inOut 闀滃ご鏀惧ぇ缂╁皬 0:鍋滄 1:缂╁皬 2:鏀惧ぇ * @param zoomSpeed 闀滃ご缂╂斁閫熷害 */ - public boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed); + boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed); /** * 浜戝彴鎺у埗锛屾敮鎸佹柟鍚戜笌缂╂斁鎺у埗 @@ -63,7 +64,7 @@ * @param moveSpeed 闀滃ご绉诲姩閫熷害 * @param zoomSpeed 闀滃ご缂╂斁閫熷害 */ - public boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed); + boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed); /** * 鍓嶇鎺у埗锛屽寘鎷琍TZ鎸囦护銆丗I鎸囦护銆侀缃綅鎸囦护銆佸贰鑸寚浠ゃ�佹壂鎻忔寚浠ゅ拰杈呭姪寮�鍏虫寚浠� @@ -75,7 +76,7 @@ * @param parameter2 鏁版嵁2 * @param combineCode2 缁勫悎鐮�2 */ - public boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2); + boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2); /** * 璇锋眰棰勮瑙嗛娴� @@ -83,7 +84,7 @@ * @param device 瑙嗛璁惧 * @param channelId 棰勮閫氶亾 */ - public StreamInfo playStreamCmd(Device device, String channelId); + void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event); /** * 璇锋眰鍥炴斁瑙嗛娴� @@ -93,14 +94,14 @@ * @param startTime 寮�濮嬫椂闂�,鏍煎紡瑕佹眰锛歽yyy-MM-dd HH:mm:ss * @param endTime 缁撴潫鏃堕棿,鏍煎紡瑕佹眰锛歽yyy-MM-dd HH:mm:ss */ - public StreamInfo playbackStreamCmd(Device device,String channelId, String startTime, String endTime); + void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event); /** * 瑙嗛娴佸仠姝� * * @param ssrc ssrc */ - public void streamByeCmd(String ssrc); + void streamByeCmd(String ssrc); /** * 璇煶骞挎挱 @@ -108,7 +109,7 @@ * @param device 瑙嗛璁惧 * @param channelId 棰勮閫氶亾 */ - public boolean audioBroadcastCmd(Device device,String channelId); + boolean audioBroadcastCmd(Device device,String channelId); /** * 闊宠棰戝綍鍍忔帶鍒� @@ -116,21 +117,21 @@ * @param device 瑙嗛璁惧 * @param channelId 棰勮閫氶亾 */ - public boolean recordCmd(Device device,String channelId); + boolean recordCmd(Device device,String channelId); /** * 鎶ヨ甯冮槻/鎾ら槻鍛戒护 * * @param device 瑙嗛璁惧 */ - public boolean guardCmd(Device device); + boolean guardCmd(Device device); /** * 鎶ヨ澶嶄綅鍛戒护 * * @param device 瑙嗛璁惧 */ - public boolean alarmCmd(Device device); + boolean alarmCmd(Device device); /** * 寮哄埗鍏抽敭甯у懡浠�,璁惧鏀跺埌姝ゅ懡浠ゅ簲绔嬪埢鍙戦�佷竴涓狪DR甯� @@ -138,21 +139,21 @@ * @param device 瑙嗛璁惧 * @param channelId 棰勮閫氶亾 */ - public boolean iFameCmd(Device device,String channelId); + boolean iFameCmd(Device device,String channelId); /** * 鐪嬪畧浣嶆帶鍒跺懡浠� * * @param device 瑙嗛璁惧 */ - public boolean homePositionCmd(Device device); + boolean homePositionCmd(Device device); /** * 璁惧閰嶇疆鍛戒护 * * @param device 瑙嗛璁惧 */ - public boolean deviceConfigCmd(Device device); + boolean deviceConfigCmd(Device device); /** @@ -160,7 +161,7 @@ * * @param device 瑙嗛璁惧 */ - public boolean deviceStatusQuery(Device device); + boolean deviceStatusQuery(Device device); /** * 鏌ヨ璁惧淇℃伅 @@ -168,14 +169,14 @@ * @param device 瑙嗛璁惧 * @return */ - public boolean deviceInfoQuery(Device device); + boolean deviceInfoQuery(Device device); /** * 鏌ヨ鐩綍鍒楄〃 * * @param device 瑙嗛璁惧 */ - public boolean catalogQuery(Device device); + boolean catalogQuery(Device device); /** * 鏌ヨ褰曞儚淇℃伅 @@ -184,35 +185,33 @@ * @param startTime 寮�濮嬫椂闂�,鏍煎紡瑕佹眰锛歽yyy-MM-dd HH:mm:ss * @param endTime 缁撴潫鏃堕棿,鏍煎紡瑕佹眰锛歽yyy-MM-dd HH:mm:ss */ - public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime); + boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime); /** * 鏌ヨ鎶ヨ淇℃伅 * * @param device 瑙嗛璁惧 */ - public boolean alarmInfoQuery(Device device); + boolean alarmInfoQuery(Device device); /** * 鏌ヨ璁惧閰嶇疆 * * @param device 瑙嗛璁惧 */ - public boolean configQuery(Device device); + boolean configQuery(Device device); /** * 鏌ヨ璁惧棰勭疆浣嶇疆 * * @param device 瑙嗛璁惧 */ - public boolean presetQuery(Device device); + boolean presetQuery(Device device); /** * 鏌ヨ绉诲姩璁惧浣嶇疆鏁版嵁 * * @param device 瑙嗛璁惧 */ - public boolean mobilePostitionQuery(Device device); - - + boolean mobilePostitionQuery(Device device); } 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 42657a6..3d5aacc 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 @@ -19,6 +19,7 @@ import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; import com.genersoft.iot.vmp.media.zlm.ZLMUtils; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import org.springframework.beans.factory.annotation.Autowired; @@ -66,6 +67,9 @@ @Value("${media.rtp.enable}") private boolean rtpEnable; + + @Autowired + private ZLMHttpHookSubscribe subscribe; @@ -264,12 +268,12 @@ } /** * 璇锋眰棰勮瑙嗛娴� - * + * * @param device 瑙嗛璁惧 * @param channelId 棰勮閫氶亾 - */ + */ @Override - public StreamInfo playStreamCmd(Device device, String channelId) { + public void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event) { try { String ssrc = streamSession.createPlaySsrc(); @@ -282,53 +286,63 @@ }else { mediaPort = mediaInfo.getRtpProxyPort(); } + + String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); + // 娣诲姞璁㈤槄 + JSONObject subscribeKey = new JSONObject(); + subscribeKey.put("app", "rtp"); + subscribeKey.put("id", streamId); + + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, subscribeKey, event); // StringBuffer content = new StringBuffer(200); - content.append("v=0\r\n"); - content.append("o="+channelId+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); - content.append("s=Play\r\n"); - content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n"); - content.append("t=0 0\r\n"); - if("TCP-PASSIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); + content.append("v=0\r\n"); + content.append("o="+channelId+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); + content.append("s=Play\r\n"); + content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n"); + content.append("t=0 0\r\n"); + if("TCP-PASSIVE".equals(streamMode)) { + content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n"); }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); + content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n"); }else if("UDP".equals(streamMode)) { - content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n"); + content.append("m=video "+ mediaPort +" RTP/AVP 126 125 99 34 98 97 96\r\n"); } - content.append("a=recvonly\r\n"); - content.append("a=rtpmap:96 PS/90000\r\n"); - content.append("a=rtpmap:98 H264/90000\r\n"); - content.append("a=rtpmap:97 MPEG4/90000\r\n"); - if("TCP-PASSIVE".equals(streamMode)){ // tcp琚姩妯″紡 - content.append("a=setup:passive\r\n"); + content.append("a=recvonly\r\n"); + content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:126 H264/90000\r\n"); + content.append("a=rtpmap:125 H264S/90000\r\n"); + content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); + content.append("a=fmtp:99 profile-level-id=3\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + if("TCP-PASSIVE".equals(streamMode)){ // tcp琚姩妯″紡 + content.append("a=setup:passive\r\n"); content.append("a=connection:new\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp涓诲姩妯″紡 + }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp涓诲姩妯″紡 content.append("a=setup:active\r\n"); content.append("a=connection:new\r\n"); } - content.append("y="+ssrc+"\r\n");//ssrc - - Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "live", null, ssrc); - - ClientTransaction transaction = transmitRequest(device, request); - streamSession.put(ssrc, transaction); + content.append("y="+ssrc+"\r\n");//ssrc + + Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "live", null, ssrc); + + ClientTransaction transaction = transmitRequest(device, request); + streamSession.put(ssrc, transaction); DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId); if (deviceChannel != null) { deviceChannel.setSsrc(ssrc); storager.updateChannel(device.getDeviceId(), deviceChannel); } - StreamInfo streamInfo = new StreamInfo(); - streamInfo.setSsrc(ssrc); - streamInfo.setCahnnelId(channelId); - streamInfo.setDeviceID(device.getDeviceId()); - storager.startPlay(streamInfo); - return streamInfo; + // TODO 璁㈤槄SIP response锛屽鐞嗗鏂圭殑閿欒杩斿洖 + + } catch ( SipException | ParseException | InvalidArgumentException e) { e.printStackTrace(); - return null; - } + } } /** @@ -340,10 +354,18 @@ * @param endTime 缁撴潫鏃堕棿,鏍煎紡瑕佹眰锛歽yyy-MM-dd HH:mm:ss */ @Override - public StreamInfo playbackStreamCmd(Device device, String channelId, String startTime, String endTime) { + public void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event) { try { MediaServerConfig mediaInfo = storager.getMediaInfo(); String ssrc = streamSession.createPlayBackSsrc(); + String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); + // 娣诲姞璁㈤槄 + JSONObject subscribeKey = new JSONObject(); + subscribeKey.put("app", "rtp"); + subscribeKey.put("id", streamId); + + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, subscribeKey, event); + // StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); @@ -362,16 +384,22 @@ } String streamMode = device.getStreamMode().toUpperCase(); if("TCP-PASSIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); + content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n"); }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); + content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n"); }else if("UDP".equals(streamMode)) { - content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n"); + content.append("m=video "+ mediaPort +" RTP/AVP 126 125 99 34 98 97 96\r\n"); } - content.append("a=recvonly\r\n"); - content.append("a=rtpmap:96 PS/90000\r\n"); - content.append("a=rtpmap:98 H264/90000\r\n"); - content.append("a=rtpmap:97 MPEG4/90000\r\n"); + content.append("a=recvonly\r\n"); + content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:126 H264/90000\r\n"); + content.append("a=rtpmap:125 H264S/90000\r\n"); + content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); + content.append("a=fmtp:99 profile-level-id=3\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); if("TCP-PASSIVE".equals(streamMode)){ // tcp琚姩妯″紡 content.append("a=setup:passive\r\n"); content.append("a=connection:new\r\n"); @@ -386,16 +414,8 @@ ClientTransaction transaction = transmitRequest(device, request); streamSession.put(ssrc, transaction); - StreamInfo streamInfo = new StreamInfo(); - streamInfo.setSsrc(ssrc); - streamInfo.setCahnnelId(channelId); - streamInfo.setDeviceID(device.getDeviceId()); - boolean b = storager.startPlayback(streamInfo); - return streamInfo; - } catch ( SipException | ParseException | InvalidArgumentException e) { e.printStackTrace(); - return null; } } @@ -433,6 +453,7 @@ clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest); } dialog.sendRequest(clientTransaction); + streamSession.remove(ssrc); } catch (TransactionDoesNotExistException e) { e.printStackTrace(); } catch (SipException e) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java index aebc601..13d630c 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java @@ -21,14 +21,12 @@ * 澶勭悊 ACK璇锋眰 * * @param evt - * @param layer - * @param transaction - * @param config - */ + */ @Override public void process(RequestEvent evt) { Request request = evt.getRequest(); Dialog dialog = evt.getDialog(); + if (dialog == null) return; try { Request ackRequest = null; CSeq csReq = (CSeq) request.getHeader(CSeq.NAME); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java index 0730731..0ba6bd8 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java @@ -1,8 +1,13 @@ package com.genersoft.iot.vmp.gb28181.transmit.request.impl; +import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; + +import java.text.ParseException; /** * @Description: BYE璇锋眰澶勭悊鍣� @@ -11,18 +16,35 @@ */ public class ByeRequestProcessor extends SIPRequestAbstractProcessor { - /** + /** * 澶勭悊BYE璇锋眰 - * * @param evt - * @param layer - * @param transaction - * @param config - */ + */ @Override public void process(RequestEvent evt) { + try { + responseAck(evt); + } catch (SipException e) { + e.printStackTrace(); + } catch (InvalidArgumentException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } // TODO 浼樺厛绾�99 Bye Request娑堟伅瀹炵幇锛屾娑堟伅涓�鑸负绾ц仈娑堟伅锛屼笂绾х粰涓嬬骇鍙戦�佽棰戝仠姝㈡寚浠� } + /*** + * 鍥炲200 OK + * @param evt + * @throws SipException + * @throws InvalidArgumentException + * @throws ParseException + */ + private void responseAck(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException { + Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest()); + getServerTransaction(evt).sendResponse(response); + } + } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java index 4c95cf1..c987f5e 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java @@ -184,10 +184,11 @@ DeviceChannel deviceChannel = new DeviceChannel(); deviceChannel.setName(channelName); deviceChannel.setChannelId(channelDeviceId); - if (status.equals("ON") || status.equals("On")) { + // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR鐨勫吋瀹规�у鐞� + if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) { deviceChannel.setStatus(1); } - if (status.equals("OFF") || status.equals("Off")) { + if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) { deviceChannel.setStatus(0); } 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 b9c05e4..99da624 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 @@ -49,6 +49,9 @@ @Autowired private ZLMRESTfulUtils zlmresTfulUtils; + @Autowired + private ZLMHttpHookSubscribe subscribe; + @Value("${media.ip}") private String mediaIp; @@ -128,30 +131,38 @@ } String app = json.getString("app"); String streamId = json.getString("id"); - if ("rtp".equals(app)) { - String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16)); - StreamInfo streamInfoForPlay = storager.queryPlayBySSRC(ssrc); - if ("rtp".equals(app) && streamInfoForPlay != null ) { - MediaServerConfig mediaInfo = storager.getMediaInfo(); - streamInfoForPlay.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoForPlay.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoForPlay.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId)); - streamInfoForPlay.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoForPlay.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); - storager.startPlay(streamInfoForPlay); - } - StreamInfo streamInfoForPlayBack = storager.queryPlaybackBySSRC(ssrc); - if ("rtp".equals(app) && streamInfoForPlayBack != null ) { - MediaServerConfig mediaInfo = storager.getMediaInfo(); - streamInfoForPlayBack.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoForPlayBack.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoForPlayBack.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId)); - streamInfoForPlayBack.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoForPlayBack.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); - storager.startPlayback(streamInfoForPlayBack); - } - } + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); + if (subscribe != null) subscribe.response(json); + +// if ("rtp".equals(app)) { +// String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16)); +// StreamInfo streamInfoForPlay = storager.queryPlayBySSRC(ssrc); +// if ("rtp".equals(app) && streamInfoForPlay != null ) { +// MediaServerConfig mediaInfo = storager.getMediaInfo(); +// streamInfoForPlay.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); +// streamInfoForPlay.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); +// streamInfoForPlay.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); +// streamInfoForPlay.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); +// streamInfoForPlay.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId)); +// streamInfoForPlay.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); +// streamInfoForPlay.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); +// storager.startPlay(streamInfoForPlay); +// } +// +// StreamInfo streamInfoForPlayBack = storager.queryPlaybackBySSRC(ssrc); +// if ("rtp".equals(app) && streamInfoForPlayBack != null ) { +// MediaServerConfig mediaInfo = storager.getMediaInfo(); +// streamInfoForPlayBack.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); +// streamInfoForPlayBack.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); +// streamInfoForPlayBack.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); +// streamInfoForPlayBack.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); +// streamInfoForPlayBack.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId)); +// streamInfoForPlayBack.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); +// streamInfoForPlayBack.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); +// storager.startPlayback(streamInfoForPlayBack); +// } +// } // TODO Auto-generated method stub diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java new file mode 100644 index 0000000..0c00b82 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java @@ -0,0 +1,90 @@ +package com.genersoft.iot.vmp.media.zlm; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.MediaServerConfig; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.ConcurrentReferenceHashMap; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.math.BigInteger; +import java.text.DecimalFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @Description:閽堝 ZLMediaServer鐨刪ook浜嬩欢璁㈤槄 + * @author: pan + * @date: 2020骞�12鏈�2鏃� 21:17:32 + */ +@Component +public class ZLMHttpHookSubscribe { + + private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookSubscribe.class); + + public enum HookType{ + on_flow_report, + on_http_access, + on_play, + on_publish, + on_record_mp4, + on_rtsp_auth, + on_rtsp_realm, + on_shell_login, + on_stream_changed, + on_stream_none_reader, + on_stream_not_found, + on_server_started + } + + public interface Event{ + void response(JSONObject response); + } + + private Map<HookType, Map<JSONObject, ZLMHttpHookSubscribe.Event>> allSubscribes = new ConcurrentHashMap<>(); + + public void addSubscribe(HookType type, JSONObject hookResponse, ZLMHttpHookSubscribe.Event event) { + Map<JSONObject, Event> eventMap = allSubscribes.get(type); + if (eventMap == null) { + eventMap = new HashMap<JSONObject, Event>(); + allSubscribes.put(type,eventMap); + } + eventMap.put(hookResponse, event); + } + + public ZLMHttpHookSubscribe.Event getSubscribe(HookType type, JSONObject hookResponse) { + ZLMHttpHookSubscribe.Event event= null; + Map<JSONObject, Event> eventMap = allSubscribes.get(type); + if (eventMap == null) { + return null; + } + for (JSONObject key : eventMap.keySet()) { + Boolean result = null; + for (String s : key.keySet()) { + String string = hookResponse.getString(s); + String string1 = key.getString(s); + if (result == null) { + result = key.getString(s).equals(hookResponse.getString(s)); + }else { + result = result && key.getString(s).equals(hookResponse.getString(s)); + } + + } + if (result) { + event = eventMap.get(key); + } + } + return event; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java index 66f8e61..3f88b2a 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java @@ -36,6 +36,9 @@ @Value("${media.wanIp}") private String mediaWanIp; + @Value("${media.hookIp}") + private String mediaHookIp; + @Value("${media.port}") private int mediaPort; @@ -51,6 +54,9 @@ @Value("${server.port}") private String serverPort; + @Value("${media.autoConfig}") + private boolean autoConfig; + @Autowired private ZLMRESTfulUtils zlmresTfulUtils; @@ -61,8 +67,7 @@ MediaServerConfig mediaServerConfig = getMediaServerConfig(); if (mediaServerConfig != null) { logger.info("zlm鎺ュ叆鎴愬姛..."); - logger.info("璁剧疆zlm..."); - saveZLMConfig(); + if (autoConfig) saveZLMConfig(); mediaServerConfig = getMediaServerConfig(); storager.updateMediaInfo(mediaServerConfig); } @@ -91,12 +96,12 @@ } private void saveZLMConfig() { - String hookIP = sipIP; - if (mediaIp.equals(sipIP)) { - hookIP = "127.0.0.1"; + logger.info("璁剧疆zlm..."); + if (StringUtils.isEmpty(mediaHookIp)) { + mediaHookIp = sipIP; } - String hookPrex = String.format("http://%s:%s/index/hook", hookIP, serverPort); + String hookPrex = String.format("http://%s:%s/index/hook", mediaHookIp, serverPort); Map<String, Object> param = new HashMap<>(); param.put("api.secret",mediaSecret); // -profile:v Baseline param.put("ffmpeg.cmd","%s -fflags nobuffer -rtsp_transport tcp -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s"); diff --git a/src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java index ad77995..99c7f06 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java @@ -555,6 +555,10 @@ List<Object> playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, deviceId, code)); + if (playLeys == null || playLeys.size() == 0) { + playLeys = redis.scan(String.format("%S_*_*_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, + deviceId)); + } if (playLeys == null || playLeys.size() == 0) return null; return (StreamInfo)redis.get(playLeys.get(0).toString()); } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java index 300ffc3..e741b5a 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java @@ -4,7 +4,10 @@ import com.alibaba.fastjson.JSONArray; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.MediaServerConfig; +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; +import com.genersoft.iot.vmp.vmanager.service.IPlayService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -22,6 +25,10 @@ import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import org.springframework.web.context.request.async.DeferredResult; + +import java.text.DecimalFormat; +import java.util.UUID; @CrossOrigin @RestController @@ -39,94 +46,57 @@ @Autowired private ZLMRESTfulUtils zlmresTfulUtils; - @Value("${media.closeWaitRTPInfo}") - private boolean closeWaitRTPInfo; + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private IPlayService playService; @GetMapping("/play/{deviceId}/{channelId}") - public ResponseEntity<String> play(@PathVariable String deviceId, @PathVariable String channelId, - Integer getEncoding) { + public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, + @PathVariable String channelId) { - if (getEncoding == null) getEncoding = 0; - getEncoding = closeWaitRTPInfo ? 0 : getEncoding; + Device device = storager.queryVideoDevice(deviceId); StreamInfo streamInfo = storager.queryPlayByDevice(deviceId, channelId); + UUID uuid = UUID.randomUUID(); + DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(); + // 瓒呮椂澶勭悊 + result.onTimeout(()->{ + logger.warn(String.format("璁惧鐐规挱瓒呮椂锛宒eviceId锛�%s 锛宑hannelId锛�%s", deviceId, channelId)); + RequestMessage msg = new RequestMessage(); + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); + msg.setData("Timeout"); + resultHolder.invokeResult(msg); + }); + // 褰曞儚鏌ヨ浠hannelId浣滀负deviceId鏌ヨ + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); + if (streamInfo == null) { - streamInfo = cmder.playStreamCmd(device, channelId); + // 鍙戦�佺偣鎾秷鎭� + cmder.playStreamCmd(device, channelId, (JSONObject response) -> { + logger.info("鏀跺埌璁㈤槄娑堟伅锛� " + response.toJSONString()); + playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); + }); } else { String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); if (rtpInfo.getBoolean("exist")) { - return new ResponseEntity<String>(JSON.toJSONString(streamInfo), HttpStatus.OK); + RequestMessage msg = new RequestMessage(); + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); + msg.setData(JSON.toJSONString(streamInfo)); + resultHolder.invokeResult(msg); } else { storager.stopPlay(streamInfo); - streamInfo = cmder.playStreamCmd(device, channelId); + // TODO playStreamCmd 瓒呮椂澶勭悊 + cmder.playStreamCmd(device, channelId, (JSONObject response) -> { + logger.info("鏀跺埌璁㈤槄娑堟伅锛� " + response.toJSONString()); + playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); + }); } } - String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); - // 绛夊緟鎺ㄦ祦, TODO 榛樿瓒呮椂30s - boolean lockFlag = true; - boolean rtpPushed = false; - long startTime = System.currentTimeMillis(); - JSONObject rtpInfo = null; - - if (getEncoding == 1) { - while (lockFlag) { - try { - if (System.currentTimeMillis() - startTime > 60 * 1000) { - storager.stopPlay(streamInfo); - logger.info("鎾斁绛夊緟瓒呮椂"); - return new ResponseEntity<String>("timeout", HttpStatus.OK); - } else { - streamInfo = storager.queryPlayByDevice(deviceId, channelId); - if (!rtpPushed) { - logger.info("鏌ヨRTP鎺ㄦ祦淇℃伅..."); - rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); - } - if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo != null - && streamInfo.getFlv() != null) { - logger.info("鏌ヨ娴佺紪鐮佷俊鎭細" + streamInfo.getFlv()); - rtpPushed = true; - Thread.sleep(2000); - JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId); - if (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")) { - lockFlag = false; - logger.info("娴佺紪鐮佷俊鎭凡鑾峰彇"); - JSONArray tracks = mediaInfo.getJSONArray("tracks"); - streamInfo.setTracks(tracks); - storager.startPlay(streamInfo); - } else { - logger.info("娴佺紪鐮佷俊鎭湭鑾峰彇锛�2绉掑悗閲嶈瘯..."); - } - } else { - Thread.sleep(2000); - continue; - } - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } else { - String flv = storager.getMediaInfo().getWanIp() + ":" + storager.getMediaInfo().getHttpPort() + "/rtp/" - + streamId + ".flv"; - streamInfo.setFlv("http://" + flv); - streamInfo.setWs_flv("ws://" + flv); - storager.startPlay(streamInfo); - } - - if (logger.isDebugEnabled()) { - logger.debug(String.format("璁惧棰勮 API璋冪敤锛宒eviceId锛�%s 锛宑hannelId锛�%s", deviceId, channelId)); - logger.debug("璁惧棰勮 API璋冪敤锛宻src锛�" + streamInfo.getSsrc() + ",ZLMedia streamId:" - + Integer.toHexString(Integer.parseInt(streamInfo.getSsrc()))); - } - - if (streamInfo != null) { - return new ResponseEntity<String>(JSON.toJSONString(streamInfo), HttpStatus.OK); - } else { - logger.warn("璁惧棰勮API璋冪敤澶辫触锛�"); - return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); - } + return result; } @PostMapping("/play/{ssrc}/stop") @@ -172,17 +142,28 @@ MediaServerConfig mediaInfo = storager.getMediaInfo(); String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(), streamId ); - JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(streamInfo.getRtsp(), dstUrl, "1000000"); + String srcUrl = String.format("rtsp://%s:%s/rtp/%s", "127.0.0.1", mediaInfo.getRtspPort(), streamId); + JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(srcUrl, dstUrl, "1000000"); System.out.println(jsonObject); JSONObject result = new JSONObject(); if (jsonObject != null && jsonObject.getInteger("code") == 0) { result.put("code", 0); JSONObject data = jsonObject.getJSONObject("data"); if (data != null) { - result.put("key", data.getString("key")); - result.put("rtmp", dstUrl); - result.put("flv", String.format("http://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - result.put("ws_flv", String.format("ws://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + result.put("key", data.getString("key")); + StreamInfo streamInfoResult = new StreamInfo(); + streamInfoResult.setRtmp(dstUrl); + streamInfoResult.setRtsp(String.format("rtsp://%s:%s/convert/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); + streamInfoResult.setStreamId(streamId); + streamInfoResult.setFlv(String.format("http://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + streamInfoResult.setWs_flv(String.format("ws://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + streamInfoResult.setHls(String.format("http://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + streamInfoResult.setWs_hls(String.format("ws://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + streamInfoResult.setFmp4(String.format("http://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + streamInfoResult.setWs_fmp4(String.format("ws://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + streamInfoResult.setTs(String.format("http://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + streamInfoResult.setWs_ts(String.format("ws://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); + result.put("data", streamInfoResult); } }else { result.put("code", 1); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java index 16713fe..2e32b1b 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java @@ -3,7 +3,10 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; +import com.genersoft.iot.vmp.vmanager.service.IPlayService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -22,6 +25,9 @@ import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import org.springframework.web.context.request.async.DeferredResult; + +import java.util.UUID; @CrossOrigin @RestController @@ -39,105 +45,42 @@ @Autowired private ZLMRESTfulUtils zlmresTfulUtils; - @Value("${media.closeWaitRTPInfo}") - private boolean closeWaitRTPInfo; + @Autowired + private IPlayService playService; + + @Autowired + private DeferredResultHolder resultHolder; @GetMapping("/playback/{deviceId}/{channelId}") - public ResponseEntity<String> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime, - String endTime) { + public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime, + String endTime) { if (logger.isDebugEnabled()) { logger.debug(String.format("璁惧鍥炴斁 API璋冪敤锛宒eviceId锛�%s 锛宑hannelId锛�%s", deviceId, channelId)); } - - if (StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(channelId)) { - String log = String.format("璁惧鍥炴斁 API璋冪敤澶辫触锛宒eviceId锛�%s 锛宑hannelId锛�%s", deviceId, channelId); - logger.warn(log); - return new ResponseEntity<String>(log, HttpStatus.BAD_REQUEST); - } - + UUID uuid = UUID.randomUUID(); + DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(); + // 瓒呮椂澶勭悊 + result.onTimeout(()->{ + logger.warn(String.format("璁惧鍥炴斁瓒呮椂锛宒eviceId锛�%s 锛宑hannelId锛�%s", deviceId, channelId)); + RequestMessage msg = new RequestMessage(); + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); + msg.setData("Timeout"); + resultHolder.invokeResult(msg); + }); Device device = storager.queryVideoDevice(deviceId); StreamInfo streamInfo = storager.queryPlaybackByDevice(deviceId, channelId); - if (streamInfo != null) { + // 鍋滄涔嬪墠鐨勫洖鏀� cmder.streamByeCmd(streamInfo.getSsrc()); } + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); + cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> { + logger.info("鏀跺埌璁㈤槄娑堟伅锛� " + response.toJSONString()); + playService.onPublishHandlerForPlayBack(response, deviceId, channelId, uuid.toString()); + }); - // }else { - // String streamId = String.format("%08x", - // Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); - // JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); - // if (rtpInfo.getBoolean("exist")) { - // return new - // ResponseEntity<String>(JSON.toJSONString(streamInfo),HttpStatus.OK); - // }else { - // storager.stopPlayback(streamInfo); - // streamInfo = cmder.playbackStreamCmd(device, channelId, startTime, endTime); - // } - // } - streamInfo = cmder.playbackStreamCmd(device, channelId, startTime, endTime); - - String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); - - if (logger.isDebugEnabled()) { - logger.debug("璁惧鍥炴斁 API璋冪敤锛宻src锛�" + streamInfo.getSsrc() + ",ZLMedia streamId:" + streamId); - } - // 绛夊緟鎺ㄦ祦, TODO 榛樿瓒呮椂15s - boolean lockFlag = true; - boolean rtpPushed = false; - long lockStartTime = System.currentTimeMillis(); - JSONObject rtpInfo = null; - - if (closeWaitRTPInfo) { - String flv = storager.getMediaInfo().getWanIp() + ":" + storager.getMediaInfo().getHttpPort() + "/rtp/" - + streamId + ".flv"; - streamInfo.setFlv("http://" + flv); - streamInfo.setWs_flv("ws://" + flv); - storager.startPlayback(streamInfo); - } else { - while (lockFlag) { - try { - if (System.currentTimeMillis() - lockStartTime > 75 * 1000) { - storager.stopPlayback(streamInfo); - logger.info("鎾斁绛夊緟瓒呮椂"); - return new ResponseEntity<String>("timeout", HttpStatus.OK); - } else { - streamInfo = storager.queryPlaybackByDevice(deviceId, channelId); - if (!rtpPushed) { - logger.info("鏌ヨRTP鎺ㄦ祦淇℃伅..."); - rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); - } - if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo != null - && streamInfo.getFlv() != null) { - logger.info("鏌ヨ娴佺紪鐮佷俊鎭細" + streamInfo.getFlv()); - rtpPushed = true; - Thread.sleep(2000); - JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId); - if (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")) { - lockFlag = false; - logger.info("娴佺紪鐮佷俊鎭凡鑾峰彇"); - JSONArray tracks = mediaInfo.getJSONArray("tracks"); - streamInfo.setTracks(tracks); - storager.startPlayback(streamInfo); - } else { - logger.info("娴佺紪鐮佷俊鎭湭鑾峰彇锛�2绉掑悗閲嶈瘯..."); - } - } else { - Thread.sleep(2000); - continue; - } - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - if (streamInfo != null) { - return new ResponseEntity<String>(JSON.toJSONString(streamInfo), HttpStatus.OK); - } else { - logger.warn("璁惧鍥炴斁API璋冪敤澶辫触锛�"); - return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); - } + return result; } @RequestMapping("/playback/{ssrc}/stop") diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java new file mode 100644 index 0000000..a80ab5d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java @@ -0,0 +1,13 @@ +package com.genersoft.iot.vmp.vmanager.service; + +import com.alibaba.fastjson.JSONObject; +import com.genersoft.iot.vmp.common.StreamInfo; + +/** + * 鐐规挱澶勭悊 + */ +public interface IPlayService { + + void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, String uuid); + void onPublishHandlerForPlay(JSONObject resonse, String deviceId, String channelId, String uuid); +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java new file mode 100644 index 0000000..e9528d1 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java @@ -0,0 +1,90 @@ +package com.genersoft.iot.vmp.vmanager.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.MediaServerConfig; +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.vmanager.play.PlayController; +import com.genersoft.iot.vmp.vmanager.service.IPlayService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.text.DecimalFormat; + +@Service +public class PlayServiceImpl implements IPlayService { + + private final static Logger logger = LoggerFactory.getLogger(PlayServiceImpl.class); + + @Autowired + private IVideoManagerStorager storager; + + @Autowired + private DeferredResultHolder resultHolder; + + @Override + public void onPublishHandlerForPlay(JSONObject resonse, String deviceId, String channelId, String uuid) { + RequestMessage msg = new RequestMessage(); + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); + StreamInfo streamInfo = onPublishHandler(resonse, deviceId, channelId, uuid); + if (streamInfo != null) { + storager.startPlay(streamInfo); + msg.setData(JSON.toJSONString(streamInfo)); + resultHolder.invokeResult(msg); + } else { + logger.warn("璁惧棰勮API璋冪敤澶辫触锛�"); + msg.setData("璁惧棰勮API璋冪敤澶辫触锛�"); + resultHolder.invokeResult(msg); + } + } + + @Override + public void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, String uuid) { + RequestMessage msg = new RequestMessage(); + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); + StreamInfo streamInfo = onPublishHandler(resonse, deviceId, channelId, uuid); + if (streamInfo != null) { + storager.startPlayback(streamInfo); + msg.setData(JSON.toJSONString(streamInfo)); + resultHolder.invokeResult(msg); + } else { + logger.warn("璁惧棰勮API璋冪敤澶辫触锛�"); + msg.setData("璁惧棰勮API璋冪敤澶辫触锛�"); + resultHolder.invokeResult(msg); + } + } + + public StreamInfo onPublishHandler(JSONObject resonse, String deviceId, String channelId, String uuid) { + String streamId = resonse.getString("id"); + String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16)); + StreamInfo streamInfo = new StreamInfo(); + streamInfo.setSsrc(ssrc); + streamInfo.setStreamId(streamId); + streamInfo.setDeviceID(deviceId); + streamInfo.setCahnnelId(channelId); + MediaServerConfig mediaServerConfig = storager.getMediaInfo(); + + streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); + streamInfo.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); + + streamInfo.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); + streamInfo.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); + + streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); + streamInfo.setWs_hls(String.format("ws://%s:%s/rtp/%s/hls.m3u8", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); + + streamInfo.setTs(String.format("http://%s:%s/rtp/%s.live.ts", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); + streamInfo.setWs_ts(String.format("ws://%s:%s/rtp/%s.live.ts", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); + + streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaServerConfig.getWanIp(), mediaServerConfig.getRtmpPort(), streamId)); + streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaServerConfig.getWanIp(), mediaServerConfig.getRtspPort(), streamId)); + + return streamInfo; + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java b/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java index 40b1b5b..6180fbb 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java +++ b/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java @@ -34,8 +34,7 @@ @Autowired private IVideoManagerStorager storager; - @Value("${media.closeWaitRTPInfo}") - private boolean closeWaitRTPInfo; + private boolean closeWaitRTPInfo = false; @Autowired @@ -94,7 +93,7 @@ StreamInfo streamInfo = storager.queryPlayByDevice(device.getDeviceId(), code); if (streamInfo == null) { logger.debug("streamInfo 绛変簬null, 閲嶆柊鐐规挱"); - streamInfo = cmder.playStreamCmd(device, code); +// streamInfo = cmder.playStreamCmd(device, code); }else { logger.debug("streamInfo 涓嶇瓑浜巒ull, 鍚戞祦濯掍綋鏌ヨ鏄惁姝e湪鎺ㄦ祦"); String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); @@ -136,7 +135,7 @@ } else { logger.debug("鍚戞祦濯掍綋鏌ヨ娌℃湁鎺ㄦ祦, 閲嶆柊鐐规挱"); storager.stopPlay(streamInfo); - streamInfo = cmder.playStreamCmd(device, code); +// streamInfo = cmder.playStreamCmd(device, code); } } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..638d5ae --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,94 @@ +spring: + # [涓嶉渶瑕佹敼] + application: + name: iot-vmp-vmanager + # [涓嶉渶瑕佹敼] 褰卞瓙鏁版嵁瀛樺偍鏂瑰紡锛屾敮鎸乺edis銆乯dbc,鏆備笉鏀寔mysql, + database: redis + # [涓嶉渶瑕佹敼] 閫氫俊鏂瑰紡锛屾敮鎸乲afka銆乭ttp + communicate: http + # REDIS鏁版嵁搴撻厤缃� + redis: + # [蹇呴』淇敼] Redis鏈嶅姟鍣↖P, REDIS瀹夎鍦ㄦ湰鏈虹殑,浣跨敤127.0.0.1 + host: 127.0.0.1 + # [蹇呴』淇敼] 绔彛鍙� + port: 6379 + # [鍙�塢 鏁版嵁搴� DB + database: 6 + # [鍙�塢 璁块棶瀵嗙爜,鑻ヤ綘鐨剅edis鏈嶅姟鍣ㄦ病鏈夎缃瘑鐮侊紝灏变笉闇�瑕佺敤瀵嗙爜鍘昏繛鎺� + password: + # [鍙�塢 瓒呮椂鏃堕棿 + timeout: 10000 + # [涓嶅彲鐢╙ jdbc鏁版嵁搴撻厤缃�, 鏆備笉鏀寔 + datasource: + name: eiot + url: jdbc:mysql://127.0.0.1:3306/eiot?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true + username: + password: + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.jdbc.Driver + +# [鍙�塢 WVP鐩戝惉鐨凥TTP绔彛, 缃戦〉鍜屾帴鍙h皟鐢ㄩ兘鏄繖涓鍙� +server: + port: 18080 + +# 浣滀负28181鏈嶅姟鍣ㄧ殑閰嶇疆 +sip: + # [蹇呴』淇敼] 鏈満鐨処P, 蹇呴』鏄綉鍗′笂鐨処P + ip: 192.168.0.100 + # [鍙�塢 28181鏈嶅姟鐩戝惉鐨勭鍙� + port: 5060 + # 鏍规嵁鍥芥爣6.1.2涓瀹氾紝domain瀹滈噰鐢↖D缁熶竴缂栫爜鐨勫墠鍗佷綅缂栫爜銆傚浗鏍囬檮褰旸涓畾涔夊墠8浣嶄负涓績缂栫爜锛堢敱鐪佺骇銆佸競绾с�佸尯绾с�佸熀灞傜紪鍙风粍鎴愶紝鍙傜収GB/T 2260-2007锛� + # 鍚庝袱浣嶄负琛屼笟缂栫爜锛屽畾涔夊弬鐓ч檮褰旸.3 + # 3701020049鏍囪瘑灞变笢娴庡崡鍘嗕笅鍖� 淇℃伅琛屼笟鎺ュ叆 + # [鍙�塢 + domain: 4401020049 + # [鍙�塢 + id: 44010200492000000001 + # [鍙�塢 榛樿璁惧璁よ瘉瀵嗙爜锛屽悗缁墿灞曚娇鐢ㄨ澶囧崟鐙瘑鐮� + password: admin123 + +# 鐧婚檰鐨勭敤鎴峰悕瀵嗙爜 +auth: + # [鍙�塢 鐢ㄦ埛鍚� + username: admin + # [鍙�塢 瀵嗙爜, 榛樿涓篴dmin + password: 21232f297a57a5a743894a0e4a801fc3 + +#zlm鏈嶅姟鍣ㄩ厤缃� +media: + # [蹇呴』淇敼] zlm鏈嶅姟鍣ㄧ殑鍐呯綉IP + ip: 192.168.0.100 + # [鍙�塢 zlm鏈嶅姟鍣ㄧ殑鍏綉IP, 鍐呯綉閮ㄧ讲缃┖鍗冲彲 + wanIp: + # [鍙�塢 zlm鏈嶅姟鍣ㄧ殑hook鎵�浣跨敤鐨処P, 榛樿浣跨敤sip.ip + hookIp: + # [蹇呴』淇敼] zlm鏈嶅姟鍣ㄧ殑http.port + port: 80 + # [鍙�塢 鏄惁鑷姩閰嶇疆ZLM, 濡傛灉甯屾湜鎵嬪姩閰嶇疆ZLM, 鍙互璁句负false, 涓嶅缓璁柊鎺ヨЕ鐨勭敤鎴蜂慨鏀� + autoConfig: true + # [鍙�塢 zlm鏈嶅姟鍣ㄧ殑hook.admin_params=secret + secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc + # [鍙�塢 zlm鏈嶅姟鍣ㄧ殑general.streamNoneReaderDelayMS + streamNoneReaderDelayMS: 18000 # 鏃犱汉瑙傜湅澶氫箙鑷姩鍏抽棴娴� + # [鍙�塢 鍏抽棴绛夊緟鏀跺埌娴佺紪鐮佷俊鎭悗鍦ㄨ繑鍥�, + # 璁句负false鍙互鑾峰緱鏇村ソ鐨勫吋瀹规��,淇濊瘉杩斿洖鍚庢祦灏卞彲浠ユ挱鏀�, + # 璁句负true鍙互蹇�熸墦寮�鎾斁绐楀彛,鍙互鑾峰緱鏇村ソ鐨勪綋楠� + closeWaitRTPInfo: false + # 鍚敤udp澶氱鍙fā寮� + rtp: + # [鍙�塢 鏄惁鍚敤udp澶氱鍙fā寮�, 寮�鍚悗浼氬湪udpPortRange鑼冨洿鍐呴�夋嫨绔彛鐢ㄤ簬濯掍綋娴佷紶杈� + enable: true + # [鍙�塢 鍦ㄦ鑼冨洿鍐呴�夋嫨绔彛鐢ㄤ簬濯掍綋娴佷紶杈�, 涓嶅彧鏄痷dp, 浣跨敤TCP琚姩浼犺緭妯″紡鏃�,涔熸槸浠庤繖涓寖鍥村唴閫夋嫨绔彛 + udpPortRange: 30000,30500 # 绔彛鑼冨洿 + +# [鍙�塢 鏃ュ織閰嶇疆, 涓�鑸笉闇�瑕佹敼 +logging: + file: + name: logs/wvp.log + max-history: 30 + max-size: 10MB + total-size-cap: 300MB + level: + com: + genersoft: + iot: debug \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index fd7509e..caf4dfc 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,64 +1,3 @@ spring: - application: - name: iot-vmp-vmanager - # 褰卞瓙鏁版嵁瀛樺偍鏂瑰紡锛屾敮鎸乺edis銆乯dbc,鏆備笉鏀寔mysql - database: redis - # 閫氫俊鏂瑰紡锛屾敮鎸乲afka銆乭ttp - communicate: http - redis: - # Redis鏈嶅姟鍣↖P - host: 192.168.1.141 - #绔彛鍙� - port: 6379 - database: 6 - #璁块棶瀵嗙爜,鑻ヤ綘鐨剅edis鏈嶅姟鍣ㄦ病鏈夎缃瘑鐮侊紝灏变笉闇�瑕佺敤瀵嗙爜鍘昏繛鎺� - password: 4767cb971b40a1300fa09b7f87b09d1c - #瓒呮椂鏃堕棿 - timeout: 10000 - datasource: - name: eiot - url: jdbc:mysql://127.0.0.1:3306/eiot?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true - username: - password: - type: com.alibaba.druid.pool.DruidDataSource - driver-class-name: com.mysql.jdbc.Driver -server: - port: 18080 -sip: - ip: 192.168.1.20 - port: 5060 - # 鏍规嵁鍥芥爣6.1.2涓瀹氾紝domain瀹滈噰鐢↖D缁熶竴缂栫爜鐨勫墠鍗佷綅缂栫爜銆傚浗鏍囬檮褰旸涓畾涔夊墠8浣嶄负涓績缂栫爜锛堢敱鐪佺骇銆佸競绾с�佸尯绾с�佸熀灞傜紪鍙风粍鎴愶紝鍙傜収GB/T 2260-2007锛� - # 鍚庝袱浣嶄负琛屼笟缂栫爜锛屽畾涔夊弬鐓ч檮褰旸.3 - # 3701020049鏍囪瘑灞变笢娴庡崡鍘嗕笅鍖� 淇℃伅琛屼笟鎺ュ叆 - domain: 3402000000 - id: 34020000002000000001 - # 榛樿璁惧璁よ瘉瀵嗙爜锛屽悗缁墿灞曚娇鐢ㄨ澶囧崟鐙瘑鐮� - password: 12345678 - -auth: #32浣嶅皬鍐檓d5鍔犲瘑锛堥粯璁ゅ瘑鐮佷负admin锛� - username: admin - password: 21232f297a57a5a743894a0e4a801fc3 - -media: #zlm鏈嶅姟鍣ㄧ殑ip涓巋ttp绔彛, 閲嶇偣: 杩欐槸http绔彛 - ip: 127.0.0.1 - wanIp: 192.168.1.20 - port: 6080 - secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc - streamNoneReaderDelayMS: 600000 # 鏃犱汉瑙傜湅澶氫箙鑷姩鍏抽棴娴� - # 鍏抽棴绛夊緟鏀跺埌娴佺紪鐮佷俊鎭悗鍦ㄨ繑鍥�, - # 璁句负false鍙互鑾峰緱鏇村ソ鐨勫吋瀹规��,淇濊瘉杩斿洖鍚庢祦灏卞彲浠ユ挱鏀�, - # 璁句负true鍙互蹇�熸墦寮�鎾斁绐楀彛,鍙互鑾峰緱鏇村ソ鐨勪綋楠� - closeWaitRTPInfo: true - rtp: # 鍚敤udp澶氱鍙fā寮� - enable: true - udpPortRange: 30000,30500 # 绔彛鑼冨洿 -logging: - file: - name: logs/wvp.log - max-history: 30 - max-size: 10MB - total-size-cap: 300MB - level: - com: - genersoft: - iot: debug \ No newline at end of file + profiles: + active: dev \ No newline at end of file diff --git a/web_src/build/webpack.dev.conf.js b/web_src/build/webpack.dev.conf.js index c12be3e..e33cb01 100755 --- a/web_src/build/webpack.dev.conf.js +++ b/web_src/build/webpack.dev.conf.js @@ -64,9 +64,8 @@ to: config.dev.assetsSubDirectory, ignore: ['.*'] }, - { from: 'node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml'}, - { from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf'}, - { from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js', to: 'js/'} + { from: 'node_modules/@easydarwin/easywasmplayer/libDecoder.wasm'}, + { from: 'node_modules/@easydarwin/easywasmplayer/EasyWasmPlayer.js', to: 'js/'} ]) ] }) diff --git a/web_src/build/webpack.prod.conf.js b/web_src/build/webpack.prod.conf.js index 2bfb52c..13d373d 100644 --- a/web_src/build/webpack.prod.conf.js +++ b/web_src/build/webpack.prod.conf.js @@ -115,9 +115,8 @@ to: config.build.assetsSubDirectory, ignore: ['.*'] }, - { from: 'node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml'}, - { from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf'}, - { from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js', to: 'js/'} + { from: 'node_modules/@easydarwin/easywasmplayer/libDecoder.wasm'}, + { from: 'node_modules/@easydarwin/easywasmplayer/EasyWasmPlayer.js', to: 'js/'} ]) ] }) diff --git a/web_src/index.html b/web_src/index.html index 9c4e39b..1224125 100644 --- a/web_src/index.html +++ b/web_src/index.html @@ -6,7 +6,7 @@ <title>鍥芥爣28181</title> </head> <body> - <script type="text/javascript" src="./js/liveplayer-lib.min.js"></script> + <script type="text/javascript" src="./js/EasyWasmPlayer.js"></script> <div id="app"></div> <!-- built files will be auto injected --> </body> diff --git a/web_src/package-lock.json b/web_src/package-lock.json index e00c5ce..334f488 100644 --- a/web_src/package-lock.json +++ b/web_src/package-lock.json @@ -4,10 +4,10 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@liveqing/liveplayer": { - "version": "1.9.9", - "resolved": "https://registry.npm.taobao.org/@liveqing/liveplayer/download/@liveqing/liveplayer-1.9.9.tgz", - "integrity": "sha1-K7wiab+BiY5qe1/nTpKVyeGdIGo=" + "@easydarwin/easywasmplayer": { + "version": "4.0.7", + "resolved": "https://registry.npm.taobao.org/@easydarwin/easywasmplayer/download/@easydarwin/easywasmplayer-4.0.7.tgz", + "integrity": "sha1-FNtIUXbdwIWdalvIMEaH0+zUGx4=" }, "@types/q": { "version": "1.5.4", diff --git a/web_src/package.json b/web_src/package.json index 05178d4..c949393 100644 --- a/web_src/package.json +++ b/web_src/package.json @@ -10,7 +10,7 @@ "build": "node build/build.js" }, "dependencies": { - "@liveqing/liveplayer": "^1.9.6", + "@easydarwin/easywasmplayer": "^4.0.7", "axios": "^0.19.2", "core-js": "^2.6.5", "echarts": "^4.7.0", diff --git a/web_src/src/components/gb28181/devicePlayer.vue b/web_src/src/components/gb28181/devicePlayer.vue index c87d0c4..8442a82 100644 --- a/web_src/src/components/gb28181/devicePlayer.vue +++ b/web_src/src/components/gb28181/devicePlayer.vue @@ -1,9 +1,11 @@ <template> <div id="devicePlayer" v-loading="isLoging"> + <el-dialog title="瑙嗛鎾斁" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()"> - <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> + <!-- <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> --> + <player ref="videoPlayer" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></player> <div id="shared" style="text-align: right; margin-top: 1rem;"> - <el-tabs v-model="tabActiveName"> + <el-tabs v-model="tabActiveName" @tab-click="tabHandleClick"> <el-tab-pane label="瀹炴椂瑙嗛" name="media"> <div style="margin-bottom: 0.5rem;"> <!-- <el-button type="primary" size="small" @click="playRecord(true, '')">鎾斁</el-button>--> @@ -97,6 +99,32 @@ </div> </div> </el-tab-pane> + <el-tab-pane label="缂栫爜淇℃伅" name="codec" v-loading="tracksLoading"> + <p> + 鏃犳硶鎾斁鎴栬�呮病鏈夊0闊�?   璇曚竴璇� + <el-button size="mini" type="primary" v-if="!coverPlaying" @click="coverPlay">杞爜鎾斁</el-button> + <el-button size="mini" type="danger" v-if="coverPlaying" @click="convertStopClick">鍋滄杞爜</el-button> + </p> + <div class="trank" > + <div v-for="(item, index) in tracks"> + <span >娴� {{index}}</span> + <div class="trankInfo" v-if="item.codec_type == 0"> + <p>鏍煎紡: {{item.codec_id_name}}</p> + <p>绫诲瀷: 瑙嗛</p> + <p>鍒嗚鲸鐜�: {{item.width}} x {{item.height}}</p> + <p>甯х巼: {{item.fps}}</p> + </div> + <div class="trankInfo" v-if="item.codec_type == 1"> + <p>鏍煎紡: {{item.codec_id_name}}</p> + <p>绫诲瀷: 闊抽</p> + <p>閲囨牱浣嶆暟: {{item.sample_bit}}</p> + <p>閲囨牱鐜�: {{item.sample_rate}}</p> + </div> + </div> + + </div> + + </el-tab-pane> </el-tabs> </div> </el-dialog> @@ -104,12 +132,12 @@ </template> <script> -import LivePlayer from '@liveqing/liveplayer' +import player from './player.vue' export default { name: 'devicePlayer', props: {}, components: { - LivePlayer + player, }, computed: { getPlayerShared: function () { @@ -131,6 +159,7 @@ }, showVideoDialog: false, ssrc: '', + streamId: '', convertKey: '', deviceId: '', channelId: '', @@ -148,20 +177,45 @@ cruisingGroup: 0, scanSpeed: 100, scanGroup: 0, + tracks: [], + coverPlaying:false, + tracksLoading: false }; }, methods: { + tabHandleClick: function(tab, event) { + console.log(tab) + var that = this; + that.tracks = []; + that.tracksLoading = true; + if (tab.name == "codec") { + this.$axios({ + method: 'get', + url: '/zlm/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtmp&app=rtp&stream='+ this.streamId + }).then(function (res) { + that.tracksLoading = false; + if (res.data.code == 0 && res.data.online) { + that.tracks = res.data.tracks; + }else{ + that.$message({ + showClose: true, + message: '鑾峰彇缂栫爜淇℃伅澶辫触,', + type: 'warning' + }); + } + }).catch(function (e) {}); + } + }, openDialog: function (tab, deviceId, channelId, param) { this.tabActiveName = tab; this.channelId = channelId; this.deviceId = deviceId; this.ssrc = ""; + this.streamId = ""; this.videoUrl = "" if (!!this.$refs.videoPlayer) { this.$refs.videoPlayer.pause(); } - - switch (tab) { case "media": this.play(param.streamInfo, param.hasAudio) @@ -180,85 +234,56 @@ console.log(val) }, play: function (streamInfo, hasAudio) { + this.hasaudio = hasAudio; - var that = this; - that.isLoging = false; - if (!!streamInfo.tracks && streamInfo.tracks.length > 0 ) { - for (let i = 0; i < streamInfo.tracks.length; i++) { - if (streamInfo.tracks[i].codec_type == 0 && streamInfo.tracks[i].codec_id_name != "CodecH264") { // 鍒ゆ柇涓篐265瑙嗛 - that.coverPlay(streamInfo, streamInfo.tracks[i].codec_id_name, ()=>{ - that.close(); - return; - }) - }else if (streamInfo.tracks[i].codec_type == 1 && streamInfo.tracks[i].codec_id_name != "CodecAAC") { - that.coverPlay(streamInfo, streamInfo.tracks[i].codec_id_name, ()=>{ - that.playFromStreamInfo(false. streamInfo) - }) - }else if (streamInfo.tracks[i].codec_type == 1 && streamInfo.tracks[i].codec_id_name == "CodecAAC") { - that.playFromStreamInfo(true, streamInfo) - }else { - that.playFromStreamInfo(false, streamInfo) - } - } - }else { - that.playFromStreamInfo(false, streamInfo) - } + this.isLoging = false; + this.videoUrl = streamInfo.ws_flv; + this.ssrc = streamInfo.ssrc; + this.streamId = streamInfo.streamId; + this.playFromStreamInfo(false, streamInfo) }, - coverPlay: function (streamInfo, codec_id_name, catchcallback) { - var that = this; - - that.$confirm(codec_id_name + ' 缂栫爜鏍煎紡涓嶆敮鎸佹挱鏀�, 鏄惁杞爜鎾斁?', '鎻愮ず', { - confirmButtonText: '纭畾', - cancelButtonText: '鍙栨秷', - type: 'warning' - }).then(() => { - that.isLoging = true; - that.$axios({ + coverPlay: function () { + var that = this; + this.coverPlaying = true; + this.$refs.videoPlayer.pause() + that.$axios({ method: 'post', - url: '/api/play/' + streamInfo.ssrc + '/convert' - }).then(function (res) { - if (res.data.code == 0) { - streamInfo.ws_flv = res.data.ws_flv; - that.convertKey = res.data.key; - setTimeout(()=>{ - that.isLoging = false; - that.playFromStreamInfo(false, streamInfo); - }, 2000) - } else { - that.isLoging = false; - that.$message({ - showClose: true, - message: '杞爜澶辫触', - type: 'error' - }); - } - }).catch(function (e) { - that.$message({ - showClose: true, - message: '鎾斁閿欒', - type: 'error' + url: '/api/play/' + that.ssrc + '/convert' + }).then(function (res) { + if (res.data.code == 0) { + that.convertKey = res.data.key; + setTimeout(()=>{ + that.isLoging = false; + that.playFromStreamInfo(false, res.data.data); + }, 2000) + } else { + that.isLoging = false; + that.coverPlaying = false; + that.$message({ + showClose: true, + message: '杞爜澶辫触', + type: 'error' + }); + } + }).catch(function (e) { + console.log(e) + that.coverPlaying = false; + that.$message({ + showClose: true, + message: '鎾斁閿欒', + type: 'error' + }); }); - }); - }).catch(function (e) { - if (catchcallback)catchcallback() + }, + convertStopClick: function() { + this.convertStop(()=>{ + this.$refs.videoPlayer.play(this.videoUrl) }); }, - playFromStreamInfo: function (realHasAudio, streamInfo) { - this.videoUrl = streamInfo.ws_flv; - this.showVideoDialog = true; - this.hasaudio = realHasAudio && this.hasaudio; - this.ssrc = streamInfo.ssrc; - console.log(this.ssrc); - }, - close: function () { - console.log('鍏抽棴瑙嗛'); - if (!this.$refs.videoPlayer){ - this.$refs.videoPlayer.pause(); - } - this.videoUrl = ''; - this.showVideoDialog = false; - if (this.convertKey != '') { - this.$axios({ + convertStop: function(callback) { + var that = this; + that.$refs.videoPlayer.pause() + this.$axios({ method: 'post', url: '/api/play/convert/stop/' + this.convertKey }).then(function (res) { @@ -267,12 +292,36 @@ }else { console.error(res.data.msg) } + if (callback )callback(); }).catch(function (e) {}); - } - this.convertKey = '' + that.coverPlaying = false; + that.convertKey = ""; + // if (callback )callback(); }, + + playFromStreamInfo: function (realHasAudio, streamInfo) { + this.showVideoDialog = true; + this.hasaudio = realHasAudio && this.hasaudio; + this.$refs.videoPlayer.play(streamInfo.ws_flv) + }, + close: function () { + console.log('鍏抽棴瑙嗛'); + if (!!this.$refs.videoPlayer){ + this.$refs.videoPlayer.pause(); + } + this.videoUrl = ''; + this.coverPlaying = false; + this.showVideoDialog = false; + if (this.convertKey != '') { + this.convertStop(); + } + this.convertKey = '' + }, + copySharedInfo: function (data) { console.log('澶嶅埗鍐呭锛�' + data); + this.coverPlaying = false; + this.tracks = [] let _this = this; this.$copyText(data).then( function (e) { @@ -602,4 +651,15 @@ .control-bottom .fa { transform: rotate(-45deg) translateY(7px); } +.trank { + width: 80%; + height: 180px; + text-align: left; + padding: 0 10%; + overflow: auto; +} +.trankInfo { + width: 80%; + padding: 0 10%; +} </style> diff --git a/web_src/src/components/gb28181/player.vue b/web_src/src/components/gb28181/player.vue new file mode 100644 index 0000000..0abf5c0 --- /dev/null +++ b/web_src/src/components/gb28181/player.vue @@ -0,0 +1,57 @@ +<template> + <div id="player"> + <div id="easyplayer"></div> + </div> +</template> + +<script> +export default { + name: 'player', + data() { + return { + easyPlayer: null + }; + }, + props: ['videoUrl', 'error', 'hasaudio'], + mounted () { + this.$nextTick(() =>{ + console.log("鍒濆鍖栨椂鐨勫湴鍧�涓�: " + this.videoUrl) + this.easyPlayer = new WasmPlayer(null, 'easyplayer', this.eventcallbacK) + this.easyPlayer.play(this.videoUrl, 1) + }) + }, + watch:{ + videoUrl(newData, oldData){ + this.easyPlayer.destroy() + this.easyPlayer = new WasmPlayer(null, 'easyplayer', this.eventcallbacK) + this.easyPlayer.play(newData, 1) + }, + immediate:true + }, + methods: { + play: function (url) { + this.easyPlayer = new WasmPlayer(null, 'easyplayer', this.eventcallbacK) + this.easyPlayer.play(url, 1) + }, + pause: function () { + this.easyPlayer.destroy(); + }, + eventcallbacK: function(type, message) { + console.log("player 浜嬩欢鍥炶皟") + console.log(type) + console.log(message) + } + }, +} +</script> + +<style> + .LodingTitle { + min-width: 70px; + } + /* 闅愯棌logo */ + /* .iconqingxiLOGO { + display: none !important; + } */ + +</style> \ No newline at end of file diff --git a/web_src/src/components/videoList.vue b/web_src/src/components/videoList.vue index ebf3ad0..ad2f701 100644 --- a/web_src/src/components/videoList.vue +++ b/web_src/src/components/videoList.vue @@ -73,12 +73,10 @@ </template> <script> - import devicePlayer from './gb28181/devicePlayer.vue' import uiHeader from './UiHeader.vue' export default { name: 'app', components: { - devicePlayer, uiHeader }, data() { diff --git a/web_src/src/router/index.js b/web_src/src/router/index.js index 73a5781..658da4e 100644 --- a/web_src/src/router/index.js +++ b/web_src/src/router/index.js @@ -35,7 +35,7 @@ path: '/channelList/:deviceId/:parentChannelId/:count/:page', name: 'channelList', component: channelList, - },, + }, { path: '/parentPlatformList/:count/:page', name: 'parentPlatformList', -- Gitblit v1.8.0