old mode 100644
new mode 100755
| | |
| | | package com.genersoft.iot.vmp.service.impl; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.genersoft.iot.vmp.common.InviteInfo; |
| | | import com.genersoft.iot.vmp.common.InviteSessionType; |
| | | import com.genersoft.iot.vmp.common.StreamInfo; |
| | | import com.genersoft.iot.vmp.common.VideoManagerConstants; |
| | | import com.genersoft.iot.vmp.conf.MediaConfig; |
| | | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.MediaItem; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| | | import com.genersoft.iot.vmp.service.IMediaServerService; |
| | | import com.genersoft.iot.vmp.conf.UserSetting; |
| | | import com.genersoft.iot.vmp.conf.exception.ControllerException; |
| | | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
| | | import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; |
| | | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| | | import com.genersoft.iot.vmp.media.bean.MediaInfo; |
| | | import com.genersoft.iot.vmp.media.bean.ResultForOnPublish; |
| | | import com.genersoft.iot.vmp.media.service.IMediaServerService; |
| | | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookListener; |
| | | import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager; |
| | | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.HookType; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.MediaServer; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.hook.HookResultForOnPublish; |
| | | import com.genersoft.iot.vmp.service.*; |
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| | | import com.genersoft.iot.vmp.service.IMediaService; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; |
| | | import com.genersoft.iot.vmp.vmanager.bean.OtherPsSendInfo; |
| | | import com.genersoft.iot.vmp.vmanager.bean.OtherRtpSendInfo; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.data.redis.core.RedisTemplate; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.ObjectUtils; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | @Service |
| | | public class MediaServiceImpl implements IMediaService { |
| | | |
| | | private final static Logger logger = LoggerFactory.getLogger(MediaServiceImpl.class); |
| | | |
| | | @Autowired |
| | | private IRedisCatchStorage redisCatchStorage; |
| | | |
| | | @Autowired |
| | | private IVideoManagerStorager storager; |
| | | |
| | | @Autowired |
| | | private IMediaServerService mediaServerService; |
| | | |
| | | |
| | | @Autowired |
| | | private MediaConfig mediaConfig; |
| | | |
| | | @Autowired |
| | | private ZLMRESTfulUtils zlmresTfulUtils; |
| | | private IStreamProxyService streamProxyService; |
| | | |
| | | @Autowired |
| | | private UserSetting userSetting; |
| | | |
| | | @Autowired |
| | | private RedisTemplate<Object, Object> redisTemplate; |
| | | |
| | | @Autowired |
| | | private IUserService userService; |
| | | |
| | | @Autowired |
| | | private IInviteStreamService inviteStreamService; |
| | | |
| | | @Autowired |
| | | private VideoStreamSessionManager sessionManager; |
| | | |
| | | @Autowired |
| | | private IVideoManagerStorage storager; |
| | | |
| | | @Autowired |
| | | private ZLMMediaListManager zlmMediaListManager; |
| | | |
| | | |
| | | |
| | | @Override |
| | | public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks) { |
| | | return getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null); |
| | | public StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServerItem, String app, String stream, MediaInfo mediaInfo, String callId) { |
| | | return getStreamInfoByAppAndStream(mediaServerItem, app, stream, mediaInfo, null, callId, true); |
| | | } |
| | | |
| | | @Override |
| | | public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr) { |
| | | public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr, boolean authority) { |
| | | StreamInfo streamInfo = null; |
| | | if (mediaServerId == null) { |
| | | mediaServerId = mediaConfig.getId(); |
| | | } |
| | | MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);; |
| | | MediaServer mediaInfo = mediaServerService.getOne(mediaServerId); |
| | | if (mediaInfo == null) { |
| | | return null; |
| | | } |
| | | JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaInfo, app, stream); |
| | | if (mediaList != null) { |
| | | if (mediaList.getInteger("code") == 0) { |
| | | JSONArray data = mediaList.getJSONArray("data"); |
| | | if (data == null) return null; |
| | | JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class); |
| | | JSONArray tracks = mediaJSON.getJSONArray("tracks"); |
| | | streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks); |
| | | } |
| | | String calld = null; |
| | | StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream); |
| | | if (streamAuthorityInfo != null) { |
| | | calld = streamAuthorityInfo.getCallId(); |
| | | } |
| | | return streamInfo; |
| | | List<StreamInfo> streamInfoList = mediaServerService.getMediaList(mediaInfo, app, stream, calld); |
| | | if (streamInfoList.isEmpty()) { |
| | | return null; |
| | | }else { |
| | | return streamInfoList.get(0); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | @Override |
| | | public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId) { |
| | | return getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, null); |
| | | public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority) { |
| | | return getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, null, authority); |
| | | } |
| | | |
| | | @Override |
| | | public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String addr) { |
| | | public StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServer, String app, String stream, MediaInfo mediaInfo, String addr, String callId, boolean isPlay) { |
| | | StreamInfo streamInfoResult = new StreamInfo(); |
| | | streamInfoResult.setStream(stream); |
| | | streamInfoResult.setApp(app); |
| | | if (addr == null) { |
| | | addr = mediaInfo.getStreamIp(); |
| | | } |
| | | streamInfoResult.setMediaServerId(mediaInfo.getId()); |
| | | streamInfoResult.setRtmp(String.format("rtmp://%s:%s/%s/%s", addr, mediaInfo.getRtmpPort(), app, stream)); |
| | | if (mediaInfo.getRtmpSSlPort() != 0) { |
| | | streamInfoResult.setRtmps(String.format("rtmps://%s:%s/%s/%s", addr, mediaInfo.getRtmpSSlPort(), app, stream)); |
| | | } |
| | | streamInfoResult.setRtsp(String.format("rtsp://%s:%s/%s/%s", addr, mediaInfo.getRtspPort(), app, stream)); |
| | | if (mediaInfo.getRtspSSLPort() != 0) { |
| | | streamInfoResult.setRtsps(String.format("rtsps://%s:%s/%s/%s", addr, mediaInfo.getRtspSSLPort(), app, stream)); |
| | | } |
| | | streamInfoResult.setFlv(String.format("http://%s:%s/%s/%s.live.flv", addr, mediaInfo.getHttpPort(), app, stream)); |
| | | streamInfoResult.setWs_flv(String.format("ws://%s:%s/%s/%s.live.flv", addr, mediaInfo.getHttpPort(), app, stream)); |
| | | streamInfoResult.setHls(String.format("http://%s:%s/%s/%s/hls.m3u8", addr, mediaInfo.getHttpPort(), app, stream)); |
| | | streamInfoResult.setWs_hls(String.format("ws://%s:%s/%s/%s/hls.m3u8", addr, mediaInfo.getHttpPort(), app, stream)); |
| | | streamInfoResult.setFmp4(String.format("http://%s:%s/%s/%s.live.mp4", addr, mediaInfo.getHttpPort(), app, stream)); |
| | | streamInfoResult.setWs_fmp4(String.format("ws://%s:%s/%s/%s.live.mp4", addr, mediaInfo.getHttpPort(), app, stream)); |
| | | streamInfoResult.setTs(String.format("http://%s:%s/%s/%s.live.ts", addr, mediaInfo.getHttpPort(), app, stream)); |
| | | streamInfoResult.setWs_ts(String.format("ws://%s:%s/%s/%s.live.ts", addr, mediaInfo.getHttpPort(), app, stream)); |
| | | if (mediaInfo.getHttpSSlPort() != 0) { |
| | | streamInfoResult.setHttps_flv(String.format("https://%s:%s/%s/%s.live.flv", addr, mediaInfo.getHttpSSlPort(), app, stream)); |
| | | streamInfoResult.setWss_flv(String.format("wss://%s:%s/%s/%s.live.flv", addr, mediaInfo.getHttpSSlPort(), app, stream)); |
| | | streamInfoResult.setHttps_hls(String.format("https://%s:%s/%s/%s/hls.m3u8", addr, mediaInfo.getHttpSSlPort(), app, stream)); |
| | | streamInfoResult.setWss_hls(String.format("wss://%s:%s/%s/%s/hls.m3u8", addr, mediaInfo.getHttpSSlPort(), app, stream)); |
| | | streamInfoResult.setHttps_fmp4(String.format("https://%s:%s/%s/%s.live.mp4", addr, mediaInfo.getHttpSSlPort(), app, stream)); |
| | | streamInfoResult.setWss_fmp4(String.format("wss://%s:%s/%s/%s.live.mp4", addr, mediaInfo.getHttpSSlPort(), app, stream)); |
| | | streamInfoResult.setHttps_ts(String.format("https://%s:%s/%s/%s.live.ts", addr, mediaInfo.getHttpSSlPort(), app, stream)); |
| | | streamInfoResult.setWss_ts(String.format("wss://%s:%s/%s/%s.live.ts", addr, mediaInfo.getHttpSSlPort(), app, stream)); |
| | | streamInfoResult.setWss_ts(String.format("wss://%s:%s/%s/%s.live.ts", addr, mediaInfo.getHttpSSlPort(), app, stream)); |
| | | streamInfoResult.setRtc(String.format("https://%s:%s/index/api/webrtc?app=%s&stream=%s&type=play", mediaInfo.getStreamIp(), mediaInfo.getHttpSSlPort(), app, stream)); |
| | | addr = mediaServer.getStreamIp(); |
| | | } |
| | | |
| | | streamInfoResult.setTracks(tracks); |
| | | streamInfoResult.setIp(addr); |
| | | streamInfoResult.setMediaServerId(mediaServer.getId()); |
| | | String callIdParam = ObjectUtils.isEmpty(callId)?"":"?callId=" + callId; |
| | | streamInfoResult.setRtmp(addr, mediaServer.getRtmpPort(),mediaServer.getRtmpSSlPort(), app, stream, callIdParam); |
| | | streamInfoResult.setRtsp(addr, mediaServer.getRtspPort(),mediaServer.getRtspSSLPort(), app, stream, callIdParam); |
| | | streamInfoResult.setFlv(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam); |
| | | streamInfoResult.setFmp4(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam); |
| | | streamInfoResult.setHls(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam); |
| | | streamInfoResult.setTs(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam); |
| | | streamInfoResult.setRtc(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam, isPlay); |
| | | |
| | | streamInfoResult.setMediaInfo(mediaInfo); |
| | | return streamInfoResult; |
| | | } |
| | | |
| | | @Override |
| | | public boolean authenticatePlay(String app, String stream, String callId) { |
| | | if (app == null || stream == null) { |
| | | return false; |
| | | } |
| | | if ("rtp".equals(app)) { |
| | | return true; |
| | | } |
| | | StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream); |
| | | return (streamAuthorityInfo != null && streamAuthorityInfo.getCallId() != null && !streamAuthorityInfo.getCallId().equals(callId)); |
| | | } |
| | | |
| | | @Override |
| | | public ResultForOnPublish authenticatePublish(MediaServer mediaServer, String app, String stream, String params) { |
| | | // 推流鉴权的处理 |
| | | if (!"rtp".equals(app)) { |
| | | StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(app, stream); |
| | | if (streamProxyItem != null) { |
| | | ResultForOnPublish result = new ResultForOnPublish(); |
| | | result.setEnable_audio(streamProxyItem.isEnableAudio()); |
| | | result.setEnable_mp4(streamProxyItem.isEnableMp4()); |
| | | return result; |
| | | } |
| | | if (userSetting.getPushAuthority()) { |
| | | // 对于推流进行鉴权 |
| | | Map<String, String> paramMap = urlParamToMap(params); |
| | | // 推流鉴权 |
| | | if (params == null) { |
| | | logger.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)"); |
| | | throw new ControllerException(ErrorCode.ERROR401.getCode(), "Unauthorized"); |
| | | } |
| | | |
| | | String sign = paramMap.get("sign"); |
| | | if (sign == null) { |
| | | logger.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)"); |
| | | throw new ControllerException(ErrorCode.ERROR401.getCode(), "Unauthorized"); |
| | | } |
| | | // 推流自定义播放鉴权码 |
| | | String callId = paramMap.get("callId"); |
| | | // 鉴权配置 |
| | | boolean hasAuthority = userService.checkPushAuthority(callId, sign); |
| | | if (!hasAuthority) { |
| | | logger.info("推流鉴权失败: sign 无权限: callId={}. sign={}", callId, sign); |
| | | throw new ControllerException(ErrorCode.ERROR401.getCode(), "Unauthorized"); |
| | | } |
| | | StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(app, stream, mediaServer.getId()); |
| | | streamAuthorityInfo.setCallId(callId); |
| | | streamAuthorityInfo.setSign(sign); |
| | | // 鉴权通过 |
| | | redisCatchStorage.updateStreamAuthorityInfo(app, stream, streamAuthorityInfo); |
| | | } |
| | | } else { |
| | | zlmMediaListManager.sendStreamEvent(app, stream, mediaServer.getId()); |
| | | } |
| | | |
| | | |
| | | ResultForOnPublish result = new ResultForOnPublish(); |
| | | result.setEnable_audio(true); |
| | | |
| | | |
| | | // 是否录像 |
| | | if ("rtp".equals(app)) { |
| | | result.setEnable_mp4(userSetting.getRecordSip()); |
| | | } else { |
| | | result.setEnable_mp4(userSetting.isRecordPushLive()); |
| | | } |
| | | // 国标流 |
| | | if ("rtp".equals(app)) { |
| | | |
| | | InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, stream); |
| | | |
| | | // 单端口模式下修改流 ID |
| | | if (!mediaServer.isRtpEnable() && inviteInfo == null) { |
| | | String ssrc = String.format("%010d", Long.parseLong(stream, 16)); |
| | | inviteInfo = inviteStreamService.getInviteInfoBySSRC(ssrc); |
| | | if (inviteInfo != null) { |
| | | result.setStream_replace(inviteInfo.getStream()); |
| | | logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", stream, inviteInfo.getStream()); |
| | | } |
| | | } |
| | | |
| | | // 设置音频信息及录制信息 |
| | | List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, stream); |
| | | if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) { |
| | | |
| | | // 为录制国标模拟一个鉴权信息, 方便后续写入录像文件时使用 |
| | | StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(app, stream, mediaServer.getId()); |
| | | streamAuthorityInfo.setApp(app); |
| | | streamAuthorityInfo.setStream(ssrcTransactionForAll.get(0).getStream()); |
| | | streamAuthorityInfo.setCallId(ssrcTransactionForAll.get(0).getSipTransactionInfo().getCallId()); |
| | | |
| | | redisCatchStorage.updateStreamAuthorityInfo(app, ssrcTransactionForAll.get(0).getStream(), streamAuthorityInfo); |
| | | |
| | | String deviceId = ssrcTransactionForAll.get(0).getDeviceId(); |
| | | String channelId = ssrcTransactionForAll.get(0).getChannelId(); |
| | | DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); |
| | | if (deviceChannel != null) { |
| | | result.setEnable_audio(deviceChannel.isHasAudio()); |
| | | } |
| | | // 如果是录像下载就设置视频间隔十秒 |
| | | if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) { |
| | | // 获取录像的总时长,然后设置为这个视频的时长 |
| | | InviteInfo inviteInfoForDownload = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, stream); |
| | | if (inviteInfoForDownload != null && inviteInfoForDownload.getStreamInfo() != null) { |
| | | String startTime = inviteInfoForDownload.getStreamInfo().getStartTime(); |
| | | String endTime = inviteInfoForDownload.getStreamInfo().getEndTime(); |
| | | long difference = DateUtil.getDifference(startTime, endTime) / 1000; |
| | | result.setMp4_max_second((int) difference); |
| | | result.setEnable_mp4(true); |
| | | // 设置为2保证得到的mp4的时长是正常的 |
| | | result.setModify_stamp(2); |
| | | } |
| | | } |
| | | // 如果是talk对讲,则默认获取声音 |
| | | if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) { |
| | | result.setEnable_audio(true); |
| | | } |
| | | } |
| | | } else if (app.equals("broadcast")) { |
| | | result.setEnable_audio(true); |
| | | } else if (app.equals("talk")) { |
| | | result.setEnable_audio(true); |
| | | } |
| | | if (app.equalsIgnoreCase("rtp")) { |
| | | String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + "_" + stream; |
| | | OtherRtpSendInfo otherRtpSendInfo = (OtherRtpSendInfo) redisTemplate.opsForValue().get(receiveKey); |
| | | |
| | | String receiveKeyForPS = VideoManagerConstants.WVP_OTHER_RECEIVE_PS_INFO + userSetting.getServerId() + "_" + stream; |
| | | OtherPsSendInfo otherPsSendInfo = (OtherPsSendInfo) redisTemplate.opsForValue().get(receiveKeyForPS); |
| | | if (otherRtpSendInfo != null || otherPsSendInfo != null) { |
| | | result.setEnable_mp4(true); |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private Map<String, String> urlParamToMap(String params) { |
| | | HashMap<String, String> map = new HashMap<>(); |
| | | if (ObjectUtils.isEmpty(params)) { |
| | | return map; |
| | | } |
| | | String[] paramsArray = params.split("&"); |
| | | if (paramsArray.length == 0) { |
| | | return map; |
| | | } |
| | | for (String param : paramsArray) { |
| | | String[] paramArray = param.split("="); |
| | | if (paramArray.length == 2) { |
| | | map.put(paramArray[0], paramArray[1]); |
| | | } |
| | | } |
| | | return map; |
| | | } |
| | | } |