| 
					
				 | 
			
			
				@@ -193,17 +193,30 @@ public class PlayServiceImpl implements IPlayService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if(rtpInfo.getInteger("code") == 0){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if (rtpInfo.getBoolean("exist")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    WVPResult wvpResult = new WVPResult(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    wvpResult.setCode(ErrorCode.SUCCESS.getCode()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    wvpResult.setData(streamInfo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    msg.setData(wvpResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    resultHolder.invokeAllResult(msg); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if (hookEvent != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    int localPort = rtpInfo.getInteger("local_port"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (localPort == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        logger.warn("[点播],点播时发现rtpServerC存在,但是尚未开始推流"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        // 此时说明rtpServer已经创建但是流还没有推上来 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        WVPResult wvpResult = new WVPResult(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        wvpResult.setCode(ErrorCode.ERROR100.getCode()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        wvpResult.setMsg("点播已经在进行中,请稍候重试"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        msg.setData(wvpResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        resultHolder.invokeAllResult(msg); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        return playResult; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    }else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        WVPResult wvpResult = new WVPResult(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        wvpResult.setCode(ErrorCode.SUCCESS.getCode()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        wvpResult.setData(streamInfo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        msg.setData(wvpResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        resultHolder.invokeAllResult(msg); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (hookEvent != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 }else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     redisCatchStorage.stopPlay(streamInfo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -318,7 +331,7 @@ public class PlayServiceImpl implements IPlayService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    logger.info("[SIP 消息] SSRC修正 {}->{}", ssrc, ssrcInResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    logger.info("[点播消息] SSRC修正 {}->{}", ssrc, ssrcInResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         // ssrc 不可用 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -468,37 +481,92 @@ public class PlayServiceImpl implements IPlayService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             resultHolder.exist(DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId, uuid); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         }, userSetting.getPlayTimeout()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        SipSubscribe.Event errorEvent = event -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            dynamicTask.stop(playBackTimeOutTaskKey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            requestMessage.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            playBackResult.setCode(ErrorCode.ERROR100.getCode()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            playBackResult.setMsg(String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            playBackResult.setData(requestMessage); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            playBackResult.setEvent(event); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            playBackCallback.call(playBackResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        InviteStreamCallback hookEvent = (InviteStreamInfo inviteStreamInfo) -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logger.info("收到回放订阅消息: " + inviteStreamInfo.getResponse().toJSONString()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            dynamicTask.stop(playBackTimeOutTaskKey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (streamInfo == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                logger.warn("设备回放API调用失败!"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                playBackResult.setCode(ErrorCode.ERROR100.getCode()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                playBackResult.setMsg("设备回放API调用失败!"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                playBackCallback.call(playBackResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            redisCatchStorage.startPlayback(streamInfo, inviteStreamInfo.getCallId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            WVPResult<StreamInfo> success = WVPResult.success(streamInfo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            requestMessage.setData(success); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            playBackResult.setCode(ErrorCode.SUCCESS.getCode()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            playBackResult.setMsg(ErrorCode.SUCCESS.getMsg()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            playBackResult.setData(requestMessage); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            playBackResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            playBackResult.setResponse(inviteStreamInfo.getResponse()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            playBackCallback.call(playBackResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                (InviteStreamInfo inviteStreamInfo) -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    dynamicTask.stop(playBackTimeOutTaskKey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if (streamInfo == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        logger.warn("设备回放API调用失败!"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        playBackResult.setCode(ErrorCode.ERROR100.getCode()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        playBackResult.setMsg("设备回放API调用失败!"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        playBackCallback.call(playBackResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                hookEvent, eventResult -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (eventResult.type == SipSubscribe.EventResultType.response) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ResponseEvent responseEvent = (ResponseEvent)eventResult.event; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        String contentString = new String(responseEvent.getResponse().getRawContent()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        // 获取ssrc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        int ssrcIndex = contentString.indexOf("y="); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        // 检查是否有y字段 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (ssrcIndex >= 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                logger.info("[回放消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    // ssrc 不可用 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    // 释放ssrc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    eventResult.msg = "下级自定义了ssrc,但是此ssrc不可用"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    eventResult.statusCode = 400; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    errorEvent.response(eventResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                // 单端口模式streamId也有变化,需要重新设置监听 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                if (!mediaServerItem.isRtpEnable()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    // 添加订阅 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    subscribe.removeSubscribe(hookSubscribe); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response)->{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        dynamicTask.stop(playBackTimeOutTaskKey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        // hook响应 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId, uuid); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                // 关闭rtp server 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                mediaServerService.closeRTPServer(device.getDeviceId(), channelId, ssrcInfo.getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                // 重新开启ssrc server 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    redisCatchStorage.startPlayback(streamInfo, inviteStreamInfo.getCallId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    WVPResult<StreamInfo> success = WVPResult.success(streamInfo); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    requestMessage.setData(success); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    playBackResult.setCode(ErrorCode.SUCCESS.getCode()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    playBackResult.setMsg(ErrorCode.SUCCESS.getMsg()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    playBackResult.setData(requestMessage); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    playBackResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    playBackResult.setResponse(inviteStreamInfo.getResponse()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    playBackCallback.call(playBackResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                }, event -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    dynamicTask.stop(playBackTimeOutTaskKey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    requestMessage.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    playBackResult.setCode(ErrorCode.ERROR100.getCode()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    playBackResult.setMsg(String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    playBackResult.setData(requestMessage); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    playBackResult.setEvent(event); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    playBackCallback.call(playBackResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                }, errorEvent); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 |