648540858
2024-04-18 55a240bb4504baa9a75e44bc6cc597c96b80705d
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -10,8 +10,8 @@
import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
@@ -24,8 +24,8 @@
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
import com.genersoft.iot.vmp.service.*;
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.utils.DateUtil;
@@ -73,9 +73,6 @@
    private AudioBroadcastManager audioBroadcastManager;
    @Autowired
    private ZLMServerFactory zlmServerFactory;
    @Autowired
    private IPlayService playService;
    @Autowired
@@ -83,6 +80,10 @@
    @Autowired
    private IRedisCatchStorage redisCatchStorage;
    @Autowired
    private IRedisRpcService redisRpcService;
    @Autowired
    private IInviteStreamService inviteStreamService;
@@ -106,9 +107,6 @@
    private EventPublisher eventPublisher;
    @Autowired
    private ZLMMediaListManager zlmMediaListManager;
    @Autowired
    private ZlmHttpHookSubscribe subscribe;
    @Autowired
@@ -124,9 +122,6 @@
    private VideoStreamSessionManager sessionManager;
    @Autowired
    private AssistRESTfulUtils assistRESTfulUtils;
    @Autowired
    private SSRCFactory ssrcFactory;
    @Qualifier("taskExecutor")
@@ -135,6 +130,9 @@
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;
    @Autowired
    private IStreamPushService streamPushService;
    /**
     * 服务器定时上报时间,上报间隔可配置,默认10s上报一次
@@ -147,7 +145,7 @@
        taskExecutor.execute(() -> {
            List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
            if (subscribes != null && subscribes.size() > 0) {
            if (subscribes != null && !subscribes.isEmpty()) {
                for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
                    subscribe.response(null, param);
                }
@@ -166,7 +164,7 @@
    @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
    public HookResult onPlay(@RequestBody OnPlayHookParam param) {
        if (logger.isDebugEnabled()) {
            logger.debug("[ZLM HOOK] 播放鉴权:{}->{}" + param.getMediaServerId(), param);
            logger.debug("[ZLM HOOK] 播放鉴权:{}->{}", param.getMediaServerId(), param);
        }
        String mediaServerId = param.getMediaServerId();
@@ -242,21 +240,14 @@
                // 鉴权通过
                redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
            }
        } else {
            zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId());
        }
        HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
        result.setEnable_audio(true);
        taskExecutor.execute(() -> {
            ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
            if (subscribe != null) {
                if (mediaInfo != null) {
                    subscribe.response(mediaInfo, param);
                } else {
                    new HookResultForOnPublish(1, "zlm not register");
                }
                subscribe.response(mediaInfo, param);
            }
        });
@@ -267,10 +258,9 @@
            result.setEnable_mp4(userSetting.isRecordPushLive());
        }
        // 国标流
        if ("rtp".equals(param.getApp()) ) {
        if ("rtp".equals(param.getApp())) {
            InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
            // 单端口模式下修改流 ID
            if (!mediaInfo.isRtpEnable() && inviteInfo == null) {
                String ssrc = String.format("%010d", Long.parseLong(param.getStream(), 16));
@@ -278,6 +268,8 @@
                if (inviteInfo != null) {
                    result.setStream_replace(inviteInfo.getStream());
                    logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", param.getStream(), inviteInfo.getStream());
                    // 单端口模式下修改流ID为目标流ID,不然其他地方可能都无法对应
                    param.setStream(inviteInfo.getStream());
                }
            }
@@ -318,13 +310,17 @@
                    result.setEnable_audio(true);
                }
            }
        } else if (param.getApp().equals("broadcast")) {
            result.setEnable_audio(true);
        } else if (param.getApp().equals("talk")) {
            result.setEnable_audio(true);
        }
        if (param.getApp().equalsIgnoreCase("rtp")) {
            String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + "_" + param.getStream();
            OtherRtpSendInfo otherRtpSendInfo = (OtherRtpSendInfo)redisTemplate.opsForValue().get(receiveKey);
            OtherRtpSendInfo otherRtpSendInfo = (OtherRtpSendInfo) redisTemplate.opsForValue().get(receiveKey);
            String receiveKeyForPS = VideoManagerConstants.WVP_OTHER_RECEIVE_PS_INFO + userSetting.getServerId() + "_" + param.getStream();
            OtherPsSendInfo otherPsSendInfo = (OtherPsSendInfo)redisTemplate.opsForValue().get(receiveKeyForPS);
            OtherPsSendInfo otherPsSendInfo = (OtherPsSendInfo) redisTemplate.opsForValue().get(receiveKeyForPS);
            if (otherRtpSendInfo != null || otherPsSendInfo != null) {
                result.setEnable_mp4(true);
            }
@@ -347,13 +343,10 @@
            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(() -> {
            ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
            MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
            if (mediaInfo == null) {
                logger.info("[ZLM HOOK] 流变化未找到ZLM, {}", param.getMediaServerId());
                return;
@@ -379,7 +372,6 @@
                    redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
                }
            }
            if ("rtsp".equals(param.getSchema())) {
                logger.info("流变化:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream());
                if (param.isRegist()) {
@@ -474,8 +466,7 @@
                                    || param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
                                    || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
                                param.setSeverId(userSetting.getServerId());
                                zlmMediaListManager.addPush(param);
                                streamPushService.updatePush(param);
                                // 冗余数据,自己系统中自用
                                redisCatchStorage.addPushListItem(param.getApp(), param.getStream(), param);
                            }
@@ -492,15 +483,18 @@
                                }
                            }
                            GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
                            if (gbStream != null) {
//                           eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
                            // 查找是否关联了国标, 关联了不删除, 置为离线
                            if (gbStream == null) {
                                storager.removeMedia(param.getApp(), param.getStream());
                            }else {
//                                eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
                                storager.mediaOffline(param.getApp(), param.getStream());
                            }
                            zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
                        }
                        GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
                        if (gbStream != null) {
                            if (userSetting.isUsePushingAsStatus()) {
                                eventPublisher.catalogEventPublishForStream(null, gbStream, param.isRegist()?CatalogEvent.ON:CatalogEvent.OFF);
                                eventPublisher.catalogEventPublishForStream(null, gbStream, param.isRegist() ? CatalogEvent.ON : CatalogEvent.OFF);
                            }
                        }
                        if (type != null) {
@@ -519,38 +513,42 @@
                    List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
                    if (!sendRtpItems.isEmpty()) {
                        for (SendRtpItem sendRtpItem : sendRtpItems) {
                            if (sendRtpItem != null && sendRtpItem.getApp().equals(param.getApp())) {
                                String platformId = sendRtpItem.getPlatformId();
                                ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
                                Device device = deviceService.getDevice(platformId);
                                    try {
                                        if (platform != null) {
                                            commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
                                            redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(),
                                                    sendRtpItem.getCallId(), sendRtpItem.getStream());
                                        } else {
                                            cmder.streamByeCmd(device, sendRtpItem.getChannelId(), param.getStream(), sendRtpItem.getCallId());
                                            if (sendRtpItem.getPlayType().equals(InviteStreamType.BROADCAST)
                                                    || sendRtpItem.getPlayType().equals(InviteStreamType.TALK)) {
                                                AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
                                                if (audioBroadcastCatch != null) {
                                                    // 来自上级平台的停止对讲
                                                    logger.info("[停止对讲] 来自上级,平台:{}, 通道:{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
                                                    audioBroadcastManager.del(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
                                                }
                            if (sendRtpItem == null) {
                                continue;
                            }
                            if (sendRtpItem.getApp().equals(param.getApp())) {
                                // 在hook收到这个消息,说明发流一定是本级完成的。
                                redisCatchStorage.deleteSendRTPServer(sendRtpItem);
                                ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
                                ParentPlatform platform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
                                Device device = deviceService.getDevice(sendRtpItem.getPlatformId());
                                try {
                                    if (platform != null) {
                                        commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
                                        redisCatchStorage.sendPlatformStopPlayMsg(sendRtpItem, platform);
                                    } else if (device != null) {
                                        cmder.streamByeCmd(device, sendRtpItem.getChannelId(), param.getStream(), sendRtpItem.getCallId());
                                        if (sendRtpItem.getPlayType().equals(InviteStreamType.BROADCAST)
                                                || sendRtpItem.getPlayType().equals(InviteStreamType.TALK)) {
                                            AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
                                            if (audioBroadcastCatch != null) {
                                                // 来自上级平台的停止对讲
                                                logger.info("[停止对讲] 来自上级,平台:{}, 通道:{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
                                                audioBroadcastManager.del(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
                                            }
                                        }
                                    } catch (SipException | InvalidArgumentException | ParseException |
                                             SsrcTransactionNotFoundException e) {
                                        logger.error("[命令发送失败] 发送BYE: {}", e.getMessage());
                                    }else {
                                        // 通知其他wvp停止发流
                                        redisRpcService.rtpSendStopped(sendRtpItem.getRedisKey());
                                    }
                                } catch (SipException | InvalidArgumentException | ParseException |
                                         SsrcTransactionNotFoundException e) {
                                    logger.error("[命令发送失败] 发送BYE: {}", e.getMessage());
                                }
                            }
                        }
                    }
                }
            }
        });
        return HookResult.SUCCESS();
@@ -581,9 +579,9 @@
                }
                // 收到无人观看说明流也没有在往上级推送
                if (redisCatchStorage.isChannelSendingRTP(inviteInfo.getChannelId())) {
                    List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByChnnelId(
                    List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByChannelId(
                            inviteInfo.getChannelId());
                    if (sendRtpItems.size() > 0) {
                    if (!sendRtpItems.isEmpty()) {
                        for (SendRtpItem sendRtpItem : sendRtpItems) {
                            ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
                            try {
@@ -591,14 +589,10 @@
                            } catch (SipException | InvalidArgumentException | ParseException e) {
                                logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
                            }
                            redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
                                    sendRtpItem.getCallId(), sendRtpItem.getStream());
                            redisCatchStorage.deleteSendRTPServer(sendRtpItem);
                            ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
                            if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) {
                                MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
                                        sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
                                        sendRtpItem.getPlatformId(), parentPlatform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
                                messageForPushChannel.setPlatFormIndex(parentPlatform.getId());
                                redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel);
                                redisCatchStorage.sendPlatformStopPlayMsg(sendRtpItem, parentPlatform);
                            }
                        }
                    }
@@ -612,14 +606,14 @@
                        if (info != null) {
                            cmder.streamByeCmd(device, inviteInfo.getChannelId(),
                                    inviteInfo.getStream(), null);
                        }else {
                        } else {
                            logger.info("[无人观看] 未找到设备的点播信息: {}, 流:{}", inviteInfo.getDeviceId(), param.getStream());
                        }
                    } catch (InvalidArgumentException | ParseException | SipException |
                             SsrcTransactionNotFoundException e) {
                        logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage());
                    }
                }else {
                } else {
                    logger.info("[无人观看] 未找到设备: {},流:{}", inviteInfo.getDeviceId(), param.getStream());
                }
@@ -729,7 +723,7 @@
                    });
                }
                return result;
            }else if(s.length == 4){
            } else if (s.length == 4) {
                // 此时为录像回放, 录像回放格式为> 设备ID_通道ID_开始时间_结束时间
                String startTimeStr = s[2];
                String endTimeStr = s[3];
@@ -763,14 +757,14 @@
                if (!exist) {
                    SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaInfo, param.getStream(), null,
                            device.isSsrcCheck(),  true, 0, false, false, device.getStreamModeForParam());
                            device.isSsrcCheck(), true, 0, false, false, device.getStreamModeForParam());
                    playService.playBack(mediaInfo, ssrcInfo, deviceId, channelId, startTime, endTime, (code, message, data) -> {
                        msg.setData(new HookResult(code, message));
                        resultHolder.invokeResult(msg);
                    });
                }
                return result;
            }else {
            } else {
                defaultResult.setResult(HookResult.SUCCESS());
                return defaultResult;
            }
@@ -800,7 +794,7 @@
        logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId());
        taskExecutor.execute(() -> {
            List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
            if (subscribes != null && subscribes.size() > 0) {
            if (subscribes != null && !subscribes.isEmpty()) {
                for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
                    subscribe.response(null, zlmServerConfig);
                }
@@ -826,17 +820,18 @@
        }
        taskExecutor.execute(() -> {
            List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
            if (sendRtpItems.size() > 0) {
            if (!sendRtpItems.isEmpty()) {
                for (SendRtpItem sendRtpItem : sendRtpItems) {
                    ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
                    ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
                    try {
                        commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
                    } catch (SipException | InvalidArgumentException | ParseException e) {
                        logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
                    if(parentPlatform != null) {
                        try {
                            commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
                        } catch (SipException | InvalidArgumentException | ParseException e) {
                            logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
                        }
                    }
                    redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
                            sendRtpItem.getCallId(), sendRtpItem.getStream());
                    ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
                    redisCatchStorage.deleteSendRTPServer(sendRtpItem);
                }
            }
        });
@@ -849,12 +844,11 @@
     */
    @ResponseBody
    @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")
    public HookResult onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam
    public HookResult onRtpServerTimeout(@RequestBody OnRtpServerTimeoutHookParam
            param) {
        logger.info("[ZLM HOOK] rtpServer收流超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
        taskExecutor.execute(() -> {
            JSONObject json = (JSONObject) JSON.toJSON(param);
            List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
            if (subscribes != null && !subscribes.isEmpty()) {
                for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {