From 26bdf2e7768ee5dfc400c3970a5aa129fed49453 Mon Sep 17 00:00:00 2001
From: 648540858 <648540858@qq.com>
Date: 星期一, 20 三月 2023 15:31:43 +0800
Subject: [PATCH] Merge branch '级联' into main-dev

---
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java                                                   |    6 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java                                                            |   55 ++
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java |    3 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java                                    |   87 ++++
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java                             |   22 
 src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java                                                                 |   28 +
 web_src/src/components/dialog/devicePlayer.vue                                                                                    |   98 ++--
 doc/_sidebar.md                                                                                                                   |    1 
 src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java                                                                         |   10 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java                                            |   75 ++
 doc/README.md                                                                                                                     |    4 
 src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java                                                      |   20 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamType.java                                                            |    2 
 src/main/java/com/genersoft/iot/vmp/service/IPlayService.java                                                                     |    7 
 src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java                                                         |  156 +++++++
 doc/_content/theory/broadcast_cascade.md                                                                                          |   46 ++
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java                                                              |    6 
 src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java                                                               |    5 
 src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java                                                             |   33 +
 src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java                                                              |    2 
 src/main/resources/all-application.yml                                                                                            |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java                                                         |   77 +++
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java                                  |    1 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java                               |    6 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java                                                            |   59 +-
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java                                        |  124 ++++++
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java     |  200 ++++++++++
 src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java                                                     |    9 
 28 files changed, 970 insertions(+), 174 deletions(-)

diff --git a/doc/README.md b/doc/README.md
index 0fb5b86..2bbc184 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -52,7 +52,7 @@
     - [X] 鎶ヨ璁㈤槄
     - [X] 鐩綍璁㈤槄
 - [ ] 璇煶骞挎挱
-- [ ] 璇煶瀵硅
+- [ ] 璇煶鍠婅瘽
 
 **浣滀负涓嬬骇骞冲彴**
 - [X] 娉ㄥ唽
@@ -91,7 +91,7 @@
     - [ ] 鎶ヨ璁㈤槄
     - [X] 鐩綍璁㈤槄
 - [ ] 璇煶骞挎挱
-- [ ] 璇煶瀵硅
+- [ ] 璇煶鍠婅瘽
 
    
 
diff --git a/doc/_content/theory/broadcast_cascade.md b/doc/_content/theory/broadcast_cascade.md
new file mode 100644
index 0000000..e59b8c2
--- /dev/null
+++ b/doc/_content/theory/broadcast_cascade.md
@@ -0,0 +1,46 @@
+<!-- 鐐规挱娴佺▼ -->
+
+# 鐐规挱娴佺▼
+> 浠ヤ笅涓篧VP-PRO绾ц仈璇煶鍠婅瘽娴佺▼銆�
+
+```plantuml
+@startuml
+"涓婄骇骞冲彴"  -> "涓嬬骇骞冲彴": 1. 鍙戣捣璇煶鍠婅瘽璇锋眰
+"涓婄骇骞冲彴" <--  "涓嬬骇骞冲彴": 2. 200OK
+"涓婄骇骞冲彴" <- "涓嬬骇骞冲彴": 3. 鍥炲Result OK
+"涓婄骇骞冲彴" -->  "涓嬬骇骞冲彴": 4. 200OK
+
+"涓嬬骇骞冲彴"  -> "璁惧": 5. 鍙戣捣璇煶鍠婅瘽璇锋眰
+"涓嬬骇骞冲彴" <--  "璁惧": 6. 200OK
+"涓嬬骇骞冲彴" <- "璁惧": 7. 鍥炲Result OK
+"涓嬬骇骞冲彴" -->  "璁惧": 8. 200OK
+
+"涓嬬骇骞冲彴"  <- "璁惧": 9. invite(broadcast)
+"涓嬬骇骞冲彴"  --> "璁惧": 10. 100 trying
+"涓嬬骇骞冲彴"  --> "璁惧": 11. 200OK SDP
+"涓嬬骇骞冲彴"  <-- "璁惧": 12. ack
+
+"涓婄骇骞冲彴"  <- "涓嬬骇骞冲彴": 13. invite(broadcast)
+"涓婄骇骞冲彴"  --> "涓嬬骇骞冲彴": 14. 100 trying
+"涓婄骇骞冲彴"  --> "涓嬬骇骞冲彴": 15. 200OK SDP
+"涓婄骇骞冲彴"  <-- "涓嬬骇骞冲彴": 16. ack
+
+"涓婄骇骞冲彴"  -> "涓嬬骇骞冲彴": 17. 鎺ㄩ�丷TP
+"涓嬬骇骞冲彴"  -> "璁惧": 18. 鎺ㄩ�丷TP
+
+@enduml
+```
+
+
+##  娉ㄥ唽娴佺▼鎻忚堪濡備笅:
+1. 鐢ㄦ埛浠庣綉椤垫垨璋冪敤鎺ュ彛鍙戣捣鐐规挱璇锋眰;
+2. WVP-PRO鍚戞憚鍍忔満鍙戦�両nvite娑堟伅,娑堟伅澶村煙涓惡甯� Subject瀛楁,琛ㄦ槑鐐规挱鐨勮棰戞簮ID銆佸彂閫佹柟濯掍綋娴佸簭鍒楀彿銆乑LMediaKit鎺ユ敹娴佷娇鐢ㄧ殑IP銆佺鍙e彿銆�
+   鎺ユ敹绔獟浣撴祦搴忓垪鍙风瓑鍙傛暟,SDP娑堟伅浣撲腑 s瀛楁涓衡�淧lay鈥濅唬琛ㄥ疄鏃剁偣鎾紝y瀛楁鎻忚堪SSRC鍊�,f瀛楁鎻忚堪濯掍綋鍙傛暟銆�
+3. 鎽勫儚鏈哄悜WVP-PRO鍥炲200OK锛屾秷鎭綋涓弿杩颁簡濯掍綋娴佸彂閫佽�呭彂閫佸獟浣撴祦鐨処P銆佺鍙c�佸獟浣撴牸寮忋�丼SRC瀛楁绛夊唴瀹广��
+4. WVP-PRO鍚戣澶囧洖澶岮ck锛� 浼氳瘽寤虹珛鎴愬姛銆�
+5. 璁惧鍚慫LMediaKit鍙戦�佸疄鏃舵祦銆�
+6. ZLMediaKit鍚慦VP-PRO鍙戦�佹祦鏀瑰彉浜嬩欢銆�
+7. WVP-PRO鍚慦EB鐢ㄦ埛鍥炲鎾斁鍦板潃銆�
+8. ZLMediaKit鍚慦VP鍙戦�佹祦鏃犱汉瑙傜湅浜嬩欢銆�
+9. WVP-PRO鍚戣澶囧洖澶岯ye锛� 缁撴潫浼氳瘽銆�
+10. 璁惧鍥炲200OK锛屼細璇濈粨鏉熸垚鍔熴��
diff --git a/doc/_sidebar.md b/doc/_sidebar.md
index 3b10bae..05101c1 100644
--- a/doc/_sidebar.md
+++ b/doc/_sidebar.md
@@ -19,6 +19,7 @@
   * [鏍戝舰缁撴瀯](_content/theory/channel_tree.md)
   * [娉ㄥ唽娴佺▼](_content/theory/register.md)
   * [鐐规挱娴佺▼](_content/theory/play.md)
+  * [绾ц仈璇煶鍠婅瘽娴佺▼](_content/theory/broadcast_cascade.md)
 * **蹇呭鎶�宸�**
   * [鎶撳寘](_content/skill/tcpdump.md)
 
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 251edf1..811f7e9 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
@@ -55,6 +55,8 @@
 
     private String thirdPartyGBIdReg = "[\\s\\S]*";
 
+    private String broadcastForPlatform = "UDP";
+
     private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
 
     public Boolean getSavePositionHistory() {
@@ -205,6 +207,14 @@
         this.syncChannelOnDeviceOnline = syncChannelOnDeviceOnline;
     }
 
+    public String getBroadcastForPlatform() {
+        return broadcastForPlatform;
+    }
+
+    public void setBroadcastForPlatform(String broadcastForPlatform) {
+        this.broadcastForPlatform = broadcastForPlatform;
+    }
+
     public Boolean getPushStreamAfterAck() {
         return pushStreamAfterAck;
     }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java
index a57f7a3..4fff352 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java
@@ -2,6 +2,7 @@
 
 
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
 import gov.nist.javax.sip.message.SIPResponse;
 
 /**
@@ -11,18 +12,24 @@
 public class AudioBroadcastCatch {
 
 
-    public AudioBroadcastCatch(String deviceId,
-                               String channelId,
-                               AudioBroadcastCatchStatus status,
-                               MediaServerItem mediaServerItem,
-                               String app,
-                               String stream) {
+    public AudioBroadcastCatch(
+            String deviceId,
+            String channelId,
+            MediaServerItem mediaServerItem,
+            String app,
+            String stream,
+            AudioBroadcastEvent event,
+            AudioBroadcastCatchStatus status,
+            boolean isFromPlatform
+    ) {
         this.deviceId = deviceId;
         this.channelId = channelId;
         this.status = status;
-        this.mediaServerItem = mediaServerItem;
+        this.event = event;
+        this.isFromPlatform = isFromPlatform;
         this.app = app;
         this.stream = stream;
+        this.mediaServerItem = mediaServerItem;
     }
 
     public AudioBroadcastCatch() {
@@ -39,19 +46,24 @@
     private String channelId;
 
     /**
-     * 浣跨敤鐨勬祦濯掍綋
+     * 娴佸獟浣撲俊鎭�
      */
     private MediaServerItem mediaServerItem;
 
     /**
-     * 寰呮帹閫佺粰璁惧鐨勬祦搴旂敤鍚�
+     * 鍏宠仈鐨勬祦APP
      */
     private String app;
 
     /**
-     * 寰呮帹閫佺粰璁惧鐨勬祦ID
+     * 鍏宠仈鐨勬祦STREAM
      */
     private String stream;
+
+    /**
+     *  鏄惁鏄骇鑱旇闊冲枈璇�
+     */
+    private boolean isFromPlatform;
 
     /**
      * 璇煶骞挎挱鐘舵��
@@ -62,6 +74,11 @@
      * 璇锋眰淇℃伅
      */
     private SipTransactionInfo sipTransactionInfo;
+
+    /**
+     * 璇锋眰缁撴灉鍥炶皟
+     */
+    private AudioBroadcastEvent event;
 
 
     public String getDeviceId() {
@@ -123,4 +140,44 @@
     public void setMediaServerItem(MediaServerItem mediaServerItem) {
         this.mediaServerItem = mediaServerItem;
     }
+
+    public AudioBroadcastEvent getEvent() {
+        return event;
+    }
+
+    public void setEvent(AudioBroadcastEvent event) {
+        this.event = event;
+    }
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getStream() {
+        return stream;
+    }
+
+    public void setStream(String stream) {
+        this.stream = stream;
+    }
+
+    public boolean isFromPlatform() {
+        return isFromPlatform;
+    }
+
+    public void setFromPlatform(boolean fromPlatform) {
+        isFromPlatform = fromPlatform;
+    }
+
+    public MediaServerItem getMediaServerItem() {
+        return mediaServerItem;
+    }
+
+    public void setMediaServerItem(MediaServerItem mediaServerItem) {
+        this.mediaServerItem = mediaServerItem;
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamType.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamType.java
index a3098fb..dde7639 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamType.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamType.java
@@ -2,7 +2,7 @@
 
 public enum InviteStreamType {
 
-    PLAY,PLAYBACK,PUSH,PROXY,CLOUD_RECORD_PUSH,CLOUD_RECORD_PROXY,TALK
+    PLAY,PLAYBACK,PUSH,PROXY,CLOUD_RECORD_PUSH,CLOUD_RECORD_PROXY,BROADCAST,TALK
 
 
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java
index b056cc7..262ac55 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java
@@ -66,7 +66,7 @@
      * 璁惧绔彛
      */
     @Schema(description = "璁惧绔彛")
-    private String devicePort;
+    private int devicePort;
 
     /**
      * SIP璁よ瘉鐢ㄦ埛鍚�(榛樿浣跨敤璁惧鍥芥爣缂栧彿)
@@ -261,11 +261,11 @@
         this.deviceIp = deviceIp;
     }
 
-    public String getDevicePort() {
+    public int getDevicePort() {
         return devicePort;
     }
 
-    public void setDevicePort(String devicePort) {
+    public void setDevicePort(int devicePort) {
         this.devicePort = devicePort;
     }
 
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
index 7564390..5cc9cb9 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
@@ -86,6 +86,11 @@
         public String callId;
         public EventObject event;
 
+        public EventResult(int statusCode, String msg) {
+            this.statusCode = statusCode;
+            this.msg = msg;
+        }
+
         public EventResult(EventObject event) {
             this.event = event;
             if (event instanceof ResponseEvent) {
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
index 0425356..9d5425b 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
@@ -1,8 +1,12 @@
 package com.genersoft.iot.vmp.gb28181.transmit.cmd;
 
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
+import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.SipException;
@@ -14,42 +18,56 @@
 
     /**
      * 鍚戜笂绾у钩鍙版敞鍐�
+     *
      * @param parentPlatform
      * @return
      */
-    void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
-    void register(ParentPlatform parentPlatform, String callId, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException;
+    void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent)
+            throws InvalidArgumentException, ParseException, SipException;
+
+    void register(ParentPlatform parentPlatform, String callId, WWWAuthenticateHeader www,
+                  SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister)
+            throws SipException, InvalidArgumentException, ParseException;
 
     /**
      * 鍚戜笂绾у钩鍙版敞閿�
+     *
      * @param parentPlatform
      * @return
      */
-    void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
+    void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent)
+            throws InvalidArgumentException, ParseException, SipException;
 
 
     /**
      * 鍚戜笂绾у钩鍙戦�佸績璺充俊鎭�
+     *
      * @param parentPlatform
      * @return callId(浣滀负鎺ュ彈鍥炲鐨勫垽瀹�)
      */
-    String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException;
+    String keepalive(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent)
+            throws SipException, InvalidArgumentException, ParseException;
 
 
     /**
      * 鍚戜笂绾у洖澶嶉�氶亾淇℃伅
-     * @param channel 閫氶亾淇℃伅
+     *
+     * @param channel        閫氶亾淇℃伅
      * @param parentPlatform 骞冲彴淇℃伅
      * @param sn
      * @param fromTag
      * @param size
      * @return
      */
-    void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException;
-    void catalogQuery(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag) throws InvalidArgumentException, ParseException, SipException;
+    void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size)
+            throws SipException, InvalidArgumentException, ParseException;
+
+    void catalogQuery(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag)
+            throws InvalidArgumentException, ParseException, SipException;
 
     /**
      * 鍚戜笂绾у洖澶岲eviceInfo鏌ヨ淇℃伅
+     *
      * @param parentPlatform 骞冲彴淇℃伅
      * @param sn SN
      * @param fromTag FROM澶寸殑tag淇℃伅
@@ -59,32 +77,37 @@
 
     /**
      * 鍚戜笂绾у洖澶岲eviceStatus鏌ヨ淇℃伅
+     *
      * @param parentPlatform 骞冲彴淇℃伅
      * @param sn
      * @param fromTag
      * @return
      */
-    void deviceStatusResponse(ParentPlatform parentPlatform,String channelId, String sn, String fromTag,int status) throws SipException, InvalidArgumentException, ParseException;
+    void deviceStatusResponse(ParentPlatform parentPlatform,String channelId, String sn, String fromTag,int status) throws SipException, InvalidArgumentException, ParseException ;
 
     /**
      * 鍚戜笂绾у洖澶嶇Щ鍔ㄤ綅缃闃呮秷鎭�
+     *
      * @param parentPlatform 骞冲彴淇℃伅
-     * @param gpsMsgInfo GPS淇℃伅
-     * @param subscribeInfo 璁㈤槄鐩稿叧鐨勪俊鎭�
+     * @param gpsMsgInfo     GPS淇℃伅
+     * @param subscribeInfo  璁㈤槄鐩稿叧鐨勪俊鎭�
      * @return
      */
-    void sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException;
+    void sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo)
+            throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException;
 
     /**
      * 鍚戜笂绾у洖澶嶆姤璀︽秷鎭�
+     *
      * @param parentPlatform 骞冲彴淇℃伅
-     * @param deviceAlarm 鎶ヨ淇℃伅淇℃伅
+     * @param deviceAlarm    鎶ヨ淇℃伅淇℃伅
      * @return
      */
     void sendAlarmMessage(ParentPlatform parentPlatform, DeviceAlarm deviceAlarm) throws SipException, InvalidArgumentException, ParseException;
 
     /**
      * 鍥炲catalog浜嬩欢-澧炲姞/鏇存柊
+     *
      * @param parentPlatform
      * @param deviceChannels
      */
@@ -92,22 +115,28 @@
 
     /**
      * 鍥炲catalog浜嬩欢-鍒犻櫎
+     *
      * @param parentPlatform
      * @param deviceChannels
      */
-    void sendNotifyForCatalogOther(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException;
+    void sendNotifyForCatalogOther(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels,
+                                   SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException,
+            ParseException, NoSuchFieldException, SipException, IllegalAccessException;
 
     /**
      * 鍥炲recordInfo
-     * @param deviceChannel 閫氶亾淇℃伅
+     *
+     * @param deviceChannel  閫氶亾淇℃伅
      * @param parentPlatform 骞冲彴淇℃伅
-     * @param fromTag fromTag
-     * @param recordInfo 褰曞儚淇℃伅
+     * @param fromTag        fromTag
+     * @param recordInfo     褰曞儚淇℃伅
      */
-    void recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo) throws SipException, InvalidArgumentException, ParseException;
+    void recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo)
+            throws SipException, InvalidArgumentException, ParseException;
 
     /**
      * 褰曞儚鎾斁鎺ㄩ�佸畬鎴愭椂鍙戦�丮ediaStatus娑堟伅
+     *
      * @param platform
      * @param sendRtpItem
      * @return
@@ -116,9 +145,19 @@
 
     /**
      * 鍚戝彂璧风偣鎾殑涓婄骇鍥炲bye
+     *
      * @param platform 骞冲彴淇℃伅
-     * @param callId  callId
+     * @param callId   callId
      */
     void streamByeCmd(ParentPlatform platform, String callId) throws SipException, InvalidArgumentException, ParseException;
+
     void streamByeCmd(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException;
+
+    void streamByeCmd(ParentPlatform platform, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException;
+
+    void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem,
+                            SSRCInfo ssrcInfo, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent,
+                            SipSubscribe.Event errorEvent) throws ParseException, SipException, InvalidArgumentException;
+
+    void broadcastResultCmd(ParentPlatform platform, DeviceChannel deviceChannel, String sn, boolean result, SipSubscribe.Event errorEvent,  SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
index 0fe11c0..b77a0a4 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
@@ -4,6 +4,7 @@
 import com.genersoft.iot.vmp.gb28181.SipLayer;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
+import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
 import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -14,7 +15,8 @@
 import org.springframework.stereotype.Component;
 import org.springframework.util.DigestUtils;
 
-import javax.sip.*;
+import javax.sip.InvalidArgumentException;
+import javax.sip.PeerUnavailableException;
 import javax.sip.address.Address;
 import javax.sip.address.SipURI;
 import javax.sip.header.*;
@@ -22,7 +24,6 @@
 import javax.validation.constraints.NotNull;
 import java.text.ParseException;
 import java.util.ArrayList;
-import java.util.List;
 import java.util.UUID;
 
 /**
@@ -175,7 +176,7 @@
 		SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress);
 		// via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
-		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()),
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), parentPlatform.getDevicePort(),
 				parentPlatform.getTransport(), viaTag);
 		viaHeader.setRPort();
 		viaHeaders.add(viaHeader);
@@ -212,7 +213,7 @@
 		SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort());
 		// via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
-		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()),
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), parentPlatform.getDevicePort(),
 				parentPlatform.getTransport(), SipUtils.getNewViaTag());
 		viaHeader.setRPort();
 		viaHeaders.add(viaHeader);
@@ -272,7 +273,7 @@
 		SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+ ":" + platform.getServerPort());
 		// via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
-		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(platform.getDeviceIp(), Integer.parseInt(platform.getDevicePort()),
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(platform.getDeviceIp(), platform.getDevicePort(),
 				platform.getTransport(), SipUtils.getNewViaTag());
 		viaHeader.setRPort();
 		viaHeaders.add(viaHeader);
@@ -308,4 +309,80 @@
 
 		return request;
 	}
+
+    public Request createInviteRequest(ParentPlatform platform, String channelId, String content, String viaTag, String fromTag, String ssrc, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException {
+		Request request = null;
+		//璇锋眰琛�
+		String platformHostAddress = platform.getServerIP() + ":" + platform.getServerPort();
+		String localHostAddress = sipLayer.getLocalIp(platform.getDeviceIp())+":"+ platform.getDevicePort();
+		SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, platformHostAddress);
+		//via
+		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getDevicePort(), platform.getTransport(), viaTag);
+		viaHeader.setRPort();
+		viaHeaders.add(viaHeader);
+
+		//from
+		SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(platform.getDeviceGBId(), sipConfig.getDomain());
+		Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag); //蹇呴』瑕佹湁鏍囪锛屽惁鍒欐棤娉曞垱寤轰細璇濓紝鏃犳硶鍥炲簲ack
+		//to
+		SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, platformHostAddress);
+		Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null);
+
+		//Forwards
+		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
+
+		//ceq
+		CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE);
+		request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
+
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
+
+		Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(),localHostAddress));
+		request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
+		// Subject
+		SubjectHeader subjectHeader = sipLayer.getSipFactory().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
+		request.addHeader(subjectHeader);
+		ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
+		request.setContent(content, contentTypeHeader);
+		return request;
+    }
+
+	public Request createByteRequest(ParentPlatform platform, String channelId, SipTransactionInfo transactionInfo) throws PeerUnavailableException, ParseException, InvalidArgumentException {
+		String deviceHostAddress = platform.getDeviceIp() + ":" + platform.getDevicePort();
+		Request request = null;
+		SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, deviceHostAddress);
+
+		// via
+		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getDevicePort(), platform.getTransport(), SipUtils.getNewViaTag());
+		viaHeaders.add(viaHeader);
+		//from
+		SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
+		Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.isAsSender()?transactionInfo.getFromTag():transactionInfo.getToTag());
+		//to
+		SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, deviceHostAddress);
+		Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,transactionInfo.isAsSender()?transactionInfo.getToTag():transactionInfo.getFromTag());
+
+		//Forwards
+		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
+
+		//ceq
+		CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
+		CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
+		request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
+
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
+
+		Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(platform.getDeviceIp())+":"+ platform.getDevicePort()));
+		request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
+
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
+
+		return request;
+	}
 }
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 3b6565a..4855114 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
@@ -591,12 +591,12 @@
             return;
         }
         if (!mediaServerItem.isRtpEnable()) {
-            // 鍗曠鍙f殏涓嶆敮鎸佽闊冲璁�
-            logger.info("[璇煶瀵硅] 鍗曠鍙f殏涓嶆敮鎸佹鎿嶄綔");
+            // 鍗曠鍙f殏涓嶆敮鎸佽闊冲枈璇�
+            logger.info("[璇煶鍠婅瘽] 鍗曠鍙f殏涓嶆敮鎸佹鎿嶄綔");
             return;
         }
 
-        logger.info("[璇煶瀵硅] {} 鍒嗛厤鐨刏LM涓�: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), sendRtpItem.getPort());
+        logger.info("[璇煶鍠婅瘽] {} 鍒嗛厤鐨刏LM涓�: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), sendRtpItem.getPort());
         HookSubscribeForStreamChange hookSubscribeForStreamChange = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
         subscribe.addSubscribe(hookSubscribeForStreamChange, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
             if (event != null) {
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
index 4fef72d..9809a9f 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
@@ -1,23 +1,32 @@
 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
 
 import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
 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.SipLayer;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderPlarformProvider;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
+import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
+import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
+import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import gov.nist.javax.sip.message.MessageFactoryImpl;
 import gov.nist.javax.sip.message.SIPRequest;
+import gov.nist.javax.sip.message.SIPResponse;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -27,6 +36,7 @@
 import org.springframework.util.ObjectUtils;
 
 import javax.sip.InvalidArgumentException;
+import javax.sip.ResponseEvent;
 import javax.sip.SipException;
 import javax.sip.header.CallIdHeader;
 import javax.sip.header.WWWAuthenticateHeader;
@@ -61,6 +71,16 @@
 
     @Autowired
     private SIPSender sipSender;
+
+    @Autowired
+    private ZlmHttpHookSubscribe subscribe;
+
+    @Autowired
+    private UserSetting userSetting;
+
+
+    @Autowired
+    private VideoStreamSessionManager streamSession;
 
     @Autowired
     private DynamicTask dynamicTask;
@@ -335,6 +355,7 @@
         Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceInfoXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
         sipSender.transmitRequest(parentPlatform.getDeviceIp(), request);
     }
+
 
     /**
      * 鍚戜笂绾у洖澶岲eviceStatus鏌ヨ淇℃伅
@@ -702,4 +723,107 @@
         }
         sipSender.transmitRequest(platform.getDeviceIp(),byeRequest);
     }
+
+    @Override
+    public void streamByeCmd(ParentPlatform platform, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException {
+        SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(platform.getServerGBId(), channelId, callId, stream);
+        if (ssrcTransaction == null) {
+            throw new SsrcTransactionNotFoundException(platform.getServerGBId(), channelId, callId, stream);
+        }
+
+        mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
+        mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream());
+        streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
+
+        Request byteRequest = headerProviderPlatformProvider.createByteRequest(platform, channelId, ssrcTransaction.getSipTransactionInfo());
+        sipSender.transmitRequest(sipLayer.getLocalIp(platform.getDeviceIp()), byteRequest, null, okEvent);
+    }
+
+    @Override
+    public void broadcastResultCmd(ParentPlatform platform, DeviceChannel deviceChannel, String sn, boolean result, SipSubscribe.Event errorEvent,  SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
+        if (platform == null || deviceChannel == null) {
+            return;
+        }
+        String characterSet = platform.getCharacterSet();
+        StringBuffer mediaStatusXml = new StringBuffer(200);
+        mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
+        mediaStatusXml.append("<Response>\r\n");
+        mediaStatusXml.append("<CmdType>Broadcast</CmdType>\r\n");
+        mediaStatusXml.append("<SN>" + sn + "</SN>\r\n");
+        mediaStatusXml.append("<DeviceID>" + deviceChannel.getChannelId() + "</DeviceID>\r\n");
+        mediaStatusXml.append("<Result>" + (result?"OK":"ERROR") + "</Result>\r\n");
+        mediaStatusXml.append("</Response>\r\n");
+
+        CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(platform.getDeviceIp(), platform.getTransport());
+
+        SIPRequest messageRequest = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(platform, mediaStatusXml.toString(),
+               SipUtils.getNewFromTag(), SipUtils.getNewViaTag(), callIdHeader);
+
+        sipSender.transmitRequest(platform.getDeviceIp(),messageRequest, errorEvent, okEvent);
+    }
+
+    @Override
+    public void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem,
+                                   SSRCInfo ssrcInfo, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent,
+                                   SipSubscribe.Event errorEvent) throws ParseException, SipException, InvalidArgumentException {
+        String stream = ssrcInfo.getStream();
+
+        if (platform == null) {
+            return;
+        }
+
+        logger.info("{} 鍒嗛厤鐨刏LM涓�: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
+        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
+        subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
+            if (event != null) {
+                event.response(mediaServerItemInUse, json);
+                subscribe.removeSubscribe(hookSubscribe);
+            }
+        });
+        String sdpIp = mediaServerItem.getSdpIp();
+
+        StringBuffer content = new StringBuffer(200);
+        content.append("v=0\r\n");
+        content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n");
+        content.append("s=Play\r\n");
+        content.append("c=IN IP4 " + sdpIp + "\r\n");
+        content.append("t=0 0\r\n");
+
+        if ("TCP-PASSIVE".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
+            content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 8 96\r\n");
+        } else if ("TCP-ACTIVE".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
+            content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 8 96\r\n");
+        } else if ("UDP".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
+            content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 8 96\r\n");
+        }
+
+        content.append("a=recvonly\r\n");
+        content.append("a=rtpmap:8 PCMA/8000\r\n");
+        content.append("a=rtpmap:96 PS/90000\r\n");
+        if ("TCP-PASSIVE".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
+            content.append("a=setup:passive\r\n");
+            content.append("a=connection:new\r\n");
+        }else if ("TCP-ACTIVE".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
+            content.append("a=setup:active\r\n");
+            content.append("a=connection:new\r\n");
+        }
+
+        content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
+        CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getTransport());
+
+        Request request = headerProviderPlatformProvider.createInviteRequest(platform, channelId,
+                content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(),  ssrcInfo.getSsrc(),
+                callIdHeader);
+        sipSender.transmitRequest(sipLayer.getLocalIp(platform.getDeviceIp()), request, (e -> {
+            streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream());
+            mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
+            errorEvent.response(e);
+        }), e -> {
+            // 杩欓噷涓轰緥閬垮厤涓�涓�氶亾鐨勭偣鎾彧鏈変竴涓猚allID杩欎釜鍙傛暟浣跨敤涓�涓浐瀹氬��
+            ResponseEvent responseEvent = (ResponseEvent) e.event;
+            SIPResponse response = (SIPResponse) responseEvent.getResponse();
+            streamSession.put(platform.getServerGBId(), channelId, callIdHeader.getCallId(),  stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.play);
+            okEvent.response(e);
+        });
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
index b00c83b..12bddb1 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
@@ -156,7 +156,6 @@
 				}
 			}
 		}
-
 	}
 
 }
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 c620354..c89279a 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
@@ -202,7 +202,7 @@
 
                 MediaServerItem mediaServerItem = null;
                 StreamPushItem streamPushItem = null;
-                StreamProxyItem proxyByAppAndStream =null;
+                StreamProxyItem proxyByAppAndStream = null;
                 // 涓嶆槸閫氶亾鍙兘鏄洿鎾祦
                 if (channel != null && gbStream == null) {
                     // 閫氶亾瀛樺湪锛屽彂100锛孴RYING
@@ -1018,7 +1018,7 @@
 
 
                 CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
-                sendRtpItem.setPlayType(InviteStreamType.TALK);
+                sendRtpItem.setPlayType(InviteStreamType.BROADCAST);
                 sendRtpItem.setCallId(callIdHeader.getCallId());
                 sendRtpItem.setPlatformId(requesterId);
                 sendRtpItem.setStatus(1);
@@ -1113,7 +1113,7 @@
             }
 
         } catch (SipException | InvalidArgumentException | ParseException | SdpParseException e) {
-            logger.error("[鍛戒护鍙戦�佸け璐 璇煶瀵硅 鍥炲200OK锛圫DP锛�: {}", e.getMessage());
+            logger.error("[鍛戒护鍙戦�佸け璐 璇煶鍠婅瘽 鍥炲200OK锛圫DP锛�: {}", e.getMessage());
         }
         return sipResponse;
     }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java
new file mode 100644
index 0000000..240f24f
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java
@@ -0,0 +1,200 @@
+package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
+import com.genersoft.iot.vmp.gb28181.bean.*;
+import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
+import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
+import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.IDeviceService;
+import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.service.IPlatformService;
+import com.genersoft.iot.vmp.service.IPlayService;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import gov.nist.javax.sip.message.SIPRequest;
+import org.dom4j.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+/**
+ * 鐘舵�佷俊鎭�(蹇冭烦)鎶ラ��
+ */
+@Component
+public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
+
+    private Logger logger = LoggerFactory.getLogger(BroadcastNotifyMessageHandler.class);
+    private final static String cmdType = "Broadcast";
+
+    @Autowired
+    private NotifyMessageHandler notifyMessageHandler;
+
+    @Autowired
+    private IVideoManagerStorage storage;
+
+    @Autowired
+    private ISIPCommanderForPlatform commanderForPlatform;
+
+    @Autowired
+    private IMediaServerService mediaServerService;
+
+    @Autowired
+    private IPlayService playService;
+
+    @Autowired
+    private IDeviceService deviceService;
+
+    @Autowired
+    private IPlatformService platformService;
+
+    @Autowired
+    private AudioBroadcastManager audioBroadcastManager;
+
+    @Autowired
+    private ZLMRTPServerFactory zlmrtpServerFactory;
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        notifyMessageHandler.addHandler(cmdType, this);
+    }
+
+    @Override
+    public void handForDevice(RequestEvent evt, Device device, Element element) {
+
+    }
+
+    @Override
+    public void handForPlatform(RequestEvent evt, ParentPlatform platform, Element rootElement) {
+        // 鏉ヨ嚜涓婄骇骞冲彴鐨勮闊冲枈璇濊姹�
+        SIPRequest request = (SIPRequest) evt.getRequest();
+        try {
+            Element snElement = rootElement.element("SN");
+            if (snElement == null) {
+                responseAck(request, Response.BAD_REQUEST, "sn must not null");
+                return;
+            }
+            String sn = snElement.getText();
+            Element targetIDElement = rootElement.element("TargetID");
+            if (targetIDElement == null) {
+                responseAck(request, Response.BAD_REQUEST, "TargetID must not null");
+                return;
+            }
+            String targetId = targetIDElement.getText();
+
+
+            logger.info("[鍥芥爣绾ц仈 璇煶鍠婅瘽] platform: {}, channel: {}", platform.getServerGBId(), targetId);
+
+            DeviceChannel deviceChannel = storage.queryChannelInParentPlatform(platform.getServerGBId(), targetId);
+            if (deviceChannel == null) {
+                responseAck(request, Response.NOT_FOUND, "TargetID not found");
+                return;
+            }
+            // 鍚戜笅绾у彂閫佽闊崇殑鍠婅瘽璇锋眰
+            Device device = deviceService.getDevice(deviceChannel.getDeviceId());
+            if (device == null) {
+                responseAck(request, Response.NOT_FOUND, "device not found");
+                return;
+            }
+            responseAck(request, Response.OK);
+
+            // 鏌ョ湅璇煶閫氶亾鏄惁宸茬粡寤虹珛骞朵笖宸茬粡鍦ㄤ娇鐢�
+            if (playService.audioBroadcastInUse(device, targetId)) {
+                commanderForPlatform.broadcastResultCmd(platform, deviceChannel, sn, false,null, null);
+                return;
+            }
+
+            MediaServerItem mediaServerForMinimumLoad = mediaServerService.getMediaServerForMinimumLoad(null);
+            commanderForPlatform.broadcastResultCmd(platform, deviceChannel, sn, true,  eventResult->{
+                logger.info("[鍥芥爣绾ц仈] 璇煶鍠婅瘽 鍥炲澶辫触 platform锛� {}锛� 閿欒锛歿}/{}", platform.getServerGBId(), eventResult.statusCode, eventResult.msg);
+            }, eventResult->{
+                // 娑堟伅鍙戦�佹垚鍔燂紝 鍚戜笂绾у彂閫乮nvite锛岃幏鍙栨帹娴�
+                try {
+                    platformService.broadcastInvite(platform, deviceChannel.getChannelId(), mediaServerForMinimumLoad,  (mediaServerItem, response)->{
+                        // 涓婄骇骞冲彴鎺ㄦ祦鎴愬姛
+                        String app = response.getString("app");
+                        String stream = response.getString("stream");
+                        AudioBroadcastCatch broadcastCatch = audioBroadcastManager.get(device.getDeviceId(), targetId);
+                        if (broadcastCatch != null ) {
+                            if (playService.audioBroadcastInUse(device, targetId)) {
+                                logger.info("[鍥芥爣绾ц仈] 璇煶鍠婅瘽 璁惧姝f鍦ㄤ娇鐢ㄤ腑 platform锛� {}锛� channel: {}",
+                                        platform.getServerGBId(), deviceChannel.getChannelId());
+                                //  鏌ョ湅璇煶閫氶亾宸茬粡寤虹珛涓斿凡缁忓崰鐢� 鍥炲BYE
+                                try {
+                                    platformService.stopBroadcast(platform, deviceChannel.getChannelId(), stream);
+                                } catch (InvalidArgumentException | ParseException | SsrcTransactionNotFoundException |
+                                         SipException e) {
+                                    logger.info("[娑堟伅鍙戦�佸け璐 鍥芥爣绾ц仈 璇煶鍠婅瘽 platform锛� {}锛� channel: {}", platform.getServerGBId(), deviceChannel.getChannelId());
+                                }
+                            }else {
+                                // 鏌ョ湅璇煶閫氶亾宸茬粡寤虹珛浣嗘槸鏈崰鐢�
+                                broadcastCatch.setApp(app);
+                                broadcastCatch.setStream(stream);
+                                broadcastCatch.setMediaServerItem(mediaServerItem);
+                                audioBroadcastManager.update(broadcastCatch);
+                                // 鎺ㄦ祦鍒拌澶�
+                                SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, targetId, stream, null);
+                                if (sendRtpItem == null) {
+                                    logger.warn("[鍥芥爣绾ц仈] 璇煶鍠婅瘽 寮傚父锛屾湭鎵惧埌鍙戞祦淇℃伅锛� channelId: {}, stream: {}", targetId, stream);
+                                    logger.info("[鍥芥爣绾ц仈] 璇煶鍠婅瘽 閲嶆柊寮�濮嬶紝channelId: {}, stream: {}", targetId, stream);
+                                    try {
+                                        playService.audioBroadcastCmd(device, targetId, mediaServerItem, app, stream, 60, true, msg -> {
+                                            logger.info("[璇煶鍠婅瘽] 閫氶亾寤虹珛鎴愬姛, device: {}, channel: {}", device.getDeviceId(), targetId);
+                                        });
+                                    } catch (SipException | InvalidArgumentException | ParseException e) {
+                                        logger.info("[娑堟伅鍙戦�佸け璐 鍥芥爣绾ц仈 璇煶鍠婅瘽 platform锛� {}", platform.getServerGBId());
+                                    }
+                                }else {
+                                    // 鍙戞祦
+                                    JSONObject jsonObject = zlmrtpServerFactory.startSendRtp(mediaServerItem, sendRtpItem);
+                                    if (jsonObject != null && jsonObject.getInteger("code") == 0 ) {
+                                        logger.info("[璇煶鍠婅瘽] 鑷姩鎺ㄦ祦鎴愬姛, device: {}, channel: {}", device.getDeviceId(), targetId);
+                                    }else {
+                                        logger.info("[璇煶鍠婅瘽] 鎺ㄦ祦澶辫触, 缁撴灉锛� {}", jsonObject);
+                                    }
+                                }
+                            }
+                        }else {
+                            try {
+                                playService.audioBroadcastCmd(device, targetId, mediaServerItem, app, stream, 60, true, msg -> {
+                                    logger.info("[璇煶鍠婅瘽] 閫氶亾寤虹珛鎴愬姛, device: {}, channel: {}", device.getDeviceId(), targetId);
+                                });
+                            } catch (SipException | InvalidArgumentException | ParseException e) {
+                                logger.info("[娑堟伅鍙戦�佸け璐 鍥芥爣绾ц仈 璇煶鍠婅瘽 platform锛� {}", platform.getServerGBId());
+                            }
+                        }
+
+                    }, eventResultForBroadcastInvite -> {
+                        // 鏀跺埌閿欒
+                        logger.info("[鍥芥爣绾ц仈-璇煶鍠婅瘽] 涓庝笅绾ч�氶亾寤虹珛澶辫触 device: {}, channel: {}锛� 閿欒锛歿}/{}", device.getDeviceId(),
+                                targetId, eventResultForBroadcastInvite.statusCode, eventResultForBroadcastInvite.msg);
+                    }, (code, msg)->{
+                        // 瓒呮椂
+                        logger.info("[鍥芥爣绾ц仈-璇煶鍠婅瘽] 涓庝笅绾ч�氶亾寤虹珛瓒呮椂 device: {}, channel: {}锛� 閿欒锛歿}/{}", device.getDeviceId(),
+                                targetId, code, msg);
+                    });
+                } catch (SipException | InvalidArgumentException | ParseException e) {
+                    logger.info("[娑堟伅鍙戦�佸け璐 鍥芥爣绾ц仈 璇煶鍠婅瘽 invite娑堟伅 platform锛� {}", platform.getServerGBId());
+                }
+            });
+        } catch (SipException | InvalidArgumentException | ParseException e) {
+            logger.info("[娑堟伅鍙戦�佸け璐 鍥芥爣绾ц仈 璇煶鍠婅瘽 platform锛� {}", platform.getServerGBId());
+        }
+
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java
index cf5c34b..4b6bbbc 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java
@@ -7,6 +7,7 @@
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
@@ -53,9 +54,9 @@
     @Override
     public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
 
-        String channelId = getText(rootElement, "DeviceID");
         SIPRequest request = (SIPRequest) evt.getRequest();
         try {
+            String channelId = getText(rootElement, "DeviceID");
             if (!audioBroadcastManager.exit(device.getDeviceId(), channelId)) {
                 // 鍥炲410
                 responseAck((SIPRequest) evt.getRequest(), Response.GONE);
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java
index b5a9ee7..7feac17 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java
@@ -1,39 +1,24 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
 
-import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.gb28181.SipLayer;
-import com.genersoft.iot.vmp.gb28181.bean.Device;
-import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
-import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
 import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
-import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
-import com.genersoft.iot.vmp.service.IDeviceService;
-import com.genersoft.iot.vmp.utils.GitUtil;
 import gov.nist.javax.sip.ResponseEventExt;
-import gov.nist.javax.sip.SipProviderImpl;
 import gov.nist.javax.sip.message.SIPResponse;
-import gov.nist.javax.sip.stack.SIPClientTransaction;
-import gov.nist.javax.sip.stack.SIPDialog;
-import gov.nist.javax.sip.stack.SIPTransaction;
-import gov.nist.javax.sip.stack.SIPTransactionImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Component;
 
 import javax.sdp.SdpFactory;
 import javax.sdp.SdpParseException;
 import javax.sdp.SessionDescription;
-import javax.sip.*;
-import javax.sip.address.Address;
+import javax.sip.InvalidArgumentException;
+import javax.sip.ResponseEvent;
+import javax.sip.SipException;
 import javax.sip.address.SipURI;
-import javax.sip.header.CSeqHeader;
-import javax.sip.header.UserAgentHeader;
 import javax.sip.message.Request;
 import javax.sip.message.Response;
 import java.text.ParseException;
@@ -104,6 +89,7 @@
 				} else {
 					sdp = SdpFactory.getInstance().createSessionDescription(contentString);
 				}
+				// 鏌ョ湅鏄惁鏄潵鑷澶囩殑锛屾鏄洖澶�
 
 				SipURI requestUri = sipLayer.getSipFactory().createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort());
 				Request reqAck = headerProvider.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response);
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 eebb461..984a377 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
@@ -280,6 +280,9 @@
             logger.info("[ZLM HOOK] 娴佹敞閿�, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
         }
 
+		JSONObject ret = new JSONObject();
+		ret.put("code", 0);
+		ret.put("msg", "success");
         MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
         JSONObject json = (JSONObject) JSON.toJSON(param);
         taskExecutor.execute(() -> {
@@ -335,34 +338,34 @@
 				}
 			}else if ("broadcast".equals(param.getApp())){
 				// 璇煶瀵硅鎺ㄦ祦  stream闇�瑕佹弧瓒虫牸寮廳eviceId_channelId
-				if (param.getStream().indexOf("_") > 0) {
-					String[] streamArray = param.getStream().split("_");
-					if (streamArray.length == 2) {
-						String deviceId = streamArray[0];
-						String channelId = streamArray[1];
-						Device device = deviceService.getDevice(deviceId);
-						if (device != null) {
-							if (param.isRegist()) {
-								if (audioBroadcastManager.exit(deviceId, channelId)) {
-									playService.stopAudioBroadcast(deviceId, channelId);
-								}
-								// 寮�鍚闊冲璁查�氶亾
-								try {
-									playService.audioBroadcastCmd(device, channelId, 60, mediaInfo, param.getApp(), param.getStream(), (msg)->{
-										logger.info("[璇煶瀵硅] 閫氶亾寤虹珛鎴愬姛, device: {}, channel: {}", deviceId, channelId);
-									});
-								} catch (InvalidArgumentException | ParseException | SipException e) {
-									logger.error("[鍛戒护鍙戦�佸け璐 璇煶瀵硅: {}", e.getMessage());
-								}
-							}else {
-								// 娴佹敞閿�
-								playService.stopAudioBroadcast(deviceId, channelId);
-							}
-						} else{
-							logger.info("[璇煶瀵硅] 鏈壘鍒拌澶囷細{}", deviceId);
-						}
-					}
-				}
+                if (param.getStream().indexOf("_") > 0) {
+                    String[] streamArray = param.getStream().split("_");
+                    if (streamArray.length == 2) {
+                        String deviceId = streamArray[0];
+                        String channelId = streamArray[1];
+                        Device device = deviceService.getDevice(deviceId);
+                        if (device != null) {
+                            if (param.isRegist()) {
+                                if (audioBroadcastManager.exit(deviceId, channelId)) {
+                                    playService.stopAudioBroadcast(deviceId, channelId);
+                                }
+                                // 寮�鍚闊冲璁查�氶亾
+                                try {
+                                    playService.audioBroadcastCmd(device, channelId, mediaInfo, param.getApp(), param.getStream(), 60, false, (msg)->{
+                                        logger.info("[璇煶瀵硅] 閫氶亾寤虹珛鎴愬姛, device: {}, channel: {}", deviceId, channelId);
+                                    });
+                                } catch (InvalidArgumentException | ParseException | SipException e) {
+                                    logger.error("[鍛戒护鍙戦�佸け璐 璇煶瀵硅: {}", e.getMessage());
+                                }
+                            }else {
+                                // 娴佹敞閿�
+                                playService.stopAudioBroadcast(deviceId, channelId);
+                            }
+                        } else{
+                            logger.info("[璇煶瀵硅] 鏈壘鍒拌澶囷細{}", deviceId);
+                        }
+                    }
+                }
 			}else if ("talk".equals(param.getApp())){
 				// 璇煶瀵硅鎺ㄦ祦  stream闇�瑕佹弧瓒虫牸寮廳eviceId_channelId
                 if (param.getStream().indexOf("_") > 0) {
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
index b382a3d..9a9dbe1 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
@@ -92,7 +92,7 @@
         return result;
     }
 
-    public int createRTPServer(MediaServerItem mediaServerItem, String streamId, int ssrc, Integer port) {
+    public int createRTPServer(MediaServerItem mediaServerItem, String streamId, int ssrc, Integer port, Boolean onlyAuto) {
         int result = -1;
         // 鏌ヨ姝tp server 鏄惁宸茬粡瀛樺湪
         JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId);
@@ -108,7 +108,7 @@
                     JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(mediaServerItem, param);
                     if (jsonObject != null ) {
                         if (jsonObject.getInteger("code") == 0) {
-                            return createRTPServer(mediaServerItem, streamId, ssrc, port);
+                            return createRTPServer(mediaServerItem, streamId, ssrc, port, onlyAuto);
                         }else {
                             logger.warn("[寮�鍚痳tpServer], 閲嶅惎RtpServer閿欒");
                         }
@@ -131,6 +131,9 @@
             param.put("port", port);
         }
         param.put("ssrc", ssrc);
+        if (onlyAuto != null) {
+            param.put("only_audio", onlyAuto?"1":"0");
+        }
         JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param);
         logger.info(JSONObject.toJSONString(openRtpServerResultJson));
         if (openRtpServerResultJson != null) {
@@ -352,4 +355,52 @@
         return result;
     }
 
+    public JSONObject startSendRtp(MediaServerItem mediaInfo, SendRtpItem sendRtpItem) {
+        String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
+        logger.info("rtp/{}寮�濮嬪悜涓婄骇鎺ㄦ祦, 鐩爣={}:{}锛孲SRC={}", sendRtpItem.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc());
+        Map<String, Object> param = new HashMap<>(12);
+        param.put("vhost","__defaultVhost__");
+        param.put("app",sendRtpItem.getApp());
+        param.put("stream",sendRtpItem.getStreamId());
+        param.put("ssrc", sendRtpItem.getSsrc());
+        param.put("src_port", sendRtpItem.getLocalPort());
+        param.put("pt", sendRtpItem.getPt());
+        param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0");
+        param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0");
+        if (!sendRtpItem.isTcp()) {
+            // udp妯″紡涓嬪紑鍚痳tcp淇濇椿
+            param.put("udp_rtcp_timeout", sendRtpItem.isRtcp()? "1":"0");
+        }
+
+        if (mediaInfo == null) {
+            return null;
+        }
+        // 濡傛灉鏄潪涓ユ牸妯″紡锛岄渶瑕佸叧闂鍙e崰鐢�
+        JSONObject startSendRtpStreamResult = null;
+        if (sendRtpItem.getLocalPort() != 0) {
+            HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(sendRtpItem.getSsrc(), null, mediaInfo.getId());
+            hookSubscribe.removeSubscribe(hookSubscribeForRtpServerTimeout);
+            if (releasePort(mediaInfo, sendRtpItem.getSsrc())) {
+                if (sendRtpItem.isTcpActive()) {
+                    startSendRtpStreamResult = startSendRtpPassive(mediaInfo, param);
+                    System.out.println(JSON.toJSON(param));
+                }else {
+                    param.put("is_udp", is_Udp);
+                    param.put("dst_url", sendRtpItem.getIp());
+                    param.put("dst_port", sendRtpItem.getPort());
+                    startSendRtpStreamResult = startSendRtpStream(mediaInfo, param);
+                }
+            }
+        }else {
+            if (sendRtpItem.isTcpActive()) {
+                startSendRtpStreamResult = startSendRtpPassive(mediaInfo, param);
+            }else {
+                param.put("is_udp", is_Udp);
+                param.put("dst_url", sendRtpItem.getIp());
+                param.put("dst_port", sendRtpItem.getPort());
+                startSendRtpStreamResult = startSendRtpStream(mediaInfo, param);
+            }
+        }
+        return startSendRtpStreamResult;
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java b/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
index 1233455..bac3d50 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
@@ -47,7 +47,7 @@
 
     SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback);
 
-    SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback, Integer port);
+    SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback, Integer port, Boolean onlyAuto);
 
     void closeRTPServer(MediaServerItem mediaServerItem, String streamId);
 
diff --git a/src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java b/src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java
index 17f8b37..be8a8f5 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java
@@ -1,7 +1,16 @@
 package com.genersoft.iot.vmp.service;
 
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
+import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
 import com.github.pagehelper.PageInfo;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
 
 /**
  * 鍥芥爣骞冲彴鐨勪笟鍔$被
@@ -48,4 +57,23 @@
      * @param platformId 骞冲彴
      */
     void sendNotifyMobilePosition(String platformId);
+
+    /**
+     * 鍚戜笂绾у彂閫佽闊冲枈璇濈殑娑堟伅
+     * @param platform 骞冲彴
+     * @param channelId 閫氶亾
+     * @param hookEvent hook浜嬩欢
+     * @param errorEvent 淇′护閿欒浜嬩欢
+     * @param timeoutCallback 瓒呮椂浜嬩欢
+     */
+    void broadcastInvite(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem,  ZlmHttpHookSubscribe.Event hookEvent,
+                         SipSubscribe.Event errorEvent, InviteTimeOutCallback timeoutCallback) throws InvalidArgumentException, ParseException, SipException;
+
+    /**
+     * 璇煶鍠婅瘽鍥炲BYE
+     * @param platform 骞冲彴
+     * @param channelId 閫氶亾
+     * @param stream 娴佷俊鎭�
+     */
+    void stopBroadcast(ParentPlatform platform, String channelId, String stream )throws InvalidArgumentException, ParseException, SsrcTransactionNotFoundException, SipException;
 }
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 b87176f..3d6352b 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
@@ -12,7 +12,9 @@
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
+import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import gov.nist.javax.sip.message.SIPResponse;
+import org.springframework.web.context.request.async.DeferredResult;
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.SipException;
@@ -54,6 +56,11 @@
     void zlmServerOnline(String mediaServerId);
 
     AudioBroadcastResult audioBroadcast(Device device, String channelId, Boolean broadcastMode);
+
+    boolean audioBroadcastCmd(Device device, String channelId, MediaServerItem mediaServerItem, String app, String stream, int timeout, boolean isFromPlatform, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException;
+
+    boolean audioBroadcastInUse(Device device, String channelId);
+
     void stopAudioBroadcast(String deviceId, String channelId);
 
     void audioBroadcastCmd(Device device, String channelId, int timeout, MediaServerItem mediaServerItem, String sourceApp, String sourceStream, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException;
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
index 522879a..226fa6f 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -1,16 +1,5 @@
 package com.genersoft.iot.vmp.service.impl;
 
-import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import com.genersoft.iot.vmp.media.zlm.ZLMRunner;
-import com.genersoft.iot.vmp.service.IStreamProxyService;
-import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
@@ -39,8 +28,6 @@
 import okhttp3.OkHttpClient;
 import okhttp3.Request;
 import okhttp3.Response;
-import java.time.LocalDateTime;
-import java.util.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -134,7 +121,8 @@
     }
 
     @Override
-    public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback, Integer port) {
+    public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck,
+                                  boolean isPlayback, Integer port, Boolean onlyAuto) {
         if (mediaServerItem == null || mediaServerItem.getId() == null) {
             logger.info("[openRTPServer] 澶辫触, mediaServerItem == null || mediaServerItem.getId() == null");
             return null;
@@ -163,7 +151,7 @@
             }
             int rtpServerPort;
             if (mediaServerItem.isRtpEnable()) {
-                rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0, port);
+                rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0, port, onlyAuto);
             } else {
                 rtpServerPort = mediaServerItem.getRtpProxyPort();
             }
@@ -174,7 +162,7 @@
 
     @Override
     public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback) {
-        return openRTPServer(mediaServerItem, streamId, ssrc, ssrcCheck, isPlayback, null);
+        return openRTPServer(mediaServerItem, streamId, ssrc, ssrcCheck, isPlayback, null, null);
     }
 
     @Override
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
index 1d48d54..1a788d2 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
@@ -1,15 +1,25 @@
 package com.genersoft.iot.vmp.service.impl;
 
+import com.alibaba.fastjson2.JSONObject;
+import com.genersoft.iot.vmp.common.StreamInfo;
 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.event.SipSubscribe;
+import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
+import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
+import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
+import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IPlatformService;
+import com.genersoft.iot.vmp.service.IPlayService;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
+import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
 import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper;
@@ -21,11 +31,13 @@
 import org.springframework.stereotype.Service;
 
 import javax.sip.InvalidArgumentException;
+import javax.sip.ResponseEvent;
 import javax.sip.SipException;
 import java.text.ParseException;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 
 /**
  * @author lin
@@ -64,6 +76,16 @@
 
     @Autowired
     private UserSetting userSetting;
+
+    @Autowired
+    private ZlmHttpHookSubscribe subscribe;
+
+    @Autowired
+    private VideoStreamSessionManager streamSession;
+
+
+    @Autowired
+    private IPlayService playService;
 
 
 
@@ -310,4 +332,138 @@
             }
         }
     }
+
+    @Override
+    public void broadcastInvite(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem, ZlmHttpHookSubscribe.Event hookEvent,
+                                SipSubscribe.Event errorEvent, InviteTimeOutCallback timeoutCallback) throws InvalidArgumentException, ParseException, SipException {
+
+        if (mediaServerItem == null) {
+            logger.info("[鍥芥爣绾ц仈] 璇煶鍠婅瘽鏈壘鍒板彲鐢ㄧ殑zlm. platform: {}", platform.getServerGBId());
+            return;
+        }
+        StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(platform.getServerGBId(), channelId);
+        if (streamInfo != null) {
+            // 濡傛灉zlm涓嶅瓨鍦ㄨ繖涓祦锛屽垯鍒犻櫎鏁版嵁鍗冲彲
+            MediaServerItem mediaServerItemForStreamInfo = mediaServerService.getOne(streamInfo.getMediaServerId());
+            if (mediaServerItemForStreamInfo != null) {
+                Boolean ready = zlmrtpServerFactory.isStreamReady(mediaServerItemForStreamInfo, streamInfo.getApp(), streamInfo.getStream());
+                if (!ready) {
+                    // 閿欒瀛樺湪浜巖edis涓殑鏁版嵁
+                    redisCatchStorage.stopPlay(streamInfo);
+                }else {
+                    // 娴佺‘瀹炲皻鍦ㄦ帹娴侊紝鐩存帴鍥炶皟缁撴灉
+                    JSONObject json = new JSONObject();
+                    json.put("app", streamInfo.getApp());
+                    json.put("stream", streamInfo.getStream());
+                    hookEvent.response(mediaServerItemForStreamInfo, json);
+                    return;
+                }
+            }
+        }
+
+        String streamId = null;
+        if (mediaServerItem.isRtpEnable()) {
+            streamId = String.format("%s_%s", platform.getServerGBId(), channelId);
+        }
+        // 榛樿涓嶈繘琛孲SRC鏍¢獙锛� TODO 鍚庣画鍙敼涓洪厤缃�
+        boolean ssrcCheck = false;
+        SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, ssrcCheck, false, null, true);
+        if (ssrcInfo == null || ssrcInfo.getPort() < 0) {
+            logger.info("[鍥芥爣绾ц仈] 鍙戣捣璇煶鍠婅瘽 寮�鍚鍙g洃鍚け璐ワ紝 platform: {}, channel锛� {}", platform.getServerGBId(), channelId);
+            errorEvent.response(new SipSubscribe.EventResult(-1, "绔彛鐩戝惉澶辫触"));
+            return;
+        }
+        logger.info("[鍥芥爣绾ц仈] 鍙戣捣璇煶鍠婅瘽 deviceId: {}, channelId: {},鏀舵祦绔彛锛� {}, 鏀舵祦妯″紡锛歿}, SSRC: {}, SSRC鏍¢獙锛歿}",
+                platform.getServerGBId(), channelId, ssrcInfo.getPort(), userSetting.getBroadcastForPlatform(), ssrcInfo.getSsrc(), ssrcCheck);
+
+        String timeOutTaskKey = UUID.randomUUID().toString();
+        dynamicTask.startDelay(timeOutTaskKey, () -> {
+            // 鎵ц瓒呮椂浠诲姟鏃舵煡璇㈡槸鍚﹀凡缁忔垚鍔燂紝鎴愬姛浜嗗垯涓嶆墽琛岃秴鏃朵换鍔★紝闃叉瓒呮椂浠诲姟鍙栨秷澶辫触鐨勬儏鍐�
+            if (redisCatchStorage.queryPlayByDevice(platform.getServerGBId(), channelId) == null) {
+                logger.info("[鍥芥爣绾ц仈] 鍙戣捣璇煶鍠婅瘽 鏀舵祦瓒呮椂 deviceId: {}, channelId: {}锛岀鍙o細{}, SSRC: {}", platform.getServerGBId(), channelId, ssrcInfo.getPort(), ssrcInfo.getSsrc());
+                // 鐐规挱瓒呮椂鍥炲BYE 鍚屾椂閲婃斁ssrc浠ュ強姝ゆ鐐规挱鐨勮祫婧�
+                try {
+                    commanderForPlatform.streamByeCmd(platform, channelId, ssrcInfo.getStream(), null, null);
+                } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
+                    logger.error("[鐐规挱瓒呮椂]锛� 鍙戦�丅YE澶辫触 {}", e.getMessage());
+                } finally {
+                    timeoutCallback.run(1, "鏀舵祦瓒呮椂");
+                    mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
+                    mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
+                    streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream());
+                    mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
+                }
+            }
+        }, userSetting.getPlayTimeout());
+        commanderForPlatform.broadcastInviteCmd(platform, channelId, mediaServerItem, ssrcInfo, (mediaServerItemForInvite, response)->{
+            dynamicTask.stop(timeOutTaskKey);
+            // hook鍝嶅簲
+            playService.onPublishHandlerForPlay(mediaServerItemForInvite, response, platform.getServerGBId(), channelId);
+            // 鏀跺埌娴�
+            if (hookEvent != null) {
+                hookEvent.response(mediaServerItem, response);
+            }
+        }, event -> {
+            // 鏀跺埌200OK 妫�娴媠src鏄惁鏈夊彉鍖栵紝闃叉涓婄骇鑷畾涔変簡ssrc
+            ResponseEvent responseEvent = (ResponseEvent) event.event;
+            String contentString = new String(responseEvent.getResponse().getRawContent());
+            // 鑾峰彇ssrc
+            int ssrcIndex = contentString.indexOf("y=");
+            // 妫�鏌ユ槸鍚︽湁y瀛楁
+            if (ssrcIndex >= 0) {
+                //ssrc瑙勫畾闀垮害涓�10瀛楄妭锛屼笉鍙栦綑涓嬮暱搴︿互閬垮厤鍚庣画杩樻湁鈥渇=鈥濆瓧娈� TODO 鍚庣画瀵逛笉瑙勮寖鐨勯潪10浣峴src鍏煎
+                String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
+                // 鏌ヨ鍒皊src涓嶄竴鑷翠笖寮�鍚簡ssrc鏍¢獙鍒欓渶瑕侀拡瀵瑰鐞�
+                if (ssrcInfo.getSsrc().equals(ssrcInResponse) || ssrcCheck) {
+                    return;
+                }
+                logger.info("[鐐规挱娑堟伅] 鏀跺埌invite 200, 鍙戠幇涓嬬骇鑷畾涔変簡ssrc: {}", ssrcInResponse);
+                if (!mediaServerItem.isRtpEnable()) {
+                    logger.info("[鐐规挱娑堟伅] SSRC淇 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
+
+                    if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
+                        // ssrc 涓嶅彲鐢�
+                        // 閲婃斁ssrc
+                        mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
+                        streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream());
+                        event.msg = "涓嬬骇鑷畾涔変簡ssrc,浣嗘槸姝src涓嶅彲鐢�";
+                        event.statusCode = 400;
+                        errorEvent.response(event);
+                        return;
+                    }
+
+                    // 鍗曠鍙fā寮弒treamId涔熸湁鍙樺寲锛岄渶瑕侀噸鏂拌缃洃鍚�
+                    if (!mediaServerItem.isRtpEnable()) {
+                        // 娣诲姞璁㈤槄
+                        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
+                        subscribe.removeSubscribe(hookSubscribe);
+                        hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
+                        subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
+                            logger.info("[ZLM HOOK] ssrc淇鍚庢敹鍒拌闃呮秷鎭細 " + response.toJSONString());
+                            dynamicTask.stop(timeOutTaskKey);
+                            // hook鍝嶅簲
+                            playService.onPublishHandlerForPlay(mediaServerItemInUse, response, platform.getServerGBId(), channelId);
+                            hookEvent.response(mediaServerItemInUse, response);
+                        });
+                    }
+                    // 鍏抽棴rtp server
+                    mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
+                    // 閲嶆柊寮�鍚痵src server
+                    mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, false, false, ssrcInfo.getPort(), true);
+
+
+                }
+            }
+        }, eventResult -> {
+            // 鏀跺埌閿欒鍥炲
+            if (errorEvent != null) {
+                errorEvent.response(eventResult);
+            }
+        });
+    }
+
+    @Override
+    public void stopBroadcast(ParentPlatform platform, String channelId, String stream) throws InvalidArgumentException, ParseException, SsrcTransactionNotFoundException, SipException {
+        commanderForPlatform.streamByeCmd(platform, channelId, stream, null, null);
+    }
 }
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 b89f173..7e85f0c 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
@@ -490,7 +490,7 @@
                         // 鍏抽棴rtp server
                         mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
                         // 閲嶆柊寮�鍚痵src server
-                        mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, ssrcInfo.getPort());
+                        mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, ssrcInfo.getPort(), false);
 
                     }
                 }
@@ -731,7 +731,7 @@
                                     // 鍏抽棴rtp server
                                     mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
                                     // 閲嶆柊寮�鍚痵src server
-                                    mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
+                                    mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort(), false);
                                 }
                             }
                         }
@@ -966,16 +966,16 @@
     }
 
     @Override
-    public void audioBroadcastCmd(Device device, String channelId, int timeout, MediaServerItem mediaServerItem, String sourceApp, String sourceStream, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException {
+    public boolean audioBroadcastCmd(Device device, String channelId, MediaServerItem mediaServerItem, String app, String stream, int timeout, boolean isFromPlatform, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException {
         if (device == null || channelId == null) {
-            return;
+            return false;
         }
         logger.info("[璇煶鍠婅瘽] device锛� {}, channel: {}", device.getDeviceId(), channelId);
         DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId);
         if (deviceChannel == null) {
             logger.warn("寮�鍚闊冲箍鎾殑鏃跺�欐湭鎵惧埌閫氶亾锛� {}", channelId);
             event.call("寮�鍚闊冲箍鎾殑鏃跺�欐湭鎵惧埌閫氶亾");
-            return;
+            return false;
         }
         // 鏌ヨ閫氶亾浣跨敤鐘舵��
         if (audioBroadcastManager.exit(device.getDeviceId(), channelId)) {
@@ -986,7 +986,7 @@
                 if (streamReady) {
                     logger.warn("璇煶骞挎挱宸茬粡寮�鍚細 {}", channelId);
                     event.call("璇煶骞挎挱宸茬粡寮�鍚�");
-                    return;
+                    return false;
                 } else {
                     stopAudioBroadcast(device.getDeviceId(), channelId);
                 }
@@ -1008,8 +1008,7 @@
         // 鍙戦�侀�氱煡
         cmder.audioBroadcastCmd(device, channelId, eventResultForOk -> {
             // 鍙戦�佹垚鍔�
-            AudioBroadcastCatch audioBroadcastCatch = new AudioBroadcastCatch(device.getDeviceId(), channelId,
-                    AudioBroadcastCatchStatus.Ready, mediaServerItem, sourceApp, sourceStream);
+            AudioBroadcastCatch audioBroadcastCatch = new AudioBroadcastCatch(device.getDeviceId(), channelId, mediaServerItem, app, stream, event, AudioBroadcastCatchStatus.Ready, isFromPlatform);
             audioBroadcastManager.update(audioBroadcastCatch);
         }, eventResultForError -> {
             // 鍙戦�佸け璐�
@@ -1017,6 +1016,24 @@
             event.call("璇煶骞挎挱鍙戦�佸け璐�");
             stopAudioBroadcast(device.getDeviceId(), channelId);
         });
+        return true;
+    }
+
+    @Override
+    public boolean audioBroadcastInUse(Device device, String channelId) {
+        if (audioBroadcastManager.exit(device.getDeviceId(), channelId)) {
+            SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null);
+            if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) {
+                // 鏌ヨ娴佹槸鍚﹀瓨鍦紝涓嶅瓨鍦ㄥ垯璁や负鏄紓甯哥姸鎬�
+                MediaServerItem mediaServerServiceOne = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+                Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerServiceOne, sendRtpItem.getApp(), sendRtpItem.getStreamId());
+                if (streamReady) {
+                    logger.warn("璇煶骞挎挱閫氶亾浣跨敤涓細 {}", channelId);
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
 
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 3eba74e..96031f8 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
@@ -250,10 +250,10 @@
 	@GetMapping("/broadcast/{deviceId}/{channelId}")
 	@PostMapping("/broadcast/{deviceId}/{channelId}")
     public AudioBroadcastResult broadcastApi(@PathVariable String deviceId, @PathVariable String channelId, Integer timeout, Boolean broadcastMode) {
-        if (logger.isDebugEnabled()) {
-            logger.debug("璇煶骞挎挱API璋冪敤");
-        }
-        Device device = storager.queryVideoDevice(deviceId);
+		if (logger.isDebugEnabled()) {
+			logger.debug("璇煶骞挎挱API璋冪敤");
+		}
+		Device device = storager.queryVideoDevice(deviceId);
 		if (device == null) {
 			throw new ControllerException(ErrorCode.ERROR400.getCode(), "鏈壘鍒拌澶囷細 " + deviceId);
 		}
@@ -264,7 +264,6 @@
 		return playService.audioBroadcast(device, channelId, broadcastMode);
 
 	}
-
 
 	@Operation(summary = "鍋滄璇煶骞挎挱")
 	@Parameter(name = "deviceId", description = "璁惧Id", required = true)
diff --git a/src/main/resources/all-application.yml b/src/main/resources/all-application.yml
index c48c26e..4f7a8f6 100644
--- a/src/main/resources/all-application.yml
+++ b/src/main/resources/all-application.yml
@@ -195,6 +195,8 @@
     gb-send-stream-strict: false
     # 璁惧涓婄嚎鏃舵槸鍚﹁嚜鍔ㄥ悓姝ラ�氶亾
     sync-channel-on-device-online: false
+    # 鍥芥爣绾ц仈璇煶鍠婅瘽鍙戞祦妯″紡 * UDP:udp浼犺緭 TCP-ACTIVE锛歵cp涓诲姩妯″紡 TCP-PASSIVE锛歵cp琚姩妯″紡
+    broadcast-for-platform: UDP
     # 鏄惁浣跨敤璁惧鏉ユ簮Ip浣滀负鍥炲IP锛� 涓嶈缃垯涓� false
     sip-use-source-ip-as-remote-address: false
     # 鏄惁寮�鍚痵ip鏃ュ織
diff --git a/web_src/src/components/dialog/devicePlayer.vue b/web_src/src/components/dialog/devicePlayer.vue
index 0bc3335..61cf066 100644
--- a/web_src/src/components/dialog/devicePlayer.vue
+++ b/web_src/src/components/dialog/devicePlayer.vue
@@ -663,46 +663,46 @@
               this.startBroadcast(streamInfo.rtc)
             }
 
-          }else {
-            this.$message({
-              showClose: true,
-              message: res.data.msg,
-              type: "error",
-            });
-          }
-        });
-      }else if (this.broadcastStatus === 1) {
-        this.broadcastStatus = -1;
-        this.broadcastRtc.close()
-      }
-    },
-    startBroadcast(url) {
-      // 鑾峰彇鎺ㄦ祦閴存潈Key
-      this.$axios({
-        method: 'post',
-        url: '/api/user/userInfo',
-      }).then((res) => {
-        if (res.data.code !== 0) {
-          this.$message({
-            showClose: true,
-            message: "鑾峰彇鎺ㄦ祦閴存潈Key澶辫触",
-            type: "error",
-          });
-          this.broadcastStatus = -1;
-        } else {
-          let pushKey = res.data.data.pushKey;
-          // 鑾峰彇鎺ㄦ祦閴存潈KEY
-          url += "&sign=" + crypto.createHash('md5').update(pushKey, "utf8").digest('hex')
-          console.log("寮�濮嬭闊冲璁诧細 " + url)
-          this.broadcastRtc = new ZLMRTCClient.Endpoint({
-            debug: true, // 鏄惁鎵撳嵃鏃ュ織
-            zlmsdpUrl: url, //娴佸湴鍧�
-            simulecast: false,
-            useCamera: false,
-            audioEnable: true,
-            videoEnable: false,
-            recvOnly: false,
-          })
+                }else {
+                  this.$message({
+                    showClose: true,
+                    message: res.data.msg,
+                    type: "error",
+                  });
+                }
+              });
+            }else if (this.broadcastStatus === 1) {
+                this.broadcastStatus = -1;
+                this.broadcastRtc.close()
+            }
+        },
+        startBroadcast(url){
+          // 鑾峰彇鎺ㄦ祦閴存潈Key
+          this.$axios({
+            method: 'post',
+            url: '/api/user/userInfo',
+          }).then( (res)=> {
+            if (res.data.code !== 0) {
+              this.$message({
+                showClose: true,
+                message: "鑾峰彇鎺ㄦ祦閴存潈Key澶辫触",
+                type: "error",
+              });
+              this.broadcastStatus = -1;
+            }else {
+              let pushKey = res.data.data.pushKey;
+              // 鑾峰彇鎺ㄦ祦閴存潈KEY
+              url += "&sign=" + crypto.createHash('md5').update(pushKey, "utf8").digest('hex')
+              console.log("寮�濮嬭闊冲枈璇濓細 " + url)
+              this.broadcastRtc = new ZLMRTCClient.Endpoint({
+                debug: true, // 鏄惁鎵撳嵃鏃ュ織
+                zlmsdpUrl: url, //娴佸湴鍧�
+                simulecast: false,
+                useCamera: false,
+                audioEnable: true,
+                videoEnable: false,
+                recvOnly: false,
+              })
 
           // webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,(e)=>{//鑾峰彇鍒颁簡杩滅娴侊紝鍙互鎾斁
           //   console.error('鎾斁鎴愬姛',e.streams)
@@ -715,15 +715,15 @@
           //   // this.eventcallbacK("LOCAL STREAM", "鑾峰彇鍒颁簡鏈湴娴�")
           // });
 
-          this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_NOT_SUPPORT, (e) => {// 鑾峰彇鍒颁簡鏈湴娴�
-            console.error('涓嶆敮鎸亀ebrtc', e)
-            this.$message({
-              showClose: true,
-              message: '涓嶆敮鎸亀ebrtc, 鏃犳硶杩涜璇煶瀵硅',
-              type: 'error'
-            });
-            this.broadcastStatus = -1;
-          });
+              this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_NOT_SUPPORT,(e)=>{// 鑾峰彇鍒颁簡鏈湴娴�
+                console.error('涓嶆敮鎸亀ebrtc',e)
+                this.$message({
+                  showClose: true,
+                  message: '涓嶆敮鎸亀ebrtc, 鏃犳硶杩涜璇煶鍠婅瘽',
+                  type: 'error'
+                });
+                this.broadcastStatus = -1;
+              });
 
           this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, (e) => {// ICE 鍗忓晢鍑洪敊
             console.error('ICE 鍗忓晢鍑洪敊')

--
Gitblit v1.8.0