| | |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| | | 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.gb28181.utils.SipUtils; |
| | | import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils; |
| | | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; |
| | | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| | |
| | | import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| | | import com.genersoft.iot.vmp.service.*; |
| | | import com.genersoft.iot.vmp.service.bean.InviteErrorCallback; |
| | | import com.genersoft.iot.vmp.service.bean.ErrorCallback; |
| | | import com.genersoft.iot.vmp.service.bean.InviteErrorCode; |
| | | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| | |
| | | import javax.sip.InvalidArgumentException; |
| | | import javax.sip.ResponseEvent; |
| | | import javax.sip.SipException; |
| | | import java.io.File; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.text.ParseException; |
| | |
| | | |
| | | |
| | | @Override |
| | | public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, InviteErrorCallback<Object> callback) { |
| | | public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, ErrorCallback<Object> callback) { |
| | | if (mediaServerItem == null) { |
| | | throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm"); |
| | | } |
| | |
| | | |
| | | @Override |
| | | public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, |
| | | InviteErrorCallback<Object> callback) { |
| | | ErrorCallback<Object> callback) { |
| | | |
| | | if (mediaServerItem == null || ssrcInfo == null) { |
| | | callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), |
| | |
| | | null); |
| | | return; |
| | | } |
| | | logger.info("[点播开始] deviceId: {}, channelId: {},收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); |
| | | logger.info("\r\n" + |
| | | " [点播开始] \r\n" + |
| | | "deviceId : {}, \r\n" + |
| | | "channelId : {},\r\n" + |
| | | "收流端口 : {}, \r\n" + |
| | | "收流模式 : {}, \r\n" + |
| | | "SSRC : {}, \r\n" + |
| | | "SSRC校验 :{}", |
| | | device.getDeviceId(), |
| | | channelId, |
| | | ssrcInfo.getPort(), |
| | | device.getStreamMode(), |
| | | ssrcInfo.getSsrc(), |
| | | device.isSsrcCheck()); |
| | | |
| | | //端口获取失败的ssrcInfo 没有必要发送点播指令 |
| | | if (ssrcInfo.getPort() <= 0) { |
| | |
| | | ResponseEvent responseEvent = (ResponseEvent) event.event; |
| | | String contentString = new String(responseEvent.getResponse().getRawContent()); |
| | | // 获取ssrc |
| | | int ssrcIndex = contentString.indexOf("y="); |
| | | String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); |
| | | |
| | | // 检查是否有y字段 |
| | | if (ssrcIndex >= 0) { |
| | | //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 |
| | | String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12).trim(); |
| | | if (ssrcInResponse != null) { |
| | | // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 |
| | | if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { |
| | | if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { |
| | | String substring = contentString.substring(0, contentString.indexOf("y=")); |
| | | try { |
| | | SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); |
| | | Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); |
| | | SessionDescription sdp = gb28181Sdp.getBaseSdb(); |
| | | int port = -1; |
| | | Vector mediaDescriptions = sdp.getMediaDescriptions(true); |
| | | for (Object description : mediaDescriptions) { |
| | |
| | | return; |
| | | } |
| | | logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); |
| | | |
| | | if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { |
| | | logger.info("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); |
| | | if (!ssrcFactory.checkSsrc(mediaServerItem.getId(),ssrcInResponse)) { |
| | | // ssrc 不可用 |
| | | logger.info("[点播消息] SSRC修正时发现ssrc不可使用 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); |
| | | // 释放ssrc |
| | | ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); |
| | | streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); |
| | | |
| | | callback.run(InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getCode(), |
| | | InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getMsg(), null); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getCode(), |
| | | InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getMsg(), null); |
| | | // 释放ssrc |
| | | mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); |
| | | |
| | | return; |
| | | } |
| | | // 单端口模式streamId也有变化,重新设置监听即可 |
| | | if (!mediaServerItem.isRtpEnable()) { |
| | | // 添加订阅 |
| | |
| | | return; |
| | | } |
| | | callback.run(InviteErrorCode.SUCCESS.getCode(), |
| | | InviteErrorCode.SUCCESS.getMsg(), null); |
| | | InviteErrorCode.SUCCESS.getMsg(), streamInfo); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.SUCCESS.getCode(), |
| | | InviteErrorCode.SUCCESS.getMsg(), |
| | |
| | | } |
| | | |
| | | dynamicTask.stop(timeOutTaskKey); |
| | | // 释放ssrc |
| | | mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); |
| | | |
| | | streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); |
| | | |
| | |
| | | |
| | | @Override |
| | | public void playBack(String deviceId, String channelId, String startTime, |
| | | String endTime, InviteErrorCallback<Object> callback) { |
| | | String endTime, ErrorCallback<Object> callback) { |
| | | Device device = storager.queryVideoDevice(deviceId); |
| | | if (device == null) { |
| | | return; |
| | |
| | | @Override |
| | | public void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, |
| | | String deviceId, String channelId, String startTime, |
| | | String endTime, InviteErrorCallback<Object> callback) { |
| | | String endTime, ErrorCallback<Object> callback) { |
| | | if (mediaServerItem == null || ssrcInfo == null) { |
| | | callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), |
| | | InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), |
| | |
| | | ResponseEvent responseEvent = (ResponseEvent) eventResult.event; |
| | | String contentString = new String(responseEvent.getResponse().getRawContent()); |
| | | // 获取ssrc |
| | | int ssrcIndex = contentString.indexOf("y="); |
| | | String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); |
| | | |
| | | // 检查是否有y字段 |
| | | if (ssrcIndex >= 0) { |
| | | //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 |
| | | String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); |
| | | if (ssrcInResponse != null) { |
| | | // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 |
| | | if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { |
| | | if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { |
| | | String substring = contentString.substring(0, contentString.indexOf("y=")); |
| | | try { |
| | | SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); |
| | | Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); |
| | | SessionDescription sdp = gb28181Sdp.getBaseSdb(); |
| | | int port = -1; |
| | | Vector mediaDescriptions = sdp.getMediaDescriptions(true); |
| | | for (Object description : mediaDescriptions) { |
| | |
| | | if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { |
| | | logger.info("[录像回放] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); |
| | | |
| | | if (!ssrcFactory.checkSsrc(mediaServerItem.getId(),ssrcInResponse)) { |
| | | // ssrc 不可用 |
| | | logger.info("[录像回放] SSRC修正时发现ssrc不可使用 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); |
| | | // 释放ssrc |
| | | dynamicTask.stop(playBackTimeOutTaskKey); |
| | | mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); |
| | | streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); |
| | | callback.run(InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getCode(), |
| | | InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getMsg(), null); |
| | | return; |
| | | } |
| | | // 释放ssrc |
| | | mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); |
| | | |
| | | // 单端口模式streamId也有变化,需要重新设置监听 |
| | | if (!mediaServerItem.isRtpEnable()) { |
| | |
| | | } |
| | | |
| | | dynamicTask.stop(playBackTimeOutTaskKey); |
| | | // 释放ssrc |
| | | mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); |
| | | |
| | | streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); |
| | | |
| | |
| | | |
| | | |
| | | @Override |
| | | public void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteErrorCallback<Object> callback) { |
| | | public void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback) { |
| | | Device device = storager.queryVideoDevice(deviceId); |
| | | if (device == null) { |
| | | return; |
| | |
| | | |
| | | |
| | | @Override |
| | | public void download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteErrorCallback<Object> callback) { |
| | | public void download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback) { |
| | | if (mediaServerItem == null || ssrcInfo == null) { |
| | | callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), |
| | | InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), |
| | |
| | | ResponseEvent responseEvent = (ResponseEvent) eventResult.event; |
| | | String contentString = new String(responseEvent.getResponse().getRawContent()); |
| | | // 获取ssrc |
| | | int ssrcIndex = contentString.indexOf("y="); |
| | | String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); |
| | | // 检查是否有y字段 |
| | | if (ssrcIndex >= 0) { |
| | | //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 |
| | | String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); |
| | | if (ssrcInResponse != null) { |
| | | // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 |
| | | if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { |
| | | if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { |
| | | String substring = contentString.substring(0, contentString.indexOf("y=")); |
| | | try { |
| | | SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); |
| | | Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); |
| | | SessionDescription sdp = gb28181Sdp.getBaseSdb(); |
| | | int port = -1; |
| | | Vector mediaDescriptions = sdp.getMediaDescriptions(true); |
| | | for (Object description : mediaDescriptions) { |
| | |
| | | if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { |
| | | logger.info("[录像下载] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); |
| | | |
| | | if (!ssrcFactory.checkSsrc(mediaServerItem.getId(),ssrcInResponse)) { |
| | | // ssrc 不可用 |
| | | // 释放ssrc |
| | | mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); |
| | | streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); |
| | | callback.run(InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getCode(), |
| | | InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getMsg(), null); |
| | | return; |
| | | } |
| | | // 释放ssrc |
| | | mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); |
| | | |
| | | // 单端口模式streamId也有变化,需要重新设置监听 |
| | | if (!mediaServerItem.isRtpEnable()) { |
| | |
| | | } |
| | | |
| | | dynamicTask.stop(downLoadTimeOutTaskKey); |
| | | // 释放ssrc |
| | | mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); |
| | | |
| | | streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); |
| | | |
| | |
| | | Device device = storager.queryVideoDevice(inviteInfo.getDeviceId()); |
| | | cmder.playResumeCmd(device, inviteInfo.getStreamInfo()); |
| | | } |
| | | |
| | | @Override |
| | | 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 = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); |
| | | if (inviteInfo != null) { |
| | | if (inviteInfo.getStreamInfo() != null) { |
| | | // 已存在线直接截图 |
| | | MediaServerItem mediaServerItemInuse = mediaServerService.getOne(inviteInfo.getStreamInfo().getMediaServerId()); |
| | | String streamUrl; |
| | | if (mediaServerItemInuse.getRtspPort() != 0) { |
| | | streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", inviteInfo.getStreamInfo().getStream()); |
| | | }else { |
| | | streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.mp4", mediaServerItemInuse.getHttpPort(), "rtp", inviteInfo.getStreamInfo().getStream()); |
| | | } |
| | | String path = "snap"; |
| | | // 请求截图 |
| | | logger.info("[请求截图]: " + fileName); |
| | | zlmresTfulUtils.getSnap(mediaServerItemInuse, streamUrl, 15, 1, path, fileName); |
| | | String filePath = path + File.separator + fileName; |
| | | File snapFile = new File(path + File.separator + fileName); |
| | | if (snapFile.exists()) { |
| | | errorCallback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), filePath); |
| | | }else { |
| | | errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); |
| | | } |
| | | return; |
| | | } |
| | | } |
| | | |
| | | MediaServerItem newMediaServerItem = getNewMediaServerItem(device); |
| | | 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, errorCallback); |
| | | }else { |
| | | errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); |
| | | } |
| | | }else { |
| | | errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | } |