From 82adc0cb23f3ee47322e78889cdaba57e9309000 Mon Sep 17 00:00:00 2001
From: 648540858 <648540858@qq.com>
Date: 星期二, 21 三月 2023 15:55:24 +0800
Subject: [PATCH] 完善语音对讲级联

---
 src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java |  556 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 530 insertions(+), 26 deletions(-)

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 b8e7c8e..68c2f77 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
@@ -5,51 +5,60 @@
 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.SipConfig;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.conf.exception.ServiceException;
 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.AudioBroadcastManager;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
+import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
 import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
+import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
 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.HookSubscribeForRtpServerTimeout;
 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
 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.IMediaService;
 import com.genersoft.iot.vmp.service.IPlayService;
-import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
-import com.genersoft.iot.vmp.service.bean.PlayBackCallback;
-import com.genersoft.iot.vmp.service.bean.PlayBackResult;
-import com.genersoft.iot.vmp.service.bean.SSRCInfo;
+import com.genersoft.iot.vmp.service.bean.*;
+import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
+import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
+import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
+import gov.nist.javax.sip.message.SIPResponse;
 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.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Service;
 import org.springframework.util.ObjectUtils;
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.ResponseEvent;
 import javax.sip.SipException;
+import javax.sip.header.CallIdHeader;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.text.ParseException;
-import java.util.List;
-import java.util.UUID;
+import java.util.*;
 
 @SuppressWarnings(value = {"rawtypes", "unchecked"})
 @Service
@@ -64,10 +73,19 @@
     private SIPCommander cmder;
 
     @Autowired
-    private SIPCommanderFroPlatform sipCommanderFroPlatform;
+    private AudioBroadcastManager audioBroadcastManager;
+
+    @Autowired
+    private IDeviceService deviceService;
+
+    @Autowired
+    private ISIPCommanderForPlatform sipCommanderFroPlatform;
 
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private ZLMRTPServerFactory zlmrtpServerFactory;
 
     @Autowired
     private DeferredResultHolder resultHolder;
@@ -87,12 +105,11 @@
     @Autowired
     private VideoStreamSessionManager streamSession;
 
-
-    @Autowired
-    private IDeviceService deviceService;
-
     @Autowired
     private UserSetting userSetting;
+
+    @Autowired
+    private SipConfig sipConfig;
 
     @Autowired
     private DynamicTask dynamicTask;
@@ -100,11 +117,25 @@
     @Autowired
     private ZlmHttpHookSubscribe subscribe;
 
+    @Autowired
+    private ISIPCommanderForPlatform commanderForPlatform;
+
+
+    @Qualifier("taskExecutor")
+    @Autowired
+    private ThreadPoolTaskExecutor taskExecutor;
+
+    @Autowired
+    private RedisGbPlayMsgListener redisGbPlayMsgListener;
+
+    @Autowired
+    private ZlmHttpHookSubscribe hookSubscribe;
+
 
     @Override
     public void play(MediaServerItem mediaServerItem, String deviceId, String channelId,
-                                 ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent,
-                                 Runnable timeoutCallback) {
+                     ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent,
+                     Runnable timeoutCallback) {
         if (mediaServerItem == null) {
             throw new ControllerException(ErrorCode.ERROR100.getCode(), "鏈壘鍒板彲鐢ㄧ殑zlm");
         }
@@ -165,7 +196,6 @@
                 redisCatchStorage.stopPlay(streamInfo);
                 storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
                 streamInfo = null;
-
             }
         }
         if (streamInfo == null) {
@@ -213,6 +243,147 @@
         }
     }
 
+    private void talk(MediaServerItem mediaServerItem, Device device, String channelId, String stream,
+                      ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent,
+                      Runnable timeoutCallback, AudioBroadcastEvent audioEvent) {
+
+        String playSsrc = mediaServerItem.getSsrcConfig().getPlaySsrc();
+        if (playSsrc == null) {
+            audioEvent.call("ssrc宸茬粡鐢ㄥ敖");
+            return;
+        }
+        SendRtpItem sendRtpItem = new SendRtpItem();
+        sendRtpItem.setApp("talk");
+        sendRtpItem.setStream(stream);
+        sendRtpItem.setSsrc(playSsrc);
+        sendRtpItem.setDeviceId(device.getDeviceId());
+        sendRtpItem.setPlatformId(device.getDeviceId());
+        sendRtpItem.setChannelId(channelId);
+        sendRtpItem.setRtcp(false);
+        sendRtpItem.setMediaServerId(mediaServerItem.getId());
+        sendRtpItem.setOnlyAudio(true);
+        sendRtpItem.setPlayType(InviteStreamType.TALK);
+        sendRtpItem.setPt(8);
+        sendRtpItem.setStatus(1);
+        sendRtpItem.setTcpActive(false);
+        sendRtpItem.setTcp(true);
+        sendRtpItem.setUsePs(false);
+        sendRtpItem.setReceiveStream(stream + "_talk");
+
+
+        int port = zlmrtpServerFactory.keepPort(mediaServerItem, playSsrc);
+        //绔彛鑾峰彇澶辫触鐨剆srcInfo 娌℃湁蹇呰鍙戦�佺偣鎾寚浠�
+        if (port <= 0) {
+            logger.info("[璇煶瀵硅] 绔彛鍒嗛厤寮傚父锛宒eviceId={},channelId={}", device.getDeviceId(), channelId);
+            audioEvent.call("绔彛鍒嗛厤寮傚父");
+            return;
+        }
+        sendRtpItem.setLocalPort(port);
+        sendRtpItem.setPort(port);
+        logger.info("[璇煶瀵硅]寮�濮� deviceId: {}, channelId: {},鏀舵祦绔彛锛� {}, 鏀舵祦妯″紡锛歿}, SSRC: {}, SSRC鏍¢獙锛歿}", device.getDeviceId(), channelId, sendRtpItem.getLocalPort(), device.getStreamMode(), sendRtpItem.getSsrc(), false);
+        // 瓒呮椂澶勭悊
+        String timeOutTaskKey = UUID.randomUUID().toString();
+        dynamicTask.startDelay(timeOutTaskKey, () -> {
+
+            logger.info("[璇煶瀵硅] 鏀舵祦瓒呮椂 deviceId: {}, channelId: {}锛岀鍙o細{}, SSRC: {}", device.getDeviceId(), channelId, sendRtpItem.getPort(), sendRtpItem.getSsrc());
+            timeoutCallback.run();
+            // 鐐规挱瓒呮椂鍥炲BYE 鍚屾椂閲婃斁ssrc浠ュ強姝ゆ鐐规挱鐨勮祫婧�
+            try {
+                cmder.streamByeCmd(device, channelId, sendRtpItem.getStream(), null);
+            } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
+                logger.error("[璇煶瀵硅]瓒呮椂锛� 鍙戦�丅YE澶辫触 {}", e.getMessage());
+            } finally {
+                timeoutCallback.run();
+                mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
+                streamSession.remove(device.getDeviceId(), channelId, sendRtpItem.getStream());
+            }
+        }, userSetting.getPlayTimeout());
+
+        String callId = SipUtils.getNewCallId();
+
+        zlmrtpServerFactory.releasePort(mediaServerItem, playSsrc);
+        Map<String, Object> param = new HashMap<>(12);
+        param.put("vhost","__defaultVhost__");
+        param.put("app", sendRtpItem.getApp());
+        param.put("stream", sendRtpItem.getStream());
+        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");
+        param.put("is_udp", sendRtpItem.isTcp() ? "0" : "1");
+        param.put("recv_stream_id", sendRtpItem.getReceiveStream());
+        param.put("close_delay_ms", userSetting.getPlayTimeout() * 1000);
+
+        zlmrtpServerFactory.startSendRtpPassive(mediaServerItem, param, jsonObject -> {
+            if (jsonObject == null || jsonObject.getInteger("code") != 0 ) {
+                mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
+                logger.info("[璇煶瀵硅]澶辫触 deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
+                audioEvent.call("澶辫触, " + jsonObject.getString("msg"));
+                // 鏌ョ湅鏄惁宸茬粡寤虹珛浜嗛�氶亾锛屽瓨鍦ㄥ垯鍙戦�乥ye
+                stopTalk(device, channelId);
+            }
+        });
+
+
+        // 鏌ョ湅璁惧鏄惁宸茬粡鍦ㄦ帹娴�
+        try {
+            cmder.talkStreamCmd(mediaServerItem, sendRtpItem, device, channelId, callId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
+                logger.info("[璇煶瀵硅] 娴佸凡鐢熸垚锛� 寮�濮嬫帹娴侊細 " + response.toJSONString());
+                dynamicTask.stop(timeOutTaskKey);
+                // TODO 鏆備笉鍋氬鐞�
+            }, (MediaServerItem mediaServerItemInuse, JSONObject json) -> {
+                logger.info("[璇煶瀵硅] 璁惧寮�濮嬫帹娴侊細 " + json.toJSONString());
+                dynamicTask.stop(timeOutTaskKey);
+
+            }, (event) -> {
+                dynamicTask.stop(timeOutTaskKey);
+
+                if (event.event instanceof ResponseEvent) {
+                    ResponseEvent responseEvent = (ResponseEvent) event.event;
+                    if (responseEvent.getResponse() instanceof SIPResponse) {
+                        SIPResponse response = (SIPResponse) responseEvent.getResponse();
+                        sendRtpItem.setFromTag(response.getFromTag());
+                        sendRtpItem.setToTag(response.getToTag());
+                        sendRtpItem.setCallId(response.getCallIdHeader().getCallId());
+                        redisCatchStorage.updateSendRTPSever(sendRtpItem);
+
+                        streamSession.put(device.getDeviceId(), channelId, "talk",
+                                sendRtpItem.getStream(), sendRtpItem.getSsrc(), sendRtpItem.getMediaServerId(),
+                                response, VideoStreamSessionManager.SessionType.talk);
+                    } else {
+                        logger.error("[璇煶瀵硅]鏀跺埌鐨勬秷鎭敊璇紝response涓嶆槸SIPResponse");
+                    }
+                } else {
+                    logger.error("[璇煶瀵硅]鏀跺埌鐨勬秷鎭敊璇紝event涓嶆槸ResponseEvent");
+                }
+
+            }, (event) -> {
+                dynamicTask.stop(timeOutTaskKey);
+                mediaServerService.closeRTPServer(mediaServerItem, sendRtpItem.getStream());
+                // 閲婃斁ssrc
+                mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
+                streamSession.remove(device.getDeviceId(), channelId, sendRtpItem.getStream());
+                errorEvent.response(event);
+            });
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+
+            logger.error("[鍛戒护鍙戦�佸け璐 瀵硅娑堟伅: {}", e.getMessage());
+            dynamicTask.stop(timeOutTaskKey);
+            mediaServerService.closeRTPServer(mediaServerItem, sendRtpItem.getStream());
+            // 閲婃斁ssrc
+            mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
+
+            streamSession.remove(device.getDeviceId(), channelId, sendRtpItem.getStream());
+            SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null));
+            eventResult.msg = "鍛戒护鍙戦�佸け璐�";
+            errorEvent.response(eventResult);
+        }
+//        }
+
+    }
+
+
 
     @Override
     public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
@@ -229,7 +400,8 @@
                 // 鐐规挱瓒呮椂鍥炲BYE 鍚屾椂閲婃斁ssrc浠ュ強姝ゆ鐐规挱鐨勮祫婧�
                 try {
                     cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null);
-                } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
+                } catch (InvalidArgumentException | ParseException | SipException |
+                         SsrcTransactionNotFoundException e) {
                     logger.error("[鐐规挱瓒呮椂]锛� 鍙戦�丅YE澶辫触 {}", e.getMessage());
                 } finally {
                     timeoutCallback.run(1, "鏀舵祦瓒呮椂");
@@ -266,7 +438,7 @@
                 onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId);
                 hookEvent.response(mediaServerItemInuse, response);
                 logger.info("[鐐规挱鎴愬姛] deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
-                String streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.flv", mediaServerItemInuse.getHttpPort(), "rtp",  ssrcInfo.getStream());
+                String streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.flv", mediaServerItemInuse.getHttpPort(), "rtp", ssrcInfo.getStream());
                 String path = "snap";
                 String fileName = device.getDeviceId() + "_" + channelId + ".jpg";
                 // 璇锋眰鎴浘
@@ -318,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);
 
                     }
                 }
@@ -435,8 +607,8 @@
 
     @Override
     public void playBack(String deviceId, String channelId, String startTime,
-                                                          String endTime, InviteStreamCallback inviteStreamCallback,
-                                                          PlayBackCallback callback) {
+                         String endTime, InviteStreamCallback inviteStreamCallback,
+                         PlayBackCallback callback) {
         Device device = storager.queryVideoDevice(deviceId);
         if (device == null) {
             return;
@@ -449,9 +621,9 @@
 
     @Override
     public void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo,
-                                                          String deviceId, String channelId, String startTime,
-                                                          String endTime, InviteStreamCallback infoCallBack,
-                                                          PlayBackCallback playBackCallback) {
+                         String deviceId, String channelId, String startTime,
+                         String endTime, InviteStreamCallback infoCallBack,
+                         PlayBackCallback playBackCallback) {
         if (mediaServerItem == null || ssrcInfo == null) {
             return;
         }
@@ -559,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);
                                 }
                             }
                         }
@@ -573,7 +745,6 @@
             errorEvent.response(eventResult);
         }
     }
-
 
 
     @Override
@@ -760,13 +931,149 @@
                         cmder.streamByeCmd(device, ssrcTransaction.getChannelId(),
                                 ssrcTransaction.getStream(), null);
                     } catch (InvalidArgumentException | ParseException | SipException |
-                            SsrcTransactionNotFoundException e) {
+                             SsrcTransactionNotFoundException e) {
                         logger.error("[zlm绂荤嚎]涓烘鍦ㄤ娇鐢ㄦzlm鐨勮澶囷紝 鍙戦�丅YE澶辫触 {}", e.getMessage());
                     }
                 }
             }
         }
     }
+
+    @Override
+    public AudioBroadcastResult audioBroadcast(Device device, String channelId, Boolean broadcastMode) {
+        // TODO 蹇呴』澶氱鍙fā寮忔墠鏀寔璇煶鍠婅瘽楣よ闊冲璁�
+        if (device == null || channelId == null) {
+            return null;
+        }
+        logger.info("[璇煶鍠婅瘽] device锛� {}, channel: {}", device.getDeviceId(), channelId);
+        DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId);
+        if (deviceChannel == null) {
+            logger.warn("寮�鍚闊冲箍鎾殑鏃跺�欐湭鎵惧埌閫氶亾锛� {}", channelId);
+            return null;
+        }
+        MediaServerItem mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(null);
+        if (broadcastMode == null) {
+            broadcastMode = true;
+        }
+        String app = broadcastMode?"broadcast":"talk";
+        String stream = device.getDeviceId() + "_" + channelId;
+        AudioBroadcastResult audioBroadcastResult = new AudioBroadcastResult();
+        audioBroadcastResult.setApp(app);
+        audioBroadcastResult.setStream(stream);
+        audioBroadcastResult.setStreamInfo(new StreamContent(mediaService.getStreamInfoByAppAndStream(mediaServerItem, app, stream, null, null, null, false)));
+        audioBroadcastResult.setCodec("G.711");
+        return audioBroadcastResult;
+    }
+
+    @Override
+    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 false;
+        }
+        logger.info("[璇煶鍠婅瘽] device锛� {}, channel: {}", device.getDeviceId(), channelId);
+        DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId);
+        if (deviceChannel == null) {
+            logger.warn("寮�鍚闊冲箍鎾殑鏃跺�欐湭鎵惧埌閫氶亾锛� {}", channelId);
+            event.call("寮�鍚闊冲箍鎾殑鏃跺�欐湭鎵惧埌閫氶亾");
+            return false;
+        }
+        // 鏌ヨ閫氶亾浣跨敤鐘舵��
+        if (audioBroadcastManager.exit(device.getDeviceId(), channelId)) {
+            SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null);
+            if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) {
+                // 鏌ヨ娴佹槸鍚﹀瓨鍦紝涓嶅瓨鍦ㄥ垯璁や负鏄紓甯哥姸鎬�
+                Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStream());
+                if (streamReady) {
+                    logger.warn("璇煶骞挎挱宸茬粡寮�鍚細 {}", channelId);
+                    event.call("璇煶骞挎挱宸茬粡寮�鍚�");
+                    return false;
+                } else {
+                    stopAudioBroadcast(device.getDeviceId(), channelId);
+                }
+            }
+        }
+        SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null);
+        if (sendRtpItem != null) {
+            MediaServerItem mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+            Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServer, "rtp", sendRtpItem.getReceiveStream());
+            if (streamReady) {
+                logger.warn("[璇煶瀵硅] 杩涜涓細 {}", channelId);
+                event.call("璇煶瀵硅杩涜涓�");
+                return false;
+            } else {
+                stopTalk(device, channelId);
+            }
+        }
+
+        // 鍙戦�侀�氱煡
+        cmder.audioBroadcastCmd(device, channelId, eventResultForOk -> {
+            // 鍙戦�佹垚鍔�
+            AudioBroadcastCatch audioBroadcastCatch = new AudioBroadcastCatch(device.getDeviceId(), channelId, mediaServerItem, app, stream, event, AudioBroadcastCatchStatus.Ready, isFromPlatform);
+            audioBroadcastManager.update(audioBroadcastCatch);
+        }, eventResultForError -> {
+            // 鍙戦�佸け璐�
+            logger.error("璇煶骞挎挱鍙戦�佸け璐ワ細 {}:{}", channelId, eventResultForError.msg);
+            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.getStream());
+                if (streamReady) {
+                    logger.warn("璇煶骞挎挱閫氶亾浣跨敤涓細 {}", channelId);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+
+    @Override
+    public void stopAudioBroadcast(String deviceId, String channelId) {
+        logger.info("[鍋滄瀵硅] 璁惧锛歿}, 閫氶亾锛歿}", deviceId, channelId);
+        List<AudioBroadcastCatch> audioBroadcastCatchList = new ArrayList<>();
+        if (channelId == null) {
+            audioBroadcastCatchList.addAll(audioBroadcastManager.get(deviceId));
+        } else {
+            audioBroadcastCatchList.add(audioBroadcastManager.get(deviceId, channelId));
+        }
+        if (audioBroadcastCatchList.size() > 0) {
+            for (AudioBroadcastCatch audioBroadcastCatch : audioBroadcastCatchList) {
+                Device device = deviceService.getDevice(deviceId);
+                if (device == null || audioBroadcastCatch == null) {
+                    return;
+                }
+                SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(deviceId, audioBroadcastCatch.getChannelId(), null, null);
+                if (sendRtpItem != null) {
+                    redisCatchStorage.deleteSendRTPServer(deviceId, sendRtpItem.getChannelId(), null, null);
+                    MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+                    Map<String, Object> param = new HashMap<>();
+                    param.put("vhost", "__defaultVhost__");
+                    param.put("app", sendRtpItem.getApp());
+                    param.put("stream", sendRtpItem.getStream());
+                    zlmresTfulUtils.stopSendRtp(mediaInfo, param);
+                    try {
+                        cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);
+                    } catch (InvalidArgumentException | ParseException | SipException |
+                             SsrcTransactionNotFoundException e) {
+                        logger.error("[娑堟伅鍙戦�佸け璐 鍙戦�佽闊冲枈璇滲YE澶辫触");
+                    }
+                }
+
+                audioBroadcastManager.del(deviceId, channelId);
+            }
+        }
+    }
+
 
     @Override
     public void zlmServerOnline(String mediaServerId) {
@@ -867,4 +1174,201 @@
         Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
         cmder.playResumeCmd(device, streamInfo);
     }
+
+    @Override
+    public void startPushStream(SendRtpItem sendRtpItem, SIPResponse sipResponse, ParentPlatform platform, CallIdHeader callIdHeader) {
+
+        // 寮�濮嬪彂娴�
+        // 鍙栨秷璁剧疆鐨勮秴鏃朵换鍔�
+//			String channelId = request.getCallIdHeader().getCallId();
+
+        String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
+        MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+        logger.info("鏀跺埌ACK锛宺tp/{}寮�濮嬫帹娴�, 鐩爣={}:{}锛孲SRC={}, RTCP={}", sendRtpItem.getStream(),
+                sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isRtcp());
+        Map<String, Object> param = new HashMap<>(12);
+        param.put("vhost", "__defaultVhost__");
+        param.put("app", sendRtpItem.getApp());
+        param.put("stream", sendRtpItem.getStream());
+        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");
+        param.put("is_udp", is_Udp);
+        if (!sendRtpItem.isTcp()) {
+            // udp妯″紡涓嬪紑鍚痳tcp淇濇椿
+            param.put("udp_rtcp_timeout", sendRtpItem.isRtcp() ? "1" : "0");
+        }
+
+        if (mediaInfo == null) {
+            RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance(
+                    sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStream(),
+                    sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(),
+                    sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio());
+            redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, json -> {
+                startSendRtpStreamHand(sendRtpItem, platform, json, param, callIdHeader);
+            });
+        } else {
+            // 濡傛灉鏄潪涓ユ牸妯″紡锛岄渶瑕佸叧闂鍙e崰鐢�
+            JSONObject startSendRtpStreamResult = null;
+            if (sendRtpItem.getLocalPort() != 0) {
+                HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(sendRtpItem.getSsrc(), null, mediaInfo.getId());
+                hookSubscribe.removeSubscribe(hookSubscribeForRtpServerTimeout);
+                if (zlmrtpServerFactory.releasePort(mediaInfo, sendRtpItem.getSsrc())) {
+                    if (sendRtpItem.isTcpActive()) {
+                        startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpPassive(mediaInfo, param);
+                    } else {
+                        param.put("dst_url", sendRtpItem.getIp());
+                        param.put("dst_port", sendRtpItem.getPort());
+                        startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
+                    }
+                }
+            } else {
+                if (sendRtpItem.isTcpActive()) {
+                    startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpPassive(mediaInfo, param);
+                } else {
+                    param.put("dst_url", sendRtpItem.getIp());
+                    param.put("dst_port", sendRtpItem.getPort());
+                    startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
+                }
+            }
+            if (startSendRtpStreamResult != null) {
+                startSendRtpStreamHand(sendRtpItem, platform, startSendRtpStreamResult, param, callIdHeader);
+            }
+        }
+    }
+
+    @Override
+    public void startSendRtpStreamHand(SendRtpItem sendRtpItem, ParentPlatform parentPlatform,
+                                       JSONObject jsonObject, Map<String, Object> param, CallIdHeader callIdHeader) {
+        if (jsonObject == null) {
+            logger.error("RTP鎺ㄦ祦澶辫触: 璇锋鏌LM鏈嶅姟");
+        } else if (jsonObject.getInteger("code") == 0) {
+            logger.info("璋冪敤ZLM鎺ㄦ祦鎺ュ彛, 缁撴灉锛� {}", jsonObject);
+            logger.info("RTP鎺ㄦ祦鎴愬姛[ {}/{} ]锛寋}->{}:{}, ", param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port"));
+        } else {
+            logger.error("RTP鎺ㄦ祦澶辫触: {}, 鍙傛暟锛歿}", jsonObject.getString("msg"), JSON.toJSONString(param));
+            if (sendRtpItem.isOnlyAudio()) {
+                Device device = deviceService.getDevice(sendRtpItem.getDeviceId());
+                AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
+                if (audioBroadcastCatch != null) {
+                    try {
+                        cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);
+                    } catch (SipException | ParseException | InvalidArgumentException |
+                             SsrcTransactionNotFoundException e) {
+                        logger.error("[鍛戒护鍙戦�佸け璐 鍋滄璇煶瀵硅: {}", e.getMessage());
+                    }
+                }
+            } else {
+                // 鍚戜笂绾у钩鍙�
+                try {
+                    commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId());
+                } catch (SipException | InvalidArgumentException | ParseException e) {
+                    logger.error("[鍛戒护鍙戦�佸け璐 鍥芥爣绾ц仈 鍙戦�丅YE: {}", e.getMessage());
+                }
+            }
+        }
+    }
+
+    @Override
+    public void talkCmd(Device device, String channelId, MediaServerItem mediaServerItem, String stream, AudioBroadcastEvent event) {
+        if (device == null || channelId == null) {
+            return;
+        }
+        // TODO 蹇呴』澶氱鍙fā寮忔墠鏀寔璇煶鍠婅瘽楣よ闊冲璁�
+        logger.info("[璇煶瀵硅] device锛� {}, channel: {}", device.getDeviceId(), channelId);
+        DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId);
+        if (deviceChannel == null) {
+            logger.warn("寮�鍚闊冲璁茬殑鏃跺�欐湭鎵惧埌閫氶亾锛� {}", channelId);
+            event.call("寮�鍚闊冲璁茬殑鏃跺�欐湭鎵惧埌閫氶亾");
+            return;
+        }
+        // 鏌ヨ閫氶亾浣跨敤鐘舵��
+        if (audioBroadcastManager.exit(device.getDeviceId(), channelId)) {
+            SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null);
+            if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) {
+                // 鏌ヨ娴佹槸鍚﹀瓨鍦紝涓嶅瓨鍦ㄥ垯璁や负鏄紓甯哥姸鎬�
+                MediaServerItem mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+                Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServer, sendRtpItem.getApp(), sendRtpItem.getStream());
+                if (streamReady) {
+                    logger.warn("[璇煶瀵硅] 姝e湪璇煶骞挎挱锛屾棤娉曞紑鍚闊抽�氳瘽锛� {}", channelId);
+                    event.call("姝e湪璇煶骞挎挱");
+                    return;
+                } else {
+                    stopAudioBroadcast(device.getDeviceId(), channelId);
+                }
+            }
+        }
+
+        SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, stream, null);
+        if (sendRtpItem != null) {
+            MediaServerItem mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+            Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServer, "rtp", sendRtpItem.getReceiveStream());
+            if (streamReady) {
+                logger.warn("[璇煶瀵硅] 杩涜涓細 {}", channelId);
+                event.call("璇煶瀵硅杩涜涓�");
+                return;
+            } else {
+                stopTalk(device, channelId);
+            }
+        }
+
+        talk(mediaServerItem, device, channelId, stream, (MediaServerItem mediaServerItem1, JSONObject response) -> {
+            logger.info("[璇煶瀵硅] 鏀跺埌璁惧鍙戞潵鐨勬祦");
+        }, eventResult -> {
+            logger.warn("[璇煶瀵硅] 澶辫触锛寋}/{}, 閿欒鐮� {} {}", device.getDeviceId(), channelId, eventResult.statusCode, eventResult.msg);
+            event.call("澶辫触锛岄敊璇爜 " + eventResult.statusCode + ", " + eventResult.msg);
+        }, () -> {
+            logger.warn("[璇煶瀵硅] 澶辫触锛寋}/{} 瓒呮椂", device.getDeviceId(), channelId);
+            event.call("澶辫触锛岃秴鏃� ");
+            stopTalk(device, channelId);
+        }, errorMsg -> {
+            logger.warn("[璇煶瀵硅] 澶辫触锛寋}/{} {}", device.getDeviceId(), channelId, errorMsg);
+            event.call(errorMsg);
+            stopTalk(device, channelId);
+        });
+    }
+
+    private void stopTalk(Device device, String channelId) {
+        stopTalk(device, channelId, null);
+    }
+
+    @Override
+    public void stopTalk(Device device, String channelId, Boolean streamIsReady) {
+        logger.info("[璇煶瀵硅] 鍋滄锛� {}/{}", device.getDeviceId(), channelId);
+        SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null);
+        if (sendRtpItem == null) {
+            logger.info("[璇煶瀵硅] 鍋滄澶辫触锛� 鏈壘鍒板彂閫佷俊鎭紝鍙兘宸茬粡鍋滄");
+            return;
+        }
+        // 鍋滄鍚戣澶囨帹娴�
+        String mediaServerId = sendRtpItem.getMediaServerId();
+        if (mediaServerId == null) {
+            return;
+        }
+
+        MediaServerItem mediaServer = mediaServerService.getOne(mediaServerId);
+
+        if (streamIsReady == null || streamIsReady) {
+            Map<String, Object> param = new HashMap<>();
+            param.put("vhost", "__defaultVhost__");
+            param.put("app", sendRtpItem.getApp());
+            param.put("stream", sendRtpItem.getStream());
+            param.put("ssrc", sendRtpItem.getSsrc());
+            zlmrtpServerFactory.stopSendRtpStream(mediaServer, param);
+        }
+
+        mediaServer.getSsrcConfig().releaseSsrc(sendRtpItem.getSsrc());
+
+        SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, null, sendRtpItem.getStream());
+        if (ssrcTransaction != null) {
+            try {
+                cmder.streamByeCmd(device, channelId, sendRtpItem.getStream(), null);
+            } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException  e) {
+                logger.info("[璇煶瀵硅] 鍋滄娑堟伅鍙戦�佸け璐ワ紝鍙兘宸茬粡鍋滄");
+            }
+        }
+        redisCatchStorage.deleteSendRTPServer(device.getDeviceId(), channelId,null, null);
+    }
 }

--
Gitblit v1.8.0