| | |
| | | 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()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |