|  |  | 
 |  |  | import com.alibaba.fastjson.JSONObject; | 
 |  |  | import com.genersoft.iot.vmp.common.StreamInfo; | 
 |  |  | import com.genersoft.iot.vmp.conf.UserSetup; | 
 |  |  | import com.genersoft.iot.vmp.gb28181.bean.Device; | 
 |  |  | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; | 
 |  |  | import com.genersoft.iot.vmp.gb28181.bean.*; | 
 |  |  | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; | 
 |  |  | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; | 
 |  |  | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; | 
 |  |  | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; | 
 |  |  | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; | 
 |  |  | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; | 
 |  |  | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; | 
 |  |  | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; | 
 |  |  | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | 
 |  |  | 
 |  |  | import org.springframework.web.context.request.async.DeferredResult; | 
 |  |  |  | 
 |  |  | import java.io.FileNotFoundException; | 
 |  |  | import java.util.Objects; | 
 |  |  | import java.util.UUID; | 
 |  |  | import java.util.*; | 
 |  |  |  | 
 |  |  | @SuppressWarnings(value = {"rawtypes", "unchecked"}) | 
 |  |  | @Service | 
 |  |  | 
 |  |  |  | 
 |  |  |     @Autowired | 
 |  |  |     private SIPCommander cmder; | 
 |  |  |  | 
 |  |  |     @Autowired | 
 |  |  |     private SIPCommanderFroPlatform sipCommanderFroPlatform; | 
 |  |  |  | 
 |  |  |     @Autowired | 
 |  |  |     private IRedisCatchStorage redisCatchStorage; | 
 |  |  | 
 |  |  |  | 
 |  |  |  | 
 |  |  |     @Override | 
 |  |  |     public PlayResult play(MediaServerItem mediaServerItem, String deviceId, String channelId, ZLMHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent) { | 
 |  |  |     public PlayResult play(MediaServerItem mediaServerItem, String deviceId, String channelId, | 
 |  |  |                            ZLMHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, | 
 |  |  |                            Runnable timeoutCallback) { | 
 |  |  |         PlayResult playResult = new PlayResult(); | 
 |  |  |         RequestMessage msg = new RequestMessage(); | 
 |  |  |         String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId; | 
 |  |  | 
 |  |  |         Device device = redisCatchStorage.getDevice(deviceId); | 
 |  |  |         StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); | 
 |  |  |         playResult.setDevice(device); | 
 |  |  |         // 超时处理 | 
 |  |  |         result.onTimeout(()->{ | 
 |  |  |             logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId)); | 
 |  |  |             WVPResult wvpResult = new WVPResult(); | 
 |  |  |             wvpResult.setCode(-1); | 
 |  |  |             SIPDialog dialog = streamSession.getDialogByStream(deviceId, channelId, streamInfo.getStream()); | 
 |  |  |             if (dialog != null) { | 
 |  |  |                 wvpResult.setMsg("收流超时,请稍候重试"); | 
 |  |  |             }else { | 
 |  |  |                 wvpResult.setMsg("点播超时,请稍候重试"); | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |             msg.setData(wvpResult); | 
 |  |  |             // 点播超时回复BYE | 
 |  |  |             cmder.streamByeCmd(device.getDeviceId(), channelId, streamInfo.getStream()); | 
 |  |  |             // 释放rtpserver | 
 |  |  |             mediaServerService.closeRTPServer(playResult.getDevice().getDeviceId(), channelId, streamInfo.getStream()); | 
 |  |  |             // 回复之前所有的点播请求 | 
 |  |  |             resultHolder.invokeAllResult(msg); | 
 |  |  |             // TODO 释放ssrc | 
 |  |  |         }); | 
 |  |  |         result.onCompletion(()->{ | 
 |  |  |             // 点播结束时调用截图接口 | 
 |  |  |             // TODO 应该在上流时调用更好,结束也可能是错误结束 | 
 |  |  |             try { | 
 |  |  |                 String classPath = ResourceUtils.getURL("classpath:").getPath(); | 
 |  |  |                 // 兼容打包为jar的class路径 | 
 |  |  | 
 |  |  |             if (mediaServerItem.isRtpEnable()) { | 
 |  |  |                 streamId = String.format("%s_%s", device.getDeviceId(), channelId); | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |             SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId); | 
 |  |  |             // 超时处理 | 
 |  |  |             Timer timer = new Timer(); | 
 |  |  |             timer.schedule(new TimerTask() { | 
 |  |  |                 @Override | 
 |  |  |                 public void run() { | 
 |  |  |                     logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId)); | 
 |  |  |                     if (timeoutCallback != null) { | 
 |  |  |                         timeoutCallback.run(); | 
 |  |  |                     } | 
 |  |  |                     WVPResult wvpResult = new WVPResult(); | 
 |  |  |                     wvpResult.setCode(-1); | 
 |  |  |                     SIPDialog dialog = streamSession.getDialogByStream(deviceId, channelId, ssrcInfo.getStream()); | 
 |  |  |                     if (dialog != null) { | 
 |  |  |                         wvpResult.setMsg("收流超时,请稍候重试"); | 
 |  |  |                         // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 | 
 |  |  |                         cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream()); | 
 |  |  |                     }else { | 
 |  |  |                         wvpResult.setMsg("点播超时,请稍候重试"); | 
 |  |  |                         mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); | 
 |  |  |                         mediaServerService.closeRTPServer(deviceId, channelId, ssrcInfo.getStream()); | 
 |  |  |                         streamSession.remove(deviceId, channelId, ssrcInfo.getStream()); | 
 |  |  |                     } | 
 |  |  |  | 
 |  |  |                     msg.setData(wvpResult); | 
 |  |  |  | 
 |  |  |                     // 回复之前所有的点播请求 | 
 |  |  |                     resultHolder.invokeAllResult(msg); | 
 |  |  |                 } | 
 |  |  |             }, userSetup.getPlayTimeout()); | 
 |  |  |             // 发送点播消息 | 
 |  |  |             cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInUse, JSONObject response) -> { | 
 |  |  |                 logger.info("收到订阅消息: " + response.toJSONString()); | 
 |  |  |                 timer.cancel(); | 
 |  |  |                 onPublishHandlerForPlay(mediaServerItemInUse, response, deviceId, channelId, uuid); | 
 |  |  |                 if (hookEvent != null) { | 
 |  |  |                     hookEvent.response(mediaServerItem, response); | 
 |  |  |                 } | 
 |  |  |             }, (event) -> { | 
 |  |  |                 timer.cancel(); | 
 |  |  |                 WVPResult wvpResult = new WVPResult(); | 
 |  |  |                 wvpResult.setCode(-1); | 
 |  |  |                 // 点播返回sip错误 | 
 |  |  |                 mediaServerService.closeRTPServer(playResult.getDevice().getDeviceId(), channelId, ssrcInfo.getStream()); | 
 |  |  |                 // 释放ssrc | 
 |  |  |                 mediaServerService.releaseSsrc(mediaServerItem, ssrcInfo.getSsrc()); | 
 |  |  |                 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); | 
 |  |  |                 streamSession.remove(deviceId, channelId, ssrcInfo.getStream()); | 
 |  |  |  | 
 |  |  |                 wvpResult.setMsg(String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg)); | 
 |  |  |                 msg.setData(wvpResult); | 
 |  |  |                 resultHolder.invokeAllResult(msg); | 
 |  |  |                 if (errorEvent != null) { | 
 |  |  |                     errorEvent.response(event); | 
 |  |  |                 } | 
 |  |  |  | 
 |  |  |  | 
 |  |  |             }); | 
 |  |  |         } else { | 
 |  |  |             String streamId = streamInfo.getStream(); | 
 |  |  | 
 |  |  |                     streamId2 = String.format("%s_%s", device.getDeviceId(), channelId); | 
 |  |  |                 } | 
 |  |  |                 SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId2); | 
 |  |  |                 // 超时处理 | 
 |  |  |                 Timer timer = new Timer(); | 
 |  |  |                 timer.schedule(new TimerTask() { | 
 |  |  |                     @Override | 
 |  |  |                     public void run() { | 
 |  |  |                         logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId)); | 
 |  |  |                         if (timeoutCallback != null) { | 
 |  |  |                             timeoutCallback.run(); | 
 |  |  |                         } | 
 |  |  |                         WVPResult wvpResult = new WVPResult(); | 
 |  |  |                         wvpResult.setCode(-1); | 
 |  |  |                         SIPDialog dialog = streamSession.getDialogByStream(deviceId, channelId, ssrcInfo.getStream()); | 
 |  |  |                         if (dialog != null) { | 
 |  |  |                             wvpResult.setMsg("收流超时,请稍候重试"); | 
 |  |  |                             // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 | 
 |  |  |                             cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream()); | 
 |  |  |                         }else { | 
 |  |  |                             wvpResult.setMsg("点播超时,请稍候重试"); | 
 |  |  |                             mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); | 
 |  |  |                             mediaServerService.closeRTPServer(deviceId, channelId, ssrcInfo.getStream()); | 
 |  |  |                             streamSession.remove(deviceId, channelId, ssrcInfo.getStream()); | 
 |  |  |                         } | 
 |  |  |  | 
 |  |  |                         msg.setData(wvpResult); | 
 |  |  |                         // 回复之前所有的点播请求 | 
 |  |  |                         resultHolder.invokeAllResult(msg); | 
 |  |  |                     } | 
 |  |  |                 }, userSetup.getPlayTimeout()); | 
 |  |  |                 cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { | 
 |  |  |                     logger.info("收到订阅消息: " + response.toJSONString()); | 
 |  |  |                     onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid); | 
 |  |  |                 }, (event) -> { | 
 |  |  |                     mediaServerService.closeRTPServer(playResult.getDevice().getDeviceId(), channelId, ssrcInfo.getStream()); | 
 |  |  |                     // 释放ssrc | 
 |  |  |                     mediaServerService.releaseSsrc(mediaServerItem, ssrcInfo.getSsrc()); | 
 |  |  |                     mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); | 
 |  |  |                     streamSession.remove(deviceId, channelId, ssrcInfo.getStream()); | 
 |  |  |                     WVPResult wvpResult = new WVPResult(); | 
 |  |  |                     wvpResult.setCode(-1); | 
 |  |  | 
 |  |  |         msg.setId(uuid); | 
 |  |  |         msg.setKey(key); | 
 |  |  |         PlayBackResult<RequestMessage> playBackResult = new PlayBackResult<>(); | 
 |  |  |         result.onTimeout(()->{ | 
 |  |  |             msg.setData("回放超时"); | 
 |  |  |             playBackResult.setCode(-1); | 
 |  |  |             playBackResult.setData(msg); | 
 |  |  |             callback.call(playBackResult); | 
 |  |  |         }); | 
 |  |  |  | 
 |  |  |         Timer timer = new Timer(); | 
 |  |  |         timer.schedule(new TimerTask() { | 
 |  |  |             @Override | 
 |  |  |             public void run() { | 
 |  |  |                 logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); | 
 |  |  |                 playBackResult.setCode(-1); | 
 |  |  |                 playBackResult.setData(msg); | 
 |  |  |                 callback.call(playBackResult); | 
 |  |  |                 SIPDialog dialog = streamSession.getDialogByStream(deviceId, channelId, ssrcInfo.getStream()); | 
 |  |  |                 // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 | 
 |  |  |                 if (dialog != null) { | 
 |  |  |                     // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 | 
 |  |  |                     cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream()); | 
 |  |  |                 }else { | 
 |  |  |                     mediaServerService.releaseSsrc(newMediaServerItem.getId(), ssrcInfo.getSsrc()); | 
 |  |  |                     mediaServerService.closeRTPServer(deviceId, channelId, ssrcInfo.getStream()); | 
 |  |  |                     streamSession.remove(deviceId, channelId, ssrcInfo.getStream()); | 
 |  |  |                 } | 
 |  |  |                 cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream()); | 
 |  |  |                 // 回复之前所有的点播请求 | 
 |  |  |                 callback.call(playBackResult); | 
 |  |  |             } | 
 |  |  |         }, userSetup.getPlayTimeout()); | 
 |  |  |         cmder.playbackStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, (MediaServerItem mediaServerItem, JSONObject response) -> { | 
 |  |  |             logger.info("收到订阅消息: " + response.toJSONString()); | 
 |  |  |             timer.cancel(); | 
 |  |  |             StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId); | 
 |  |  |             if (streamInfo == null) { | 
 |  |  |                 logger.warn("设备回放API调用失败!"); | 
 |  |  | 
 |  |  |             playBackResult.setResponse(response); | 
 |  |  |             callback.call(playBackResult); | 
 |  |  |         }, event -> { | 
 |  |  |             timer.cancel(); | 
 |  |  |             msg.setData(String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg)); | 
 |  |  |             playBackResult.setCode(-1); | 
 |  |  |             playBackResult.setData(msg); | 
 |  |  | 
 |  |  |         return streamInfo; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     @Override | 
 |  |  |     public void zlmServerOffline(String mediaServerId) { | 
 |  |  |         // 处理正在向上推流的上级平台 | 
 |  |  |         List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServer(null); | 
 |  |  |         if (sendRtpItems.size() > 0) { | 
 |  |  |             for (SendRtpItem sendRtpItem : sendRtpItems) { | 
 |  |  |                 if (sendRtpItem.getMediaServerId().equals(mediaServerId)) { | 
 |  |  |                     ParentPlatform platform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId()); | 
 |  |  |                     sipCommanderFroPlatform.streamByeCmd(platform, sendRtpItem.getCallId()); | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |         // 处理正在观看的国标设备 | 
 |  |  |         List<SsrcTransaction> allSsrc = streamSession.getAllSsrc(); | 
 |  |  |         if (allSsrc.size() > 0) { | 
 |  |  |             for (SsrcTransaction ssrcTransaction : allSsrc) { | 
 |  |  |                 if(ssrcTransaction.getMediaServerId().equals(mediaServerId)) { | 
 |  |  |                     cmder.streamByeCmd(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  | } |