648540858
2023-04-14 d46fc9de827fe85a48f447cf1550444573a6f1a5
优化下级平台自定义ssrc的情况,优化国标录像下载流程
8个文件已修改
1个文件已添加
161 ■■■■ 已修改文件
src/main/java/com/genersoft/iot/vmp/common/CommonCallback.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java 93 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/dialog/recordDownload.vue 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/common/CommonCallback.java
New file
@@ -0,0 +1,5 @@
package com.genersoft.iot.vmp.common;
public interface CommonCallback<T>{
    public void run(T t);
}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -546,7 +546,7 @@
        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId());
        // 添加订阅
        CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
        String callId=newCallIdHeader.getCallId();
        String callId= newCallIdHeader.getCallId();
        subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
            logger.debug("sipc 添加订阅===callId {}",callId);
            hookEvent.call(new InviteStreamInfo(mediaServerItem, json,callId, "rtp", ssrcInfo.getStream()));
@@ -558,7 +558,7 @@
                    (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd) -> {
                        logger.info("[录像]下载结束, 发送BYE");
                        try {
                            streamByeCmd(device, channelId, ssrcInfo.getStream(),callId);
                            streamByeCmd(device, channelId, ssrcInfo.getStream(), callId);
                        } catch (InvalidArgumentException | ParseException | SipException |
                                 SsrcTransactionNotFoundException e) {
                            logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage());
@@ -580,8 +580,6 @@
            if (ssrcIndex >= 0) {
                ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
            }
            logger.debug("接收到的下载响应ssrc====>{}",ssrcInfo.getSsrc());
            logger.debug("接收到的下载响应ssrc====>{}",ssrc);
            streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download);
            okEvent.response(event);
        });
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java
@@ -12,6 +12,9 @@
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.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import gov.nist.javax.sip.message.SIPRequest;
@@ -58,6 +61,9 @@
    @Autowired
    private VideoStreamSessionManager sessionManager;
    @Autowired
    private ZlmHttpHookSubscribe subscribe;
    @Override
    public void afterPropertiesSet() throws Exception {
        notifyMessageHandler.addHandler(cmdType, this);
@@ -93,6 +99,9 @@
                } catch (InvalidArgumentException | ParseException | SsrcTransactionNotFoundException | SipException e) {
                    logger.error("[录像流]推送完毕,收到关流通知, 发送BYE失败 {}", e.getMessage());
                }
                // 去除监听流注销自动停止下载的监听
                HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcTransaction.getStream(), false, "rtsp", ssrcTransaction.getMediaServerId());
                subscribe.removeSubscribe(hookSubscribe);
                // 如果级联播放,需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题,需要将点播CallId进行上下级绑定
                SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(null, ssrcTransaction.getChannelId(), null, null);
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -276,6 +276,10 @@
        return sendPost(mediaServerItem, "closeRtpServer",param, null);
    }
    public void closeRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param, RequestCallback callback) {
        sendPost(mediaServerItem, "closeRtpServer",param, callback);
    }
    public JSONObject listRtpServer(MediaServerItem mediaServerItem) {
        return sendPost(mediaServerItem, "listRtpServer",null, null);
    }
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
@@ -3,6 +3,7 @@
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.media.zlm.dto.*;
@@ -164,6 +165,31 @@
        return result;
    }
    public void closeRtpServer(MediaServerItem serverItem, String streamId, CommonCallback<Boolean> callback) {
        if (serverItem == null) {
            callback.run(false);
            return;
        }
        Map<String, Object> param = new HashMap<>();
        param.put("stream_id", streamId);
        zlmresTfulUtils.closeRtpServer(serverItem, param, jsonObject -> {
            if (jsonObject != null ) {
                if (jsonObject.getInteger("code") == 0) {
                    callback.run(jsonObject.getInteger("hit") == 1);
                    return;
                }else {
                    logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg"));
                }
            }else {
                //  检查ZLM状态
                logger.error("关闭RTP Server 失败: 请检查ZLM服务");
            }
            callback.run(false);
        });
    }
    /**
     * 创建一个国标推流
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
@@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.service;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData;
@@ -51,6 +52,8 @@
    void closeRTPServer(MediaServerItem mediaServerItem, String streamId);
    void closeRTPServer(MediaServerItem mediaServerItem, String streamId, CommonCallback<Boolean> callback);
    void closeRTPServer(String mediaServerId, String streamId);
    void clearRTPServer(MediaServerItem mediaServerItem);
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -3,6 +3,7 @@
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.SipConfig;
@@ -173,6 +174,15 @@
    }
    @Override
    public void closeRTPServer(MediaServerItem mediaServerItem, String streamId, CommonCallback<Boolean> callback) {
        if (mediaServerItem == null) {
            callback.run(false);
            return;
        }
        zlmrtpServerFactory.closeRtpServer(mediaServerItem, streamId, callback);
    }
    @Override
    public void closeRTPServer(String mediaServerId, String streamId) {
        MediaServerItem mediaServerItem = this.getOne(mediaServerId);
        closeRTPServer(mediaServerItem, streamId);
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
@@ -328,9 +328,30 @@
                            });
                        }
                        // 关闭rtp server
                        mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
                        // 重新开启ssrc server
                        mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, ssrcInfo.getPort());
                        mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream(), result->{
                            if (result) {
                                // 重新开启ssrc server
                                mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, ssrcInfo.getPort());
                            }else {
                                try {
                                    logger.warn("[停止点播] {}/{}", device.getDeviceId(), channelId);
                                    cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null);
                                } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
                                    logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage());
                                    throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
                                }
                                dynamicTask.stop(timeOutTaskKey);
                                // 释放ssrc
                                mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
                                streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
                                event.msg = "下级自定义了ssrc,重新设置收流信息失败";
                                event.statusCode = 500;
                                errorEvent.response(event);
                            }
                        });
                    }
                }
@@ -472,7 +493,7 @@
        if (device == null) {
            throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备: " + deviceId + "不存在");
        }
        logger.info("[回放消息] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
        PlayBackResult<StreamInfo> playBackResult = new PlayBackResult<>();
        String playBackTimeOutTaskKey = UUID.randomUUID().toString();
        dynamicTask.startDelay(playBackTimeOutTaskKey, () -> {
@@ -546,6 +567,7 @@
                                    if (!ssrcFactory.checkSsrc(mediaServerItem.getId(),ssrcInResponse)) {
                                        // ssrc 不可用
                                        // 释放ssrc
                                        dynamicTask.stop(playBackTimeOutTaskKey);
                                        mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
                                        streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
                                        eventResult.msg = "下级自定义了ssrc,但是此ssrc不可用";
@@ -568,10 +590,31 @@
                                            hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream()));
                                        });
                                    }
                                    // 关闭rtp server
                                    mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
                                    // 重新开启ssrc server
                                    mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
                                    mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream(), result->{
                                        if (result) {
                                            // 重新开启ssrc server
                                            mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
                                        }else {
                                            try {
                                                logger.warn("[回放消息]停止 {}/{}", device.getDeviceId(), channelId);
                                                cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null);
                                            } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
                                                logger.error("[命令发送失败] 停止点播 停止, 发送BYE: {}", e.getMessage());
                                                throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
                                            }
                                            dynamicTask.stop(playBackTimeOutTaskKey);
                                            // 释放ssrc
                                            mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
                                            streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
                                            errorEvent.response(eventResult);
                                            eventResult.msg = "下级自定义了ssrc,重新设置收流信息失败";
                                            eventResult.statusCode = 500;
                                            errorEvent.response(eventResult);
                                        }
                                    });
                                }
                            }
                        }
@@ -619,7 +662,7 @@
            throw new ControllerException(ErrorCode.ERROR400.getCode(), "设备:" + deviceId + "不存在");
        }
        PlayBackResult<StreamInfo> downloadResult = new PlayBackResult<>();
        logger.info("[录像下载] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
        String downLoadTimeOutTaskKey = UUID.randomUUID().toString();
        dynamicTask.startDelay(downLoadTimeOutTaskKey, () -> {
            logger.warn(String.format("录像下载请求超时,deviceId:%s ,channelId:%s", deviceId, channelId));
@@ -648,7 +691,7 @@
            streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
        };
        InviteStreamCallback hookEvent = (InviteStreamInfo inviteStreamInfo) -> {
            logger.info("收到订阅消息: " + inviteStreamInfo.getCallId());
            logger.info("[录像下载]收到订阅消息: " + inviteStreamInfo.getCallId());
            dynamicTask.stop(downLoadTimeOutTaskKey);
            StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
            streamInfo.setStartTime(startTime);
@@ -678,9 +721,9 @@
                                if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
                                    return;
                                }
                                logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
                                logger.info("[录像下载] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
                                if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
                                    logger.info("[回放消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
                                    logger.info("[录像下载] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
                                    if (!ssrcFactory.checkSsrc(mediaServerItem.getId(),ssrcInResponse)) {
                                        // ssrc 不可用
@@ -707,14 +750,34 @@
                                            hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream()));
                                        });
                                    }
                                    // 关闭rtp server
                                    mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
                                    // 重新开启ssrc server
                                    mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
                                    mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream(), result->{
                                        if (result) {
                                            // 重新开启ssrc server
                                            mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
                                        }else {
                                            try {
                                                logger.warn("[录像下载] 停止{}/{}", device.getDeviceId(), channelId);
                                                cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null);
                                            } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
                                                logger.error("[命令发送失败] 录像下载停止, 发送BYE: {}", e.getMessage());
                                                throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
                                            }
                                            dynamicTask.stop(downLoadTimeOutTaskKey);
                                            // 释放ssrc
                                            mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
                                            streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
                                            eventResult.msg = "下级自定义了ssrc,重新设置收流信息失败";
                                            eventResult.statusCode = 500;
                                            errorEvent.response(eventResult);
                                        }
                                    });
                                }
                            }
                        }
                    });
        } catch (InvalidArgumentException | SipException | ParseException e) {
            logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
web_src/src/components/dialog/recordDownload.vue
@@ -96,7 +96,10 @@
          });
        },
        close: function (){
          this.stopDownloadRecord();
          if (this.streamInfo.progress < 1) {
            this.stopDownloadRecord();
          }
          if (this.timer !== null) {
            window.clearTimeout(this.timer);
            this.timer = null;