优化子码流切换逻辑,去除主子多开逻辑。优化单端口模式ssrc变化导致的无法移出点播缓存 #895
20个文件已修改
789 ■■■■ 已修改文件
src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java 64 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java 176 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java 359 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java
@@ -29,7 +29,7 @@
    private StreamInfo streamInfo;
    public static InviteInfo getinviteInfo(String deviceId, String channelId, String stream, SSRCInfo ssrcInfo,
    public static InviteInfo getInviteInfo(String deviceId, String channelId, String stream, SSRCInfo ssrcInfo,
                                           String receiveIp, Integer receivePort, String streamMode,
                                           InviteSessionType type, InviteSessionStatus status) {
        InviteInfo inviteInfo = new InviteInfo();
@@ -138,25 +138,6 @@
        this.subStream = subStream;
    }
    public static InviteInfo getInviteInfo(String deviceId, String channelId,Boolean isSubStream, String stream, SSRCInfo ssrcInfo,
                                           String receiveIp, Integer receivePort, String streamMode,
                                           InviteSessionType type, InviteSessionStatus status) {
        InviteInfo inviteInfo = new InviteInfo();
        inviteInfo.setDeviceId(deviceId);
        inviteInfo.setChannelId(channelId);
        inviteInfo.setStream(stream);
        inviteInfo.setSsrcInfo(ssrcInfo);
        inviteInfo.setReceiveIp(receiveIp);
        inviteInfo.setReceivePort(receivePort);
        inviteInfo.setStreamMode(streamMode);
        inviteInfo.setType(type);
        inviteInfo.setStatus(status);
        if(isSubStream != null){
            inviteInfo.setSubStream(isSubStream);
        }
        return inviteInfo;
    }
    /*=========================设备主子码流逻辑END====================*/
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
@@ -542,17 +542,5 @@
        this.subStream = subStream;
    }
    public static String getPlayStream(String deviceId,String channelId,boolean isSubStream){
        String streamId;
        if(isSubStream){
            streamId = String.format("%s_%s_%s","sub",deviceId, channelId);
        }else {
            streamId = String.format("%s_%s_%s","main", deviceId, channelId);
        }
        return streamId;
    }
    /*=========================设备主子码流逻辑END====================*/
}
src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java
@@ -45,7 +45,6 @@
     * @return
     */
    public void startCron(String key, Runnable task, int cycleForCatalog) {
        System.out.println(cycleForCatalog);
        ScheduledFuture<?> future = futureMap.get(key);
        if (future != null) {
            if (future.isCancelled()) {
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
@@ -156,29 +156,5 @@
        }
    }
    /*============================设备主子码流逻辑START========================*/
    public static String getPlayKey(String deviceId,String channelId,boolean deviceSwitchSubStream,boolean isSubStream){
        String key = null;
        if(deviceSwitchSubStream){
            key = CALLBACK_CMD_PLAY + isSubStream + deviceId + channelId;
        }else {
            key = CALLBACK_CMD_PLAY +deviceId + channelId;
        }
        return key;
    }
    public static String getSnapKey(String deviceId,String channelId,boolean deviceSwitchSubStream,boolean isSubStream){
        String key = null;
        if(deviceSwitchSubStream){
            key = CALLBACK_CMD_SNAP + isSubStream + deviceId + channelId;
        }else {
            key = CALLBACK_CMD_SNAP +deviceId + channelId;
        }
        return key;
    }
    /*============================设备主子码流逻辑END========================*/
}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
@@ -98,7 +98,7 @@
     * @param device  视频设备
     * @param channelId  预览通道
     */
    void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,boolean isSubStream, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
    void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
    /**
     * 请求回放视频流
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -268,7 +268,7 @@
     * @param errorEvent sip错误订阅
     */
    @Override
    public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,boolean isSubStream,
    public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
                              ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
        String stream = ssrcInfo.getStream();
@@ -345,13 +345,13 @@
        if( device.isSwitchPrimarySubStream() ){
            if("TP-LINK".equals(device.getManufacturer())){
                if (isSubStream){
                if (device.isSwitchPrimarySubStream()){
                    content.append("a=streamMode:sub\r\n");
                }else {
                    content.append("a=streamMode:main\r\n");
                }
            }else {
                if (isSubStream){
                if (device.isSwitchPrimarySubStream()){
                    content.append("a=streamprofile:1\r\n");
                }else {
                    content.append("a=streamprofile:0\r\n");
@@ -374,11 +374,8 @@
            // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值
            ResponseEvent responseEvent = (ResponseEvent) e.event;
            SIPResponse response = (SIPResponse) responseEvent.getResponse();
            if(device.isSwitchPrimarySubStream()){
                streamSession.put(device.getDeviceId(), channelId, "switch-play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY);
            }else {
                streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY);
            }
            streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response,
                    InviteSessionType.PLAY);
            okEvent.response(e);
        });
    }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
@@ -142,13 +142,8 @@
            // 可能是设备主动停止
            Device device = storager.queryVideoDeviceByChannelId(platformGbId);
            if (device != null) {
                SsrcTransaction ssrcTransactionForPlay = null;
                if (device.isSwitchPrimarySubStream() ) {
                    ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId,  "switch-play", null);
                } else {
                    storager.stopPlay(device.getDeviceId(), channelId);
                    ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null);
                }
                storager.stopPlay(device.getDeviceId(), channelId);
                SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null);
                if (ssrcTransactionForPlay != null){
                    if (ssrcTransactionForPlay.getCallId().equals(callIdHeader.getCallId())){
                        // 释放ssrc
@@ -158,16 +153,8 @@
                        }
                        streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlay.getStream());
                    }
                    InviteInfo inviteInfo = null;
                    if (device.isSwitchPrimarySubStream() ) {
                        String streamType = ssrcTransactionForPlay.getStream().split("_")[0];
                        boolean isSubStream = "sub".equals(streamType);
                        inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream);
                        inviteStreamService.removeInviteInfo(inviteInfo.getType(),inviteInfo.getDeviceId(),inviteInfo.getChannelId(),isSubStream,inviteInfo.getStream());
                    }else {
                        inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
                        inviteStreamService.removeInviteInfo(inviteInfo);
                    }
                    InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
                    inviteStreamService.removeInviteInfo(inviteInfo);
                    if (inviteInfo != null) {
                        if (inviteInfo.getStreamInfo() != null) {
                            mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStream());
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -490,7 +490,7 @@
                        }
                        sendRtpItem.setStreamId(streamId);
                        redisCatchStorage.updateSendRTPSever(sendRtpItem);
                        playService.play(mediaServerItem, device.getDeviceId(), channelId,false, ((code, msg, data) -> {
                        playService.play(mediaServerItem, device.getDeviceId(), channelId, ((code, msg, data) -> {
                            if (code == InviteErrorCode.SUCCESS.getCode()){
                                hookEvent.run(code, msg, data);
                            }else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()){
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java
@@ -252,7 +252,6 @@
    }
    private void executeSave(){
        System.out.println("定时存储数据");
        executeSaveForUpdate();
        executeSaveForDelete();
        executeSaveForOnline();
src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java
@@ -250,7 +250,6 @@
        if (ObjectUtils.isEmpty(timeStr)){
            return null;
        }
        System.out.println(timeStr);
        LocalDateTime localDateTime;
        try {
            localDateTime = LocalDateTime.parse(timeStr);
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -343,19 +343,10 @@
                }
                if ("rtp".equals(param.getApp()) && !param.isRegist()) {
                    if(param.getStream().split("_").length == 3){
                        boolean isSubStream = "sub".equals(param.getStream().split("_")[0]);
                        InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream(), isSubStream);
                        if(inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY )){
                            inviteStreamService.removeInviteInfo(inviteInfo.getType(),inviteInfo.getDeviceId(),
                                    inviteInfo.getChannelId(),inviteInfo.isSubStream(),inviteInfo.getStream());
                        }
                    }else {
                        InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
                        if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) {
                            inviteStreamService.removeInviteInfo(inviteInfo);
                            storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
                        }
                    InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
                    if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) {
                        inviteStreamService.removeInviteInfo(inviteInfo);
                        storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
                    }
                } else {
                    if (!"rtp".equals(param.getApp())) {
@@ -483,15 +474,8 @@
                Device device = deviceService.getDevice(inviteInfo.getDeviceId());
                if (device != null) {
                    try {
                        InviteInfo info = null;
                        if(device.isSwitchPrimarySubStream()){
                            boolean isSubStream = "sub".equals(param.getStream().split("_")[0]);
                            info = inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(),isSubStream, inviteInfo.getStream());
                        }else {
                            info = inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream());
                        }
                        InviteInfo info = inviteStreamService.getInviteInfo(inviteInfo.getType(),
                                inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream());
                        if (info != null) {
                            cmder.streamByeCmd(device, inviteInfo.getChannelId(),
                                    inviteInfo.getStream(), null);
@@ -502,15 +486,9 @@
                    }
                }
                if(device.isSwitchPrimarySubStream()){
                    boolean isSubStream = "sub".equals(param.getStream().split("_")[0]);
                    inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
                            inviteInfo.getChannelId(),isSubStream, inviteInfo.getStream());
                }else {
                    inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
                            inviteInfo.getChannelId(), inviteInfo.getStream());
                    storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
                }
                inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
                        inviteInfo.getChannelId(), inviteInfo.getStream());
                storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
                return ret;
            }
        } else {
@@ -563,26 +541,12 @@
        if ("rtp".equals(param.getApp())) {
            String[] s = param.getStream().split("_");
            if (!mediaInfo.isRtpEnable() ) {
                defaultResult.setResult(HookResult.SUCCESS());
                return defaultResult;
            }else if(s.length != 2 && s.length != 3 ){
            if (!mediaInfo.isRtpEnable() || s.length != 2) {
                defaultResult.setResult(HookResult.SUCCESS());
                return defaultResult;
            }
            String deviceId = null;
            String channelId = null;
            boolean isSubStream = false;
            if (s[0].length() < 20) {
                if ("sub".equals(s[0])) {
                    isSubStream = true;
                }
                deviceId = s[1];
                channelId = s[2];
            } else {
                deviceId = s[0];
                channelId = s[1];
            }
            String deviceId = s[0];
            String channelId = s[1];
            Device device = redisCatchStorage.getDevice(deviceId);
            if (device == null) {
                defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
@@ -596,7 +560,7 @@
            logger.info("[ZLM HOOK] 流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
            RequestMessage msg = new RequestMessage();
            String key = DeferredResultHolder.getPlayKey(deviceId, channelId, device.isSwitchPrimarySubStream(), isSubStream);
            String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
            boolean exist = resultHolder.exist(key, null);
            msg.setKey(key);
            String uuid = UUID.randomUUID().toString();
@@ -614,7 +578,7 @@
            resultHolder.put(key, uuid, result);
            if (!exist) {
                playService.play(mediaInfo, deviceId, channelId,isSubStream, (code, message, data) -> {
                playService.play(mediaInfo, deviceId, channelId, (code, message, data) -> {
                    msg.setData(new HookResult(code, message));
                    resultHolder.invokeResult(msg);
                });
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -270,11 +270,6 @@
    }
    public JSONObject openRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param){
        System.out.println("==============openRtpServer=================");
        for (String s : param.keySet()) {
            System.out.println(s + "-->" + param.get(s));
        }
        System.out.println("===============================");
        return sendPost(mediaServerItem, "openRtpServer",param, null);
    }
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
@@ -227,7 +227,6 @@
        int localPort = 0;
        if (userSetting.getGbSendStreamStrict()) {
            if (userSetting.getGbSendStreamStrict()) {
                System.out.println("createSendRtpItem1");
                localPort = keepPort(serverItem, ssrc, localPort);
                if (localPort == 0) {
                    return null;
@@ -264,7 +263,6 @@
        // 默认为随机端口
        int localPort = 0;
        if (userSetting.getGbSendStreamStrict()) {
            System.out.println("createSendRtpItem2");
            localPort = keepPort(serverItem, ssrc, localPort);
            if (localPort == 0) {
                return null;
@@ -294,9 +292,7 @@
        param.put("port", localPort);
        param.put("enable_tcp", 1);
        param.put("stream_id", ssrc);
        System.out.println("用于收流");
        JSONObject jsonObject = zlmresTfulUtils.openRtpServer(serverItem, param);
        System.out.println(jsonObject);
        if (jsonObject.getInteger("code") == 0) {
            localPort = jsonObject.getInteger("port");
            HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId());
src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java
@@ -4,8 +4,6 @@
import com.genersoft.iot.vmp.common.InviteSessionType;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import java.util.List;
/**
 * 记录国标点播的状态,包括实时预览,下载,录像回放
 */
@@ -15,6 +13,8 @@
     * 更新点播的状态信息
     */
    void updateInviteInfo(InviteInfo inviteInfo);
    InviteInfo updateInviteInfoForStream(InviteInfo inviteInfo, String stream);
    /**
     * 获取点播的状态信息
@@ -72,49 +72,6 @@
     * 统计同一个zlm下的国标收流个数
     */
    int getStreamInfoCount(String mediaServerId);
    /*======================设备主子码流逻辑START=========================*/
    /**
     * 获取点播的状态信息
     */
    InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type,
                                               String deviceId,
                                               String channelId,boolean isSubStream);
    void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId,boolean isSubStream);
    InviteInfo getInviteInfo(InviteSessionType type,
                             String deviceId,
                             String channelId,
                             boolean isSubStream,
                             String stream);
    void removeInviteInfo(InviteSessionType type,
                          String deviceId,
                          String channelId,
                          boolean isSubStream,
                          String stream);
    void once(InviteSessionType type, String deviceId, String channelId,boolean isSubStream, String stream,  ErrorCallback<Object> callback);
    void call(InviteSessionType type, String deviceId, String channelId,boolean isSubStream, String stream,  int code, String msg, Object data);
    void updateInviteInfoSub(InviteInfo inviteInfo);
    /**
     * 获取点播的状态信息
     */
    InviteInfo getInviteInfoByStream(InviteSessionType type, String stream,boolean isSubStream);
    /**
     * 获取点播的状态信息
     */
    List<Object> getInviteInfos(InviteSessionType type,
                                String deviceId,
                                String channelId,
                                String stream);
    /*======================设备主子码流逻辑END=========================*/
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
@@ -16,9 +16,9 @@
 */
public interface IPlayService {
    void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,boolean isSubStream,
    void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
              ErrorCallback<Object> callback);
    SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId,boolean isSubStream, ErrorCallback<Object> callback);
    SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, ErrorCallback<Object> callback);
    MediaServerItem getNewMediaServerItem(Device device);
@@ -43,5 +43,5 @@
    void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException;
    void getSnap(String deviceId, String channelId, String fileName,boolean isSubStream, ErrorCallback errorCallback);
    void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback);
}
src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java
@@ -85,6 +85,24 @@
    }
    @Override
    public InviteInfo updateInviteInfoForStream(InviteInfo inviteInfo, String stream) {
        InviteInfo inviteInfoInDb = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream());
        if (inviteInfoInDb == null) {
            return null;
        }
        removeInviteInfo(inviteInfoInDb);
        String key = VideoManagerConstants.INVITE_PREFIX +
                "_" + inviteInfo.getType() +
                "_" + inviteInfo.getDeviceId() +
                "_" + inviteInfo.getChannelId() +
                "_" + stream;
        inviteInfoInDb.setStream(stream);
        redisTemplate.opsForValue().set(key, inviteInfoInDb);
        return inviteInfoInDb;
    }
    @Override
    public InviteInfo getInviteInfo(InviteSessionType type, String deviceId, String channelId, String stream) {
        String key = VideoManagerConstants.INVITE_PREFIX +
                "_" + (type != null ? type : "*") +
@@ -152,19 +170,6 @@
    }
    @Override
    public void call(InviteSessionType type, String deviceId, String channelId, String stream, int code, String msg, Object data) {
        String key = buildKey(type, deviceId, channelId, stream);
        List<ErrorCallback<Object>> callbacks = inviteErrorCallbackMap.get(key);
        if (callbacks == null) {
            return;
        }
        for (ErrorCallback<Object> callback : callbacks) {
            callback.run(code, msg, data);
        }
        inviteErrorCallbackMap.remove(key);
    }
    private String buildKey(InviteSessionType type, String deviceId, String channelId, String stream) {
        String key = type + "_" +  deviceId + "_" + channelId;
        // 如果ssrc未null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite
@@ -199,69 +204,9 @@
        return count;
    }
    /*======================设备主子码流逻辑START=========================*/
    @Override
    public InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, String deviceId, String channelId, boolean isSubStream) {
        return getInviteInfo(type, deviceId, channelId,isSubStream, null);
    }
    @Override
    public void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId, boolean isSubStream) {
        removeInviteInfo(inviteSessionType, deviceId, channelId,isSubStream, null);
    }
    @Override
    public InviteInfo getInviteInfo(InviteSessionType type, String deviceId, String channelId,boolean isSubStream, String stream) {
        String key = VideoManagerConstants.INVITE_PREFIX +
                "_" + (type != null ? type : "*") +
                "_" + (isSubStream ? "sub" : "main") +
                "_" + (deviceId != null ? deviceId : "*") +
                "_" + (channelId != null ? channelId : "*") +
                "_" + (stream != null ? stream : "*");
        List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
        if (scanResult.size() != 1) {
            return null;
        }
        return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0));
    }
    @Override
    public void removeInviteInfo(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream) {
        String scanKey = VideoManagerConstants.INVITE_PREFIX +
                "_" + (type != null ? type : "*") +
                "_" + (isSubStream ? "sub" : "main") +
                "_" + (deviceId != null ? deviceId : "*") +
                "_" + (channelId != null ? channelId : "*") +
                "_" + (stream != null ? stream : "*");
        List<Object> scanResult = RedisUtil.scan(redisTemplate, scanKey);
        if (scanResult.size() > 0) {
            for (Object keyObj : scanResult) {
                String key = (String) keyObj;
                InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(key);
                if (inviteInfo == null) {
                    continue;
                }
                redisTemplate.delete(key);
                inviteErrorCallbackMap.remove(buildKey(type, deviceId, channelId, inviteInfo.getStream()));
            }
        }
    }
    @Override
    public void once(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream, ErrorCallback<Object> callback) {
        String key = buildSubStreamKey(type, deviceId, channelId,isSubStream, stream);
        List<ErrorCallback<Object>> callbacks = inviteErrorCallbackMap.get(key);
        if (callbacks == null) {
            callbacks = new CopyOnWriteArrayList<>();
            inviteErrorCallbackMap.put(key, callbacks);
        }
        callbacks.add(callback);
    }
    @Override
    public void call(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream, int code, String msg, Object data) {
        String key = buildSubStreamKey(type, deviceId, channelId,isSubStream, stream);
    public void call(InviteSessionType type, String deviceId, String channelId, String stream, int code, String msg, Object data) {
        String key = buildSubStreamKey(type, deviceId, channelId, stream);
        List<ErrorCallback<Object>> callbacks = inviteErrorCallbackMap.get(key);
        if (callbacks == null) {
            return;
@@ -273,89 +218,12 @@
    }
    private String buildSubStreamKey(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream) {
        String key = type + "_" + (isSubStream ? "sub":"main") + "_" +  deviceId + "_" + channelId;
    private String buildSubStreamKey(InviteSessionType type, String deviceId, String channelId, String stream) {
        String key = type + "_" + "_" +  deviceId + "_" + channelId;
        // 如果ssrc为null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite
        if (stream != null) {
            key += ("_" + stream);
        }
        return key;
    }
    @Override
    public void updateInviteInfoSub(InviteInfo inviteInfo) {
        if (inviteInfo == null || (inviteInfo.getDeviceId() == null || inviteInfo.getChannelId() == null)) {
            logger.warn("[更新Invite信息],参数不全: {}", JSON.toJSON(inviteInfo));
            return;
        }
        InviteInfo inviteInfoForUpdate = null;
        if (InviteSessionStatus.ready == inviteInfo.getStatus()) {
            if (inviteInfo.getDeviceId() == null
                    || inviteInfo.getChannelId() == null
                    || inviteInfo.getType() == null
                    || inviteInfo.getStream() == null
            ) {
                return;
            }
            inviteInfoForUpdate = inviteInfo;
        } else {
            InviteInfo inviteInfoInRedis = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
                    inviteInfo.getChannelId(),inviteInfo.isSubStream(), inviteInfo.getStream());
            if (inviteInfoInRedis == null) {
                logger.warn("[更新Invite信息],未从缓存中读取到Invite信息: deviceId: {}, channel: {}, stream: {}",
                        inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream());
                return;
            }
            if (inviteInfo.getStreamInfo() != null) {
                inviteInfoInRedis.setStreamInfo(inviteInfo.getStreamInfo());
            }
            if (inviteInfo.getSsrcInfo() != null) {
                inviteInfoInRedis.setSsrcInfo(inviteInfo.getSsrcInfo());
            }
            if (inviteInfo.getStreamMode() != null) {
                inviteInfoInRedis.setStreamMode(inviteInfo.getStreamMode());
            }
            if (inviteInfo.getReceiveIp() != null) {
                inviteInfoInRedis.setReceiveIp(inviteInfo.getReceiveIp());
            }
            if (inviteInfo.getReceivePort() != null) {
                inviteInfoInRedis.setReceivePort(inviteInfo.getReceivePort());
            }
            if (inviteInfo.getStatus() != null) {
                inviteInfoInRedis.setStatus(inviteInfo.getStatus());
            }
            inviteInfoForUpdate = inviteInfoInRedis;
        }
        String key = VideoManagerConstants.INVITE_PREFIX +
                "_" + inviteInfoForUpdate.getType() +
                "_" + (inviteInfoForUpdate.isSubStream() ? "sub":"main") +
                "_" + inviteInfoForUpdate.getDeviceId() +
                "_" + inviteInfoForUpdate.getChannelId() +
                "_" + inviteInfoForUpdate.getStream();
        redisTemplate.opsForValue().set(key, inviteInfoForUpdate);
    }
    @Override
    public InviteInfo getInviteInfoByStream(InviteSessionType type, String stream, boolean isSubStream) {
        return getInviteInfo(type, null, null,isSubStream, stream);
    }
    @Override
    public List<Object> getInviteInfos(InviteSessionType type, String deviceId, String channelId, String stream) {
        String key = VideoManagerConstants.INVITE_PREFIX +
                "_" + (type != null ? type : "*") +
                "_" + (deviceId != null ? deviceId : "*") +
                "_" + (channelId != null ? channelId : "*") +
                "_" + (stream != null ? stream : "*");
        List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
        return scanResult;
    }
    /*======================设备主子码流逻辑END=========================*/
}
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
@@ -116,43 +116,27 @@
    @Override
    public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId,boolean isSubStream, ErrorCallback<Object> callback) {
    public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, ErrorCallback<Object> callback) {
        if (mediaServerItem == null) {
            throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm");
        }
        Device device = redisCatchStorage.getDevice(deviceId);
        InviteInfo inviteInfo;
        if(device.isSwitchPrimarySubStream()){
            inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream);
        }else {
            inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
        }
        InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
        if (inviteInfo != null ) {
            if (inviteInfo.getStreamInfo() == null) {
                // 点播发起了但是尚未成功, 仅注册回调等待结果即可
                if(device.isSwitchPrimarySubStream()){
                    inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId,isSubStream, null, callback);
                }else {
                    inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback);
                }
                inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback);
                return inviteInfo.getSsrcInfo();
            }else {
                StreamInfo streamInfo = inviteInfo.getStreamInfo();
                String streamId = streamInfo.getStream();
                if (streamId == null) {
                    callback.run(InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), "点播失败, redis缓存streamId等于null", null);
                    if(device.isSwitchPrimarySubStream()){
                        inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null,
                                InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(),
                                "点播失败, redis缓存streamId等于null",
                                null);
                    }else {
                        inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                                InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(),
                                "点播失败, redis缓存streamId等于null",
                                null);
                    }
                    inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                            InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(),
                            "点播失败, redis缓存streamId等于null",
                            null);
                    return inviteInfo.getSsrcInfo();
                }
                String mediaServerId = streamInfo.getMediaServerId();
@@ -161,64 +145,38 @@
                Boolean ready = zlmrtpServerFactory.isStreamReady(mediaInfo, "rtp", streamId);
                if (ready != null && ready) {
                    callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo);
                    if(device.isSwitchPrimarySubStream()){
                        inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null,
                                InviteErrorCode.SUCCESS.getCode(),
                                InviteErrorCode.SUCCESS.getMsg(),
                                streamInfo);
                    }else {
                        inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                                InviteErrorCode.SUCCESS.getCode(),
                                InviteErrorCode.SUCCESS.getMsg(),
                                streamInfo);
                    }
                    inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                            InviteErrorCode.SUCCESS.getCode(),
                            InviteErrorCode.SUCCESS.getMsg(),
                            streamInfo);
                    return inviteInfo.getSsrcInfo();
                }else {
                    // 点播发起了但是尚未成功, 仅注册回调等待结果即可
                    if(device.isSwitchPrimarySubStream()) {
                        inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback);
                        storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
                        inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
                    }else {
                        inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId,isSubStream, null, callback);
                        inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream);
                    }
                    inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback);
                    storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
                    inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
                }
            }
        }
        String streamId = null;
        if (mediaServerItem.isRtpEnable()) {
            if(device.isSwitchPrimarySubStream()){
                streamId = StreamInfo.getPlayStream(deviceId, channelId, isSubStream);
            }else {
                streamId = String.format("%s_%s", device.getDeviceId(), channelId);
            }
        }
        String streamId = String.format("%s_%s", device.getDeviceId(), channelId);
        SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(),  false, 0, false, device.getStreamModeForParam());
        if (ssrcInfo == null) {
            callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), null);
            if(device.isSwitchPrimarySubStream()){
                inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null,
                        InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(),
                        InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(),
                        null);
            }else {
                inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                        InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(),
                        InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(),
                        null);
            }
            inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                    InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(),
                    InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(),
                    null);
            return null;
        }
        // TODO 记录点播的状态
        play(mediaServerItem, ssrcInfo, device, channelId,isSubStream, callback);
        play(mediaServerItem, ssrcInfo, device, channelId, callback);
        return ssrcInfo;
    }
    @Override
    public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,boolean isSubStream,
    public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
                     ErrorCallback<Object> callback) {
        if (mediaServerItem == null || ssrcInfo == null) {
@@ -227,11 +185,9 @@
                    null);
            return;
        }
        if( device.isSwitchPrimarySubStream() ){
            logger.info("[点播开始] deviceId: {}, channelId: {},码流类型:{},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId,isSubStream ? "辅码流" : "主码流", ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
        }else {
            logger.info("[点播开始] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
        }
        logger.info("[点播开始] deviceId: {}, channelId: {},码流类型:{},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}",
                device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流", ssrcInfo.getPort(),
                device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
        //端口获取失败的ssrcInfo 没有必要发送点播指令
        if (ssrcInfo.getPort() <= 0) {
            logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo);
@@ -240,50 +196,27 @@
            streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
            callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null);
            if(device.isSwitchPrimarySubStream()){
                inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null,
                        InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null);
            }else {
                inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                        InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null);
            }
            inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                    InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null);
            return;
        }
        // 初始化redis中的invite消息状态
        InviteInfo inviteInfo;
        if(device.isSwitchPrimarySubStream()){
            // 初始化redis中的invite消息状态
            inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channelId,isSubStream, ssrcInfo.getStream(), ssrcInfo,
                    mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY,
                    InviteSessionStatus.ready);
            inviteStreamService.updateInviteInfoSub(inviteInfo);
        }else {
            // 初始化redis中的invite消息状态
            inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo,
                    mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY,
                    InviteSessionStatus.ready);
            inviteStreamService.updateInviteInfo(inviteInfo);
        }
        InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo,
                mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY,
                InviteSessionStatus.ready);
        inviteInfo.setSubStream(device.isSwitchPrimarySubStream());
        inviteStreamService.updateInviteInfo(inviteInfo);
        // 超时处理
        String timeOutTaskKey = UUID.randomUUID().toString();
        dynamicTask.startDelay(timeOutTaskKey, () -> {
            // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况
            InviteInfo inviteInfoForTimeOut;
            if(device.isSwitchPrimarySubStream()){
                // 初始化redis中的invite消息状态
                inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream);
            }else {
                // 初始化redis中的invite消息状态
                inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
            }
            InviteInfo inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
            if (inviteInfoForTimeOut == null || inviteInfoForTimeOut.getStreamInfo() == null) {
                if( device.isSwitchPrimarySubStream()){
                    logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},码流类型:{},端口:{}, SSRC: {}", device.getDeviceId(), channelId,isSubStream ? "辅码流" : "主码流", ssrcInfo.getPort(), ssrcInfo.getSsrc());
                }else {
                    logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getPort(), ssrcInfo.getSsrc());
                }
                logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},码流类型:{},端口:{}, SSRC: {}",
                        device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流",
                        ssrcInfo.getPort(), ssrcInfo.getSsrc());
                // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
//                InviteInfo inviteInfoForTimeout = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.play, device.getDeviceId(), channelId);
//                if (inviteInfoForTimeout == null) {
@@ -295,16 +228,10 @@
//                    // TODO 发送cancel
//                }
                callback.run(InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null);
                if( device.isSwitchPrimarySubStream()){
                    inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null,
                            InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null);
                    inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream);
                inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                        InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null);
                inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
                }else {
                    inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                            InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null);
                    inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
                }
                try {
                    cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null);
                } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
@@ -322,42 +249,26 @@
        }, userSetting.getPlayTimeout());
        try {
            cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId,isSubStream, (mediaServerItemInuse, hookParam ) -> {
            cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (mediaServerItemInuse, hookParam ) -> {
                logger.info("收到订阅消息: " + hookParam);
                dynamicTask.stop(timeOutTaskKey);
                // hook响应
                StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInuse, hookParam, device.getDeviceId(), channelId,isSubStream);
                StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInuse, hookParam, device.getDeviceId(), channelId);
                if (streamInfo == null){
                    callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),
                            InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);
                    if( device.isSwitchPrimarySubStream()){
                        inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null,
                                InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),
                                InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);
                    }else {
                        inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                                InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),
                                InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);
                    }
                    inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                            InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),
                            InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);
                    return;
                }
                callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo);
                if( device.isSwitchPrimarySubStream()){
                    inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null,
                            InviteErrorCode.SUCCESS.getCode(),
                            InviteErrorCode.SUCCESS.getMsg(),
                            streamInfo);
                }else {
                    inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                            InviteErrorCode.SUCCESS.getCode(),
                            InviteErrorCode.SUCCESS.getMsg(),
                            streamInfo);
                }
                if( device.isSwitchPrimarySubStream() ){
                    logger.info("[点播成功] deviceId: {}, channelId: {},码流类型:{}", device.getDeviceId(), channelId,isSubStream ? "辅码流" : "主码流");
                }else {
                    logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
                }
                inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                        InviteErrorCode.SUCCESS.getCode(),
                        InviteErrorCode.SUCCESS.getMsg(),
                        streamInfo);
                logger.info("[点播成功] deviceId: {}, channelId: {},码流类型:{}", device.getDeviceId(),
                        device.isSwitchPrimarySubStream() ? "辅码流" : "主码流");
                String streamUrl;
                if (mediaServerItemInuse.getRtspPort() != 0) {
                    streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp",  ssrcInfo.getStream());
@@ -413,15 +324,9 @@
                                callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),
                                        InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);
                                if(device.isSwitchPrimarySubStream()){
                                    inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null,
                                            InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),
                                            InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);
                                }else {
                                    inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                                            InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),
                                            InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);
                                }
                                inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                                        InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),
                                        InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);
                            }
                        }
                        return;
@@ -438,39 +343,26 @@
                            subscribe.removeSubscribe(hookSubscribe);
                            String stream = String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase();
                            hookSubscribe.getContent().put("stream", stream);
                            inviteInfo.setStream(stream);
                            inviteStreamService.updateInviteInfoForStream(inviteInfo, stream);
                            subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> {
                                logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + hookParam);
                                dynamicTask.stop(timeOutTaskKey);
                                // hook响应
                                StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInUse, hookParam, device.getDeviceId(), channelId,isSubStream);
                                StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInUse, hookParam, device.getDeviceId(), channelId);
                                if (streamInfo == null){
                                    callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),
                                            InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);
                                    if( device.isSwitchPrimarySubStream()){
                                        inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null,
                                                InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),
                                                InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);
                                    }else {
                                        inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                                                InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),
                                                InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);
                                    }
                                    inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                                            InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),
                                            InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);
                                    return;
                                }
                                callback.run(InviteErrorCode.SUCCESS.getCode(),
                                        InviteErrorCode.SUCCESS.getMsg(), streamInfo);
                                if( device.isSwitchPrimarySubStream()){
                                    inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null,
                                            InviteErrorCode.SUCCESS.getCode(),
                                            InviteErrorCode.SUCCESS.getMsg(),
                                            streamInfo);
                                }else {
                                    inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                                            InviteErrorCode.SUCCESS.getCode(),
                                            InviteErrorCode.SUCCESS.getMsg(),
                                            streamInfo);
                                }
                                inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                                        InviteErrorCode.SUCCESS.getCode(),
                                        InviteErrorCode.SUCCESS.getMsg(),
                                        streamInfo);
                            });
                            return;
                        }
@@ -493,17 +385,14 @@
                            callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),
                                    "下级自定义了ssrc,重新设置收流信息失败", null);
                            if( device.isSwitchPrimarySubStream()){
                                inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null,
                                        InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),
                                        "下级自定义了ssrc,重新设置收流信息失败", null);
                            }else {
                                inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                                        InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),
                                        "下级自定义了ssrc,重新设置收流信息失败", null);
                            }
                            inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                                    InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),
                                    "下级自定义了ssrc,重新设置收流信息失败", null);
                        }else {
                            if (ssrcInfo.getStream()!= null && !ssrcInfo.getStream().equals(inviteInfo.getStream())) {
                                inviteStreamService.removeInviteInfo(inviteInfo);
                            }
                            ssrcInfo.setSsrc(ssrcInResponse);
                            inviteInfo.setSsrcInfo(ssrcInfo);
                            inviteInfo.setStream(ssrcInfo.getStream());
@@ -512,11 +401,7 @@
                        logger.info("[点播消息] 收到invite 200, 下级自定义了ssrc, 但是当前模式无需修正");
                    }
                }
                if(device.isSwitchPrimarySubStream()){
                    inviteStreamService.updateInviteInfoSub(inviteInfo);
                }else {
                    inviteStreamService.updateInviteInfo(inviteInfo);
                }
                inviteStreamService.updateInviteInfo(inviteInfo);
            }, (event) -> {
                dynamicTask.stop(timeOutTaskKey);
                mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
@@ -527,19 +412,11 @@
                callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_ERROR.getCode(),
                        String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null);
                if( device.isSwitchPrimarySubStream()){
                    inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null,
                            InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),
                            String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null);
                inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                        InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),
                        String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null);
                    inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream);
                }else {
                    inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                            InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),
                            String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null);
                    inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
                }
                inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
            });
        } catch (InvalidArgumentException | SipException | ParseException e) {
@@ -553,52 +430,30 @@
            callback.run(InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(),
                    InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null);
            if( device.isSwitchPrimarySubStream()){
                inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null,
                        InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(),
                        InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null);
            inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                    InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(),
                    InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null);
                inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream);
            }else {
                inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
                        InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(),
                        InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null);
                inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
            }
            inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
        }
    }
    private StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, HookParam hookParam, String deviceId, String channelId, boolean isSubStream) {
    private StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, HookParam hookParam, String deviceId, String channelId) {
        StreamInfo streamInfo = null;
        Device device = redisCatchStorage.getDevice(deviceId);
        OnStreamChangedHookParam streamChangedHookParam = (OnStreamChangedHookParam)hookParam;
        if( device.isSwitchPrimarySubStream() ){
            streamInfo = onPublishHandler(mediaServerItem, streamChangedHookParam, deviceId, channelId,isSubStream);
        }else {
            streamInfo = onPublishHandler(mediaServerItem, streamChangedHookParam, deviceId, channelId);
        }
        streamInfo = onPublishHandler(mediaServerItem, streamChangedHookParam, deviceId, channelId);
        if (streamInfo != null) {
            InviteInfo inviteInfo;
            if(device.isSwitchPrimarySubStream()){
                inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream);
            }else {
                DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
                if (deviceChannel != null) {
                    deviceChannel.setStreamId(streamInfo.getStream());
                    storager.startPlay(deviceId, channelId, streamInfo.getStream());
                }
                inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
            DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
            if (deviceChannel != null) {
                deviceChannel.setStreamId(streamInfo.getStream());
                storager.startPlay(deviceId, channelId, streamInfo.getStream());
            }
            InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
            if (inviteInfo != null) {
                inviteInfo.setStatus(InviteSessionStatus.ok);
                inviteInfo.setStreamInfo(streamInfo);
                if(device.isSwitchPrimarySubStream()){
                    inviteStreamService.updateInviteInfoSub(inviteInfo);
                }else {
                    inviteStreamService.updateInviteInfo(inviteInfo);
                }
                inviteStreamService.updateInviteInfo(inviteInfo);
            }
        }
        return streamInfo;
@@ -693,7 +548,7 @@
                device.getDeviceId(), channelId, startTime, endTime, ssrcInfo.getPort(), device.getStreamMode(),
                ssrcInfo.getSsrc(), device.isSsrcCheck());
        // 初始化redis中的invite消息状态
        InviteInfo inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo,
        InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo,
                mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAYBACK,
                InviteSessionStatus.ready);
        inviteStreamService.updateInviteInfo(inviteInfo);
@@ -805,7 +660,7 @@
                                    subscribe.removeSubscribe(hookSubscribe);
                                    String stream = String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase();
                                    hookSubscribe.getContent().put("stream", stream);
                                    inviteInfo.setStream(stream);
                                    inviteStreamService.updateInviteInfoForStream(inviteInfo, stream);
                                    subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> {
                                        logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + hookParam);
                                        dynamicTask.stop(playBackTimeOutTaskKey);
@@ -834,6 +689,10 @@
                                            "下级自定义了ssrc,重新设置收流信息失败", null);
                                }else {
                                    if (ssrcInfo.getStream()!= null && !ssrcInfo.getStream().equals(inviteInfo.getStream())) {
                                        inviteStreamService.removeInviteInfo(inviteInfo);
                                    }
                                    ssrcInfo.setSsrc(ssrcInResponse);
                                    inviteInfo.setSsrcInfo(ssrcInfo);
                                    inviteInfo.setStream(ssrcInfo.getStream());
@@ -892,7 +751,7 @@
        }
        logger.info("[录像下载] deviceId: {}, channelId: {}, 下载速度:{}, 收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, downloadSpeed, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
        // 初始化redis中的invite消息状态
        InviteInfo inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo,
        InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo,
                mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.DOWNLOAD,
                InviteSessionStatus.ready);
        inviteStreamService.updateInviteInfo(inviteInfo);
@@ -998,7 +857,9 @@
                                    // 添加订阅
                                    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());
                                    String stream = String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase();
                                    hookSubscribe.getContent().put("stream", stream);
                                    inviteStreamService.updateInviteInfoForStream(inviteInfo, stream);
                                    subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> {
                                        logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + hookParam);
                                        dynamicTask.stop(downLoadTimeOutTaskKey);
@@ -1026,6 +887,9 @@
                                            "下级自定义了ssrc,重新设置收流信息失败", null);
                                }else {
                                    if (ssrcInfo.getStream()!= null && !ssrcInfo.getStream().equals(inviteInfo.getStream())) {
                                        inviteStreamService.removeInviteInfo(inviteInfo);
                                    }
                                    ssrcInfo.setSsrc(ssrcInResponse);
                                    inviteInfo.setSsrcInfo(ssrcInfo);
                                    inviteInfo.setStream(ssrcInfo.getStream());
@@ -1034,6 +898,7 @@
                                logger.info("[录像下载] 收到invite 200, 下级自定义了ssrc, 但是当前模式无需修正");
                            }
                        }
                        inviteStreamService.updateInviteInfo(inviteInfo);
                    });
        } catch (InvalidArgumentException | SipException | ParseException e) {
            logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
@@ -1255,18 +1120,13 @@
    }
    @Override
    public void getSnap(String deviceId, String channelId, String fileName,boolean isSubStream, ErrorCallback errorCallback) {
    public void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback) {
        Device device = deviceService.getDevice(deviceId);
        if (device == null) {
            errorCallback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), null);
            return;
        }
        InviteInfo inviteInfo;
        if(device.isSwitchPrimarySubStream()){
             inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream);
        }else {
            inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
        }
        InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
        if (inviteInfo != null) {
            if (inviteInfo.getStreamInfo() != null) {
                // 已存在线直接截图
@@ -1292,11 +1152,11 @@
        }
        MediaServerItem newMediaServerItem = getNewMediaServerItem(device);
        play(newMediaServerItem, deviceId, channelId,isSubStream, (code, msg, data)->{
        play(newMediaServerItem, deviceId, channelId, (code, msg, data)->{
           if (code == InviteErrorCode.SUCCESS.getCode()) {
               InviteInfo inviteInfoForPlay = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
               if (inviteInfoForPlay != null && inviteInfoForPlay.getStreamInfo() != null) {
                   getSnap(deviceId, channelId, fileName,isSubStream, errorCallback);
                   getSnap(deviceId, channelId, fileName, errorCallback);
               }else {
                   errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null);
               }
@@ -1305,18 +1165,5 @@
           }
        });
    }
    /*======================设备主子码流逻辑START=========================*/
    public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, OnStreamChangedHookParam hookParam, String deviceId, String channelId,boolean isSubStream) {
        String streamId = hookParam.getStream();
        List<OnStreamChangedHookParam.MediaTrack> tracks = hookParam.getTracks();
        StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem, "rtp", streamId, tracks, null);
        streamInfo.setDeviceID(deviceId);
        streamInfo.setChannelId(channelId);
        streamInfo.setSubStream(isSubStream);
        return streamInfo;
    }
    /*======================设备主子码流逻辑END=========================*/
}
src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java
@@ -70,7 +70,6 @@
                continue;
            }
            String checkStr = callId == null? user.getPushKey():(callId + "_" + user.getPushKey())  ;
            System.out.println(checkStr);
            String checkSign = DigestUtils.md5DigestAsHex(checkStr.getBytes());
            if (checkSign.equals(sign)) {
                return true;
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
@@ -88,17 +88,16 @@
    @Operation(summary = "开始点播")
    @Parameter(name = "deviceId", description = "设备国标编号", required = true)
    @Parameter(name = "channelId", description = "通道国标编号", required = true)
    @Parameter(name = "isSubStream", description = "是否子码流(true-子码流,false-主码流),默认为false", required = true)
    @GetMapping("/start/{deviceId}/{channelId}")
    public DeferredResult<WVPResult<StreamContent>> play(HttpServletRequest request, @PathVariable String deviceId,
                                                         @PathVariable String channelId,boolean isSubStream) {
                                                         @PathVariable String channelId) {
        // 获取可用的zlm
        Device device = storager.queryVideoDevice(deviceId);
        MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
        RequestMessage requestMessage = new RequestMessage();
        String key = DeferredResultHolder.getPlayKey(deviceId,channelId,device.isSwitchPrimarySubStream(),isSubStream);
        String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
        requestMessage.setKey(key);
        String uuid = UUID.randomUUID().toString();
        requestMessage.setId(uuid);
@@ -117,7 +116,7 @@
        // 录像查询以channelId作为deviceId查询
        resultHolder.put(key, uuid, result);
        playService.play(newMediaServerItem, deviceId, channelId,isSubStream, (code, msg, data) -> {
        playService.play(newMediaServerItem, deviceId, channelId, (code, msg, data) -> {
            WVPResult<StreamContent> wvpResult = new WVPResult<>();
            if (code == InviteErrorCode.SUCCESS.getCode()) {
                wvpResult.setCode(ErrorCode.SUCCESS.getCode());
@@ -158,12 +157,7 @@
            throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备[" + deviceId + "]不存在");
        }
        InviteInfo inviteInfo =null;
        if(device.isSwitchPrimarySubStream()){
            inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream);
        }else {
            inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
        }
        InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
        if (inviteInfo == null) {
            throw new ControllerException(ErrorCode.ERROR100.getCode(), "点播未找到");
        }
@@ -176,12 +170,8 @@
                throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
            }
        }
        if(device.isSwitchPrimarySubStream()){
            inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream);
        }else {
            inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
            storager.stopPlay(deviceId, channelId);
        }
        inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
        storager.stopPlay(deviceId, channelId);
        JSONObject json = new JSONObject();
        json.put("deviceId", deviceId);
@@ -360,9 +350,8 @@
            logger.debug("获取截图: {}/{}", deviceId, channelId);
        }
        Device device = storager.queryVideoDevice(deviceId);
        DeferredResult<String> result = new DeferredResult<>(3 * 1000L);
        String key = DeferredResultHolder.getSnapKey(deviceId,channelId,device.isSwitchPrimarySubStream(),isSubStream);
        String key  = DeferredResultHolder.CALLBACK_CMD_SNAP + deviceId;
        String uuid  = UUID.randomUUID().toString();
        resultHolder.put(key, uuid,  result);
@@ -371,7 +360,7 @@
        message.setId(uuid);
        String fileName = deviceId + "_" + channelId + "_" + DateUtil.getNowForUrl() + "jpg";
        playService.getSnap(deviceId, channelId, fileName,isSubStream, (code, msg, data) -> {
        playService.getSnap(deviceId, channelId, fileName, (code, msg, data) -> {
            if (code == InviteErrorCode.SUCCESS.getCode()) {
                message.setData(data);
            }else {
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java
@@ -122,7 +122,7 @@
        MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
        playService.play(newMediaServerItem, serial, code,false, (errorCode, msg, data) -> {
        playService.play(newMediaServerItem, serial, code, (errorCode, msg, data) -> {
            if (errorCode == InviteErrorCode.SUCCESS.getCode()) {
                InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, serial, code);
                if (inviteInfo != null && inviteInfo.getStreamInfo() != null) {