| | |
| | | package com.genersoft.iot.vmp.common; |
| | | |
| | | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | |
| | | /** |
| | | * 记录每次发送invite消息的状态 |
| | |
| | | public void setStreamMode(String streamMode) { |
| | | this.streamMode = streamMode; |
| | | } |
| | | |
| | | |
| | | /*=========================设备主子码流逻辑START====================*/ |
| | | @Schema(description = "是否为子码流(true-是,false-主码流)") |
| | | private boolean subStream; |
| | | |
| | | public boolean isSubStream() { |
| | | return subStream; |
| | | } |
| | | |
| | | public void setSubStream(boolean subStream) { |
| | | this.subStream = subStream; |
| | | } |
| | | |
| | | public static InviteInfo getInviteInfo(String deviceId, String channelId,Boolean isSubStream, String stream, SSRCInfo ssrcInfo, |
| | | String receiveIp, Integer receivePort, String streamMode, |
| | | InviteSessionType type, InviteSessionStatus status) { |
| | | InviteInfo inviteInfo = new InviteInfo(); |
| | | inviteInfo.setDeviceId(deviceId); |
| | | inviteInfo.setChannelId(channelId); |
| | | inviteInfo.setStream(stream); |
| | | inviteInfo.setSsrcInfo(ssrcInfo); |
| | | inviteInfo.setReceiveIp(receiveIp); |
| | | inviteInfo.setReceivePort(receivePort); |
| | | inviteInfo.setStreamMode(streamMode); |
| | | inviteInfo.setType(type); |
| | | inviteInfo.setStatus(status); |
| | | if(isSubStream != null){ |
| | | inviteInfo.setSubStream(isSubStream); |
| | | } |
| | | return inviteInfo; |
| | | } |
| | | /*=========================设备主子码流逻辑END====================*/ |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | } |
| | | return instance; |
| | | } |
| | | |
| | | |
| | | /*=========================设备主子码流逻辑START====================*/ |
| | | @Schema(description = "是否为子码流(true-是,false-主码流)") |
| | | private boolean subStream; |
| | | |
| | | public boolean isSubStream() { |
| | | return subStream; |
| | | } |
| | | |
| | | public void setSubStream(boolean subStream) { |
| | | this.subStream = subStream; |
| | | } |
| | | |
| | | public static String getPlayStream(String deviceId,String channelId,boolean isSubStream){ |
| | | String streamId; |
| | | if(isSubStream){ |
| | | streamId = String.format("%s_%s_%s","sub",deviceId, channelId); |
| | | }else { |
| | | streamId = String.format("%s_%s_%s","main", deviceId, channelId); |
| | | } |
| | | return streamId; |
| | | } |
| | | |
| | | /*=========================设备主子码流逻辑END====================*/ |
| | | |
| | | |
| | | } |
| | |
| | | package com.genersoft.iot.vmp.conf; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import org.springframework.core.annotation.Order; |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | import org.springframework.stereotype.Component; |
| | |
| | | |
| | | private int platformPlayTimeout = 60000; |
| | | |
| | | private Boolean interfaceAuthentication = Boolean.TRUE; |
| | | private Boolean interfaceAuthentication = Boolean.FALSE; |
| | | |
| | | private Boolean recordPushLive = Boolean.TRUE; |
| | | private Boolean recordPushLive = Boolean.FALSE; |
| | | |
| | | private Boolean recordSip = Boolean.TRUE; |
| | | private Boolean recordSip = Boolean.FALSE; |
| | | |
| | | private Boolean logInDatebase = Boolean.TRUE; |
| | | |
| | |
| | | private String recordPath = null; |
| | | |
| | | private String thirdPartyGBIdReg = "[\\s\\S]*"; |
| | | |
| | | |
| | | |
| | | private List<String> interfaceAuthenticationExcludes = new ArrayList<>(); |
| | | |
| | |
| | | public void setSqlLog(Boolean sqlLog) { |
| | | this.sqlLog = sqlLog; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | private SipTransactionInfo sipTransactionInfo; |
| | | |
| | | |
| | | |
| | | |
| | | public String getDeviceId() { |
| | | return deviceId; |
| | | } |
| | |
| | | public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { |
| | | this.sipTransactionInfo = sipTransactionInfo; |
| | | } |
| | | |
| | | /*======================设备主子码流逻辑START=========================*/ |
| | | @Schema(description = "开启主子码流切换的开关(false-不开启,true-开启)") |
| | | private boolean switchPrimarySubStream; |
| | | |
| | | public boolean isSwitchPrimarySubStream() { |
| | | return switchPrimarySubStream; |
| | | } |
| | | |
| | | public void setSwitchPrimarySubStream(boolean switchPrimarySubStream) { |
| | | this.switchPrimarySubStream = switchPrimarySubStream; |
| | | } |
| | | |
| | | /*======================设备主子码流逻辑END=========================*/ |
| | | |
| | | |
| | | } |
| | |
| | | map.remove(msg.getKey());
|
| | | }
|
| | | }
|
| | |
|
| | | /*============================设备主子码流逻辑START========================*/
|
| | | public static String getPlayKey(String deviceId,String channelId,boolean deviceSwitchSubStream,boolean isSubStream){
|
| | | String key = null;
|
| | | if(deviceSwitchSubStream){
|
| | | key = CALLBACK_CMD_PLAY + isSubStream + deviceId + channelId;
|
| | | }else {
|
| | | key = CALLBACK_CMD_PLAY +deviceId + channelId;
|
| | | }
|
| | | return key;
|
| | | }
|
| | |
|
| | | public static String getSnapKey(String deviceId,String channelId,boolean deviceSwitchSubStream,boolean isSubStream){
|
| | | String key = null;
|
| | | if(deviceSwitchSubStream){
|
| | | key = CALLBACK_CMD_SNAP + isSubStream + deviceId + channelId;
|
| | | }else {
|
| | | key = CALLBACK_CMD_SNAP +deviceId + channelId;
|
| | | }
|
| | | return key;
|
| | | }
|
| | |
|
| | |
|
| | | /*============================设备主子码流逻辑END========================*/
|
| | |
|
| | |
|
| | | }
|
| | |
| | | * @param device 视频设备
|
| | | * @param channelId 预览通道
|
| | | */
|
| | | void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
|
| | | void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,boolean isSubStream, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
|
| | |
|
| | | /**
|
| | | * 请求回放视频流
|
| | |
| | | * @param errorEvent sip错误订阅
|
| | | */
|
| | | @Override
|
| | | public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
|
| | | public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,boolean isSubStream,
|
| | | ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
|
| | | String stream = ssrcInfo.getStream();
|
| | |
|
| | |
| | | }
|
| | | }
|
| | |
|
| | | if( device.isSwitchPrimarySubStream() ){
|
| | | if("TP-LINK".equals(device.getManufacturer())){
|
| | | if (isSubStream){
|
| | | content.append("a=streamMode:sub\r\n");
|
| | | }else {
|
| | | content.append("a=streamMode:main\r\n");
|
| | | }
|
| | | }else {
|
| | | if (isSubStream){
|
| | | content.append("a=streamprofile:1\r\n");
|
| | | }else {
|
| | | content.append("a=streamprofile:0\r\n");
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
|
| | | // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率
|
| | | // content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 未发现支持此特性的设备
|
| | |
| | | // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值
|
| | | ResponseEvent responseEvent = (ResponseEvent) e.event;
|
| | | SIPResponse response = (SIPResponse) responseEvent.getResponse();
|
| | | streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY);
|
| | | if(device.isSwitchPrimarySubStream()){
|
| | | streamSession.put(device.getDeviceId(), channelId, "switch-play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY);
|
| | | }else {
|
| | | streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY);
|
| | | }
|
| | | okEvent.response(e);
|
| | | });
|
| | | }
|
| | |
| | | // 可能是设备主动停止 |
| | | Device device = storager.queryVideoDeviceByChannelId(platformGbId); |
| | | if (device != null) { |
| | | storager.stopPlay(device.getDeviceId(), channelId); |
| | | SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null); |
| | | SsrcTransaction ssrcTransactionForPlay = null; |
| | | if (device.isSwitchPrimarySubStream() ) { |
| | | ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "switch-play", null); |
| | | } else { |
| | | storager.stopPlay(device.getDeviceId(), channelId); |
| | | ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null); |
| | | } |
| | | if (ssrcTransactionForPlay != null){ |
| | | if (ssrcTransactionForPlay.getCallId().equals(callIdHeader.getCallId())){ |
| | | // 释放ssrc |
| | |
| | | } |
| | | streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlay.getStream()); |
| | | } |
| | | InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); |
| | | |
| | | if (inviteInfo != null) { |
| | | InviteInfo inviteInfo = null; |
| | | if (device.isSwitchPrimarySubStream() ) { |
| | | String streamType = ssrcTransactionForPlay.getStream().split("_")[0]; |
| | | boolean isSubStream = "sub".equals(streamType); |
| | | inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); |
| | | inviteStreamService.removeInviteInfo(inviteInfo.getType(),inviteInfo.getDeviceId(),inviteInfo.getChannelId(),isSubStream,inviteInfo.getStream()); |
| | | }else { |
| | | inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); |
| | | inviteStreamService.removeInviteInfo(inviteInfo); |
| | | } |
| | | if (inviteInfo != null) { |
| | | if (inviteInfo.getStreamInfo() != null) { |
| | | mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStream()); |
| | | } |
| | |
| | | } |
| | | sendRtpItem.setStreamId(streamId); |
| | | redisCatchStorage.updateSendRTPSever(sendRtpItem); |
| | | playService.play(mediaServerItem, device.getDeviceId(), channelId, ((code, msg, data) -> { |
| | | playService.play(mediaServerItem, device.getDeviceId(), channelId,false, ((code, msg, data) -> { |
| | | if (code == InviteErrorCode.SUCCESS.getCode()){ |
| | | hookEvent.run(code, msg, data); |
| | | }else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()){ |
| | |
| | | }
|
| | |
|
| | | if ("rtp".equals(param.getApp()) && !param.isRegist()) {
|
| | | InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
|
| | | if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) {
|
| | | inviteStreamService.removeInviteInfo(inviteInfo);
|
| | | storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
|
| | | if(param.getStream().split("_").length == 3){
|
| | | boolean isSubStream = "sub".equals(param.getStream().split("_")[0]);
|
| | | InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream(), isSubStream);
|
| | | if(inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY )){
|
| | | inviteStreamService.removeInviteInfo(inviteInfo.getType(),inviteInfo.getDeviceId(),
|
| | | inviteInfo.getChannelId(),inviteInfo.isSubStream(),inviteInfo.getStream());
|
| | | }
|
| | | }else {
|
| | | InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
|
| | | if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) {
|
| | | inviteStreamService.removeInviteInfo(inviteInfo);
|
| | | storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
|
| | | }
|
| | | }
|
| | | } else {
|
| | | if (!"rtp".equals(param.getApp())) {
|
| | |
| | | Device device = deviceService.getDevice(inviteInfo.getDeviceId());
|
| | | if (device != null) {
|
| | | try {
|
| | | if (inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()) != null) {
|
| | | InviteInfo info = null;
|
| | | if(device.isSwitchPrimarySubStream()){
|
| | | boolean isSubStream = "sub".equals(param.getStream().split("_")[0]);
|
| | | info = inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(),isSubStream, inviteInfo.getStream());
|
| | | }else {
|
| | | info = inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream());
|
| | |
|
| | | }
|
| | |
|
| | | if (info != null) {
|
| | | cmder.streamByeCmd(device, inviteInfo.getChannelId(),
|
| | | inviteInfo.getStream(), null);
|
| | | }
|
| | |
| | | }
|
| | | }
|
| | |
|
| | | inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
|
| | | inviteInfo.getChannelId(), inviteInfo.getStream());
|
| | | storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
|
| | | if(device.isSwitchPrimarySubStream()){
|
| | | boolean isSubStream = "sub".equals(param.getStream().split("_")[0]);
|
| | | inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
|
| | | inviteInfo.getChannelId(),isSubStream, inviteInfo.getStream());
|
| | | }else {
|
| | | inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
|
| | | inviteInfo.getChannelId(), inviteInfo.getStream());
|
| | | storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
|
| | | }
|
| | | return ret;
|
| | | }
|
| | | } else {
|
| | |
| | |
|
| | | if ("rtp".equals(param.getApp())) {
|
| | | String[] s = param.getStream().split("_");
|
| | | if (!mediaInfo.isRtpEnable() || s.length != 2) {
|
| | | if (!mediaInfo.isRtpEnable() ) {
|
| | | defaultResult.setResult(HookResult.SUCCESS());
|
| | | return defaultResult;
|
| | | }else if(s.length != 2 && s.length != 3 ){
|
| | | defaultResult.setResult(HookResult.SUCCESS());
|
| | | return defaultResult;
|
| | | }
|
| | | String deviceId = s[0];
|
| | | String channelId = s[1];
|
| | | String deviceId = null;
|
| | | String channelId = null;
|
| | | boolean isSubStream = false;
|
| | | if (s[0].length() < 20) {
|
| | | if ("sub".equals(s[0])) {
|
| | | isSubStream = true;
|
| | | }
|
| | | deviceId = s[1];
|
| | | channelId = s[2];
|
| | | } else {
|
| | | deviceId = s[0];
|
| | | channelId = s[1];
|
| | | }
|
| | | Device device = redisCatchStorage.getDevice(deviceId);
|
| | | if (device == null) {
|
| | | defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
|
| | |
| | | logger.info("[ZLM HOOK] 流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
|
| | |
|
| | | RequestMessage msg = new RequestMessage();
|
| | | String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
|
| | | String key = DeferredResultHolder.getPlayKey(deviceId, channelId, device.isSwitchPrimarySubStream(), isSubStream);
|
| | | boolean exist = resultHolder.exist(key, null);
|
| | | msg.setKey(key);
|
| | | String uuid = UUID.randomUUID().toString();
|
| | |
| | | resultHolder.put(key, uuid, result);
|
| | |
|
| | | if (!exist) {
|
| | | playService.play(mediaInfo, deviceId, channelId, (code, message, data) -> {
|
| | | playService.play(mediaInfo, deviceId, channelId,isSubStream, (code, message, data) -> {
|
| | | msg.setData(new HookResult(code, message));
|
| | | resultHolder.invokeResult(msg);
|
| | | });
|
| | |
| | | import com.genersoft.iot.vmp.common.InviteSessionType; |
| | | import com.genersoft.iot.vmp.service.bean.ErrorCallback; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 记录国标点播的状态,包括实时预览,下载,录像回放 |
| | | */ |
| | |
| | | * 统计同一个zlm下的国标收流个数 |
| | | */ |
| | | int getStreamInfoCount(String mediaServerId); |
| | | |
| | | |
| | | /*======================设备主子码流逻辑START=========================*/ |
| | | /** |
| | | * 获取点播的状态信息 |
| | | */ |
| | | InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, |
| | | String deviceId, |
| | | String channelId,boolean isSubStream); |
| | | |
| | | void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId,boolean isSubStream); |
| | | |
| | | InviteInfo getInviteInfo(InviteSessionType type, |
| | | String deviceId, |
| | | String channelId, |
| | | boolean isSubStream, |
| | | String stream); |
| | | |
| | | void removeInviteInfo(InviteSessionType type, |
| | | String deviceId, |
| | | String channelId, |
| | | boolean isSubStream, |
| | | String stream); |
| | | |
| | | void once(InviteSessionType type, String deviceId, String channelId,boolean isSubStream, String stream, ErrorCallback<Object> callback); |
| | | |
| | | void call(InviteSessionType type, String deviceId, String channelId,boolean isSubStream, String stream, int code, String msg, Object data); |
| | | |
| | | void updateInviteInfoSub(InviteInfo inviteInfo); |
| | | |
| | | /** |
| | | * 获取点播的状态信息 |
| | | */ |
| | | InviteInfo getInviteInfoByStream(InviteSessionType type, String stream,boolean isSubStream); |
| | | |
| | | /** |
| | | * 获取点播的状态信息 |
| | | */ |
| | | List<Object> getInviteInfos(InviteSessionType type, |
| | | String deviceId, |
| | | String channelId, |
| | | String stream); |
| | | /*======================设备主子码流逻辑END=========================*/ |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | */ |
| | | public interface IPlayService { |
| | | |
| | | void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, |
| | | void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,boolean isSubStream, |
| | | ErrorCallback<Object> callback); |
| | | SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, ErrorCallback<Object> callback); |
| | | SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId,boolean isSubStream, ErrorCallback<Object> callback); |
| | | |
| | | MediaServerItem getNewMediaServerItem(Device device); |
| | | |
| | |
| | | |
| | | void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; |
| | | |
| | | void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback); |
| | | void getSnap(String deviceId, String channelId, String fileName,boolean isSubStream, ErrorCallback errorCallback); |
| | | } |
| | |
| | | package com.genersoft.iot.vmp.service.impl; |
| | | |
| | | import com.genersoft.iot.vmp.common.InviteSessionType; |
| | | import com.genersoft.iot.vmp.common.VideoManagerConstants; |
| | | import com.genersoft.iot.vmp.conf.DynamicTask; |
| | | import com.genersoft.iot.vmp.conf.UserSetting; |
| | | import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; |
| | | import com.genersoft.iot.vmp.gb28181.bean.*; |
| | | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| | | import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; |
| | | import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask; |
| | | import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler; |
| | | import com.genersoft.iot.vmp.service.IDeviceChannelService; |
| | | import com.genersoft.iot.vmp.service.IDeviceService; |
| | |
| | | |
| | | private final static Logger logger = LoggerFactory.getLogger(DeviceServiceImpl.class); |
| | | |
| | | @Autowired |
| | | private SIPCommander cmder; |
| | | @Autowired |
| | | private DynamicTask dynamicTask; |
| | | |
| | |
| | | } |
| | | sync(device); |
| | | }else { |
| | | |
| | | if (deviceInDb != null) { |
| | | device.setSwitchPrimarySubStream(deviceInDb.isSwitchPrimarySubStream()); |
| | | } |
| | | if(!device.isOnLine()){ |
| | | device.setOnLine(true); |
| | | device.setCreateTime(now); |
| | |
| | | logger.warn("更新设备时未找到设备信息"); |
| | | return; |
| | | } |
| | | if(deviceInStore.isSwitchPrimarySubStream() != device.isSwitchPrimarySubStream()){ |
| | | //当修改设备的主子码流开关时,需要校验是否存在流,如果存在流则直接关闭 |
| | | List<SsrcTransaction> ssrcTransactionForAll = streamSession.getSsrcTransactionForAll(device.getDeviceId(), null, null, null); |
| | | for (SsrcTransaction ssrcTransaction: ssrcTransactionForAll) { |
| | | try { |
| | | cmder.streamByeCmd(device, ssrcTransaction.getChannelId(), ssrcTransaction.getStream(), null, null); |
| | | } catch (InvalidArgumentException | SsrcTransactionNotFoundException | ParseException | SipException e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | } |
| | | deviceChannelMapper.clearPlay(device.getDeviceId()); |
| | | inviteStreamService.clearInviteInfo(device.getDeviceId()); |
| | | } |
| | | |
| | | if (!ObjectUtils.isEmpty(device.getName())) { |
| | | deviceInStore.setName(device.getName()); |
| | | } |
| | |
| | | } |
| | | return count; |
| | | } |
| | | |
| | | /*======================设备主子码流逻辑START=========================*/ |
| | | |
| | | @Override |
| | | public InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, String deviceId, String channelId, boolean isSubStream) { |
| | | return getInviteInfo(type, deviceId, channelId,isSubStream, null); |
| | | } |
| | | |
| | | @Override |
| | | public void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId, boolean isSubStream) { |
| | | removeInviteInfo(inviteSessionType, deviceId, channelId,isSubStream, null); |
| | | } |
| | | |
| | | @Override |
| | | public InviteInfo getInviteInfo(InviteSessionType type, String deviceId, String channelId,boolean isSubStream, String stream) { |
| | | String key = VideoManagerConstants.INVITE_PREFIX + |
| | | "_" + (type != null ? type : "*") + |
| | | "_" + (isSubStream ? "sub" : "main") + |
| | | "_" + (deviceId != null ? deviceId : "*") + |
| | | "_" + (channelId != null ? channelId : "*") + |
| | | "_" + (stream != null ? stream : "*"); |
| | | List<Object> scanResult = RedisUtil.scan(redisTemplate, key); |
| | | if (scanResult.size() != 1) { |
| | | return null; |
| | | } |
| | | return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0)); |
| | | } |
| | | |
| | | @Override |
| | | public void removeInviteInfo(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream) { |
| | | String scanKey = VideoManagerConstants.INVITE_PREFIX + |
| | | "_" + (type != null ? type : "*") + |
| | | "_" + (isSubStream ? "sub" : "main") + |
| | | "_" + (deviceId != null ? deviceId : "*") + |
| | | "_" + (channelId != null ? channelId : "*") + |
| | | "_" + (stream != null ? stream : "*"); |
| | | List<Object> scanResult = RedisUtil.scan(redisTemplate, scanKey); |
| | | if (scanResult.size() > 0) { |
| | | for (Object keyObj : scanResult) { |
| | | String key = (String) keyObj; |
| | | InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(key); |
| | | if (inviteInfo == null) { |
| | | continue; |
| | | } |
| | | redisTemplate.delete(key); |
| | | inviteErrorCallbackMap.remove(buildKey(type, deviceId, channelId, inviteInfo.getStream())); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void once(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream, ErrorCallback<Object> callback) { |
| | | String key = buildSubStreamKey(type, deviceId, channelId,isSubStream, stream); |
| | | List<ErrorCallback<Object>> callbacks = inviteErrorCallbackMap.get(key); |
| | | if (callbacks == null) { |
| | | callbacks = new CopyOnWriteArrayList<>(); |
| | | inviteErrorCallbackMap.put(key, callbacks); |
| | | } |
| | | callbacks.add(callback); |
| | | } |
| | | |
| | | @Override |
| | | public void call(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream, int code, String msg, Object data) { |
| | | String key = buildSubStreamKey(type, deviceId, channelId,isSubStream, stream); |
| | | List<ErrorCallback<Object>> callbacks = inviteErrorCallbackMap.get(key); |
| | | if (callbacks == null) { |
| | | return; |
| | | } |
| | | for (ErrorCallback<Object> callback : callbacks) { |
| | | callback.run(code, msg, data); |
| | | } |
| | | inviteErrorCallbackMap.remove(key); |
| | | } |
| | | |
| | | |
| | | private String buildSubStreamKey(InviteSessionType type, String deviceId, String channelId, boolean isSubStream, String stream) { |
| | | String key = type + "_" + (isSubStream ? "sub":"main") + "_" + deviceId + "_" + channelId; |
| | | // 如果ssrc为null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite |
| | | if (stream != null) { |
| | | key += ("_" + stream); |
| | | } |
| | | return key; |
| | | } |
| | | @Override |
| | | public void updateInviteInfoSub(InviteInfo inviteInfo) { |
| | | if (inviteInfo == null || (inviteInfo.getDeviceId() == null || inviteInfo.getChannelId() == null)) { |
| | | logger.warn("[更新Invite信息],参数不全: {}", JSON.toJSON(inviteInfo)); |
| | | return; |
| | | } |
| | | InviteInfo inviteInfoForUpdate = null; |
| | | |
| | | if (InviteSessionStatus.ready == inviteInfo.getStatus()) { |
| | | if (inviteInfo.getDeviceId() == null |
| | | || inviteInfo.getChannelId() == null |
| | | || inviteInfo.getType() == null |
| | | || inviteInfo.getStream() == null |
| | | ) { |
| | | return; |
| | | } |
| | | inviteInfoForUpdate = inviteInfo; |
| | | } else { |
| | | InviteInfo inviteInfoInRedis = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), |
| | | inviteInfo.getChannelId(),inviteInfo.isSubStream(), inviteInfo.getStream()); |
| | | if (inviteInfoInRedis == null) { |
| | | logger.warn("[更新Invite信息],未从缓存中读取到Invite信息: deviceId: {}, channel: {}, stream: {}", |
| | | inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); |
| | | return; |
| | | } |
| | | if (inviteInfo.getStreamInfo() != null) { |
| | | inviteInfoInRedis.setStreamInfo(inviteInfo.getStreamInfo()); |
| | | } |
| | | if (inviteInfo.getSsrcInfo() != null) { |
| | | inviteInfoInRedis.setSsrcInfo(inviteInfo.getSsrcInfo()); |
| | | } |
| | | if (inviteInfo.getStreamMode() != null) { |
| | | inviteInfoInRedis.setStreamMode(inviteInfo.getStreamMode()); |
| | | } |
| | | if (inviteInfo.getReceiveIp() != null) { |
| | | inviteInfoInRedis.setReceiveIp(inviteInfo.getReceiveIp()); |
| | | } |
| | | if (inviteInfo.getReceivePort() != null) { |
| | | inviteInfoInRedis.setReceivePort(inviteInfo.getReceivePort()); |
| | | } |
| | | if (inviteInfo.getStatus() != null) { |
| | | inviteInfoInRedis.setStatus(inviteInfo.getStatus()); |
| | | } |
| | | |
| | | inviteInfoForUpdate = inviteInfoInRedis; |
| | | |
| | | } |
| | | String key = VideoManagerConstants.INVITE_PREFIX + |
| | | "_" + inviteInfoForUpdate.getType() + |
| | | "_" + (inviteInfoForUpdate.isSubStream() ? "sub":"main") + |
| | | "_" + inviteInfoForUpdate.getDeviceId() + |
| | | "_" + inviteInfoForUpdate.getChannelId() + |
| | | "_" + inviteInfoForUpdate.getStream(); |
| | | redisTemplate.opsForValue().set(key, inviteInfoForUpdate); |
| | | } |
| | | |
| | | @Override |
| | | public InviteInfo getInviteInfoByStream(InviteSessionType type, String stream, boolean isSubStream) { |
| | | return getInviteInfo(type, null, null,isSubStream, stream); |
| | | } |
| | | |
| | | @Override |
| | | public List<Object> getInviteInfos(InviteSessionType type, String deviceId, String channelId, String stream) { |
| | | String key = VideoManagerConstants.INVITE_PREFIX + |
| | | "_" + (type != null ? type : "*") + |
| | | "_" + (deviceId != null ? deviceId : "*") + |
| | | "_" + (channelId != null ? channelId : "*") + |
| | | "_" + (stream != null ? stream : "*"); |
| | | List<Object> scanResult = RedisUtil.scan(redisTemplate, key); |
| | | return scanResult; |
| | | } |
| | | |
| | | /*======================设备主子码流逻辑END=========================*/ |
| | | |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | |
| | | |
| | | @Override |
| | | public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, ErrorCallback<Object> callback) { |
| | | public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId,boolean isSubStream, ErrorCallback<Object> callback) { |
| | | if (mediaServerItem == null) { |
| | | throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm"); |
| | | } |
| | | |
| | | Device device = redisCatchStorage.getDevice(deviceId); |
| | | InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); |
| | | |
| | | InviteInfo inviteInfo; |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); |
| | | }else { |
| | | inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); |
| | | } |
| | | if (inviteInfo != null ) { |
| | | if (inviteInfo.getStreamInfo() == null) { |
| | | // 点播发起了但是尚未成功, 仅注册回调等待结果即可 |
| | | inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId,isSubStream, null, callback); |
| | | }else { |
| | | inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); |
| | | } |
| | | return inviteInfo.getSsrcInfo(); |
| | | }else { |
| | | StreamInfo streamInfo = inviteInfo.getStreamInfo(); |
| | | String streamId = streamInfo.getStream(); |
| | | if (streamId == null) { |
| | | callback.run(InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), "点播失败, redis缓存streamId等于null", null); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), |
| | | "点播失败, redis缓存streamId等于null", |
| | | null); |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, |
| | | InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), |
| | | "点播失败, redis缓存streamId等于null", |
| | | null); |
| | | }else { |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), |
| | | "点播失败, redis缓存streamId等于null", |
| | | null); |
| | | } |
| | | return inviteInfo.getSsrcInfo(); |
| | | } |
| | | String mediaServerId = streamInfo.getMediaServerId(); |
| | |
| | | Boolean ready = zlmrtpServerFactory.isStreamReady(mediaInfo, "rtp", streamId); |
| | | if (ready != null && ready) { |
| | | callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.SUCCESS.getCode(), |
| | | InviteErrorCode.SUCCESS.getMsg(), |
| | | streamInfo); |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, |
| | | InviteErrorCode.SUCCESS.getCode(), |
| | | InviteErrorCode.SUCCESS.getMsg(), |
| | | streamInfo); |
| | | }else { |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.SUCCESS.getCode(), |
| | | InviteErrorCode.SUCCESS.getMsg(), |
| | | streamInfo); |
| | | } |
| | | return inviteInfo.getSsrcInfo(); |
| | | }else { |
| | | // 点播发起了但是尚未成功, 仅注册回调等待结果即可 |
| | | inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); |
| | | storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); |
| | | if(device.isSwitchPrimarySubStream()) { |
| | | inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); |
| | | storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); |
| | | }else { |
| | | inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId,isSubStream, null, callback); |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | String streamId = null; |
| | | if (mediaServerItem.isRtpEnable()) { |
| | | streamId = String.format("%s_%s", device.getDeviceId(), channelId); |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | streamId = StreamInfo.getPlayStream(deviceId, channelId, isSubStream); |
| | | }else { |
| | | streamId = String.format("%s_%s", device.getDeviceId(), channelId); |
| | | } |
| | | } |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam()); |
| | | if (ssrcInfo == null) { |
| | | callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), null); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), |
| | | InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), |
| | | null); |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, |
| | | InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), |
| | | InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), |
| | | null); |
| | | }else { |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), |
| | | InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), |
| | | null); |
| | | } |
| | | return null; |
| | | } |
| | | // TODO 记录点播的状态 |
| | | play(mediaServerItem, ssrcInfo, device, channelId, callback); |
| | | play(mediaServerItem, ssrcInfo, device, channelId,isSubStream, callback); |
| | | return ssrcInfo; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, |
| | | public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,boolean isSubStream, |
| | | ErrorCallback<Object> callback) { |
| | | |
| | | if (mediaServerItem == null || ssrcInfo == null) { |
| | |
| | | null); |
| | | return; |
| | | } |
| | | logger.info("[点播开始] deviceId: {}, channelId: {},收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); |
| | | |
| | | if( device.isSwitchPrimarySubStream() ){ |
| | | logger.info("[点播开始] deviceId: {}, channelId: {},码流类型:{},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId,isSubStream ? "辅码流" : "主码流", ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); |
| | | }else { |
| | | logger.info("[点播开始] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); |
| | | } |
| | | //端口获取失败的ssrcInfo 没有必要发送点播指令 |
| | | if (ssrcInfo.getPort() <= 0) { |
| | | logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo); |
| | |
| | | streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); |
| | | |
| | | callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null); |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, |
| | | InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null); |
| | | }else { |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null); |
| | | } |
| | | return; |
| | | } |
| | | |
| | | // 初始化redis中的invite消息状态 |
| | | InviteInfo inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo, |
| | | mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, |
| | | InviteSessionStatus.ready); |
| | | inviteStreamService.updateInviteInfo(inviteInfo); |
| | | InviteInfo inviteInfo; |
| | | |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | // 初始化redis中的invite消息状态 |
| | | inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channelId,isSubStream, ssrcInfo.getStream(), ssrcInfo, |
| | | mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, |
| | | InviteSessionStatus.ready); |
| | | inviteStreamService.updateInviteInfoSub(inviteInfo); |
| | | }else { |
| | | // 初始化redis中的invite消息状态 |
| | | inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo, |
| | | mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, |
| | | InviteSessionStatus.ready); |
| | | inviteStreamService.updateInviteInfo(inviteInfo); |
| | | } |
| | | // 超时处理 |
| | | String timeOutTaskKey = UUID.randomUUID().toString(); |
| | | dynamicTask.startDelay(timeOutTaskKey, () -> { |
| | | // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况 |
| | | InviteInfo inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); |
| | | InviteInfo inviteInfoForTimeOut; |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | // 初始化redis中的invite消息状态 |
| | | inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); |
| | | }else { |
| | | // 初始化redis中的invite消息状态 |
| | | inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); |
| | | } |
| | | if (inviteInfoForTimeOut == null || inviteInfoForTimeOut.getStreamInfo() == null) { |
| | | logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getPort(), ssrcInfo.getSsrc()); |
| | | if( device.isSwitchPrimarySubStream()){ |
| | | logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},码流类型:{},端口:{}, SSRC: {}", device.getDeviceId(), channelId,isSubStream ? "辅码流" : "主码流", ssrcInfo.getPort(), ssrcInfo.getSsrc()); |
| | | }else { |
| | | logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getPort(), ssrcInfo.getSsrc()); |
| | | } |
| | | // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 |
| | | // InviteInfo inviteInfoForTimeout = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.play, device.getDeviceId(), channelId); |
| | | // if (inviteInfoForTimeout == null) { |
| | |
| | | // // TODO 发送cancel |
| | | // } |
| | | callback.run(InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); |
| | | if( device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, |
| | | InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); |
| | | |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); |
| | | }else { |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); |
| | | } |
| | | try { |
| | | cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); |
| | | } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { |
| | |
| | | }, userSetting.getPlayTimeout()); |
| | | |
| | | try { |
| | | cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { |
| | | cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId,isSubStream, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { |
| | | logger.info("收到订阅消息: " + response.toJSONString()); |
| | | dynamicTask.stop(timeOutTaskKey); |
| | | // hook响应 |
| | | StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId); |
| | | StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId,isSubStream); |
| | | if (streamInfo == null){ |
| | | callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), |
| | | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), |
| | | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); |
| | | if( device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, |
| | | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), |
| | | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); |
| | | }else { |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), |
| | | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); |
| | | } |
| | | return; |
| | | } |
| | | callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.SUCCESS.getCode(), |
| | | InviteErrorCode.SUCCESS.getMsg(), |
| | | streamInfo); |
| | | logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId); |
| | | if( device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, |
| | | InviteErrorCode.SUCCESS.getCode(), |
| | | InviteErrorCode.SUCCESS.getMsg(), |
| | | streamInfo); |
| | | }else { |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.SUCCESS.getCode(), |
| | | InviteErrorCode.SUCCESS.getMsg(), |
| | | streamInfo); |
| | | } |
| | | if( device.isSwitchPrimarySubStream() ){ |
| | | logger.info("[点播成功] deviceId: {}, channelId: {},码流类型:{}", device.getDeviceId(), channelId,isSubStream ? "辅码流" : "主码流"); |
| | | }else { |
| | | logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId); |
| | | } |
| | | String streamUrl; |
| | | if (mediaServerItemInuse.getRtspPort() != 0) { |
| | | streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", ssrcInfo.getStream()); |
| | |
| | | |
| | | callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), |
| | | InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), |
| | | InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, |
| | | InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), |
| | | InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); |
| | | }else { |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), |
| | | InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); |
| | | } |
| | | } |
| | | } |
| | | return; |
| | |
| | | |
| | | 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); |
| | | if( device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, |
| | | InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getCode(), |
| | | InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getMsg(), null); |
| | | }else { |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getCode(), |
| | | InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getMsg(), null); |
| | | } |
| | | |
| | | return; |
| | | } |
| | |
| | | logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString()); |
| | | dynamicTask.stop(timeOutTaskKey); |
| | | // hook响应 |
| | | StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId); |
| | | StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId,isSubStream); |
| | | if (streamInfo == null){ |
| | | callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), |
| | | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), |
| | | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); |
| | | if( device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, |
| | | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), |
| | | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); |
| | | }else { |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), |
| | | InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); |
| | | } |
| | | return; |
| | | } |
| | | callback.run(InviteErrorCode.SUCCESS.getCode(), |
| | | InviteErrorCode.SUCCESS.getMsg(), streamInfo); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.SUCCESS.getCode(), |
| | | InviteErrorCode.SUCCESS.getMsg(), |
| | | streamInfo); |
| | | if( device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, |
| | | InviteErrorCode.SUCCESS.getCode(), |
| | | InviteErrorCode.SUCCESS.getMsg(), |
| | | streamInfo); |
| | | }else { |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.SUCCESS.getCode(), |
| | | InviteErrorCode.SUCCESS.getMsg(), |
| | | streamInfo); |
| | | } |
| | | }); |
| | | return; |
| | | } |
| | |
| | | |
| | | callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), |
| | | "下级自定义了ssrc,重新设置收流信息失败", null); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), |
| | | "下级自定义了ssrc,重新设置收流信息失败", null); |
| | | if( device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, |
| | | InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), |
| | | "下级自定义了ssrc,重新设置收流信息失败", null); |
| | | }else { |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), |
| | | "下级自定义了ssrc,重新设置收流信息失败", null); |
| | | } |
| | | |
| | | }else { |
| | | ssrcInfo.setSsrc(ssrcInResponse); |
| | |
| | | logger.info("[点播消息] 收到invite 200, 下级自定义了ssrc, 但是当前模式无需修正"); |
| | | } |
| | | } |
| | | inviteStreamService.updateInviteInfo(inviteInfo); |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.updateInviteInfoSub(inviteInfo); |
| | | }else { |
| | | inviteStreamService.updateInviteInfo(inviteInfo); |
| | | } |
| | | }, (event) -> { |
| | | dynamicTask.stop(timeOutTaskKey); |
| | | mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); |
| | |
| | | |
| | | callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_ERROR.getCode(), |
| | | String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), |
| | | String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null); |
| | | if( device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, |
| | | InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), |
| | | String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null); |
| | | |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); |
| | | }else { |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), |
| | | String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null); |
| | | |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); |
| | | } |
| | | }); |
| | | } catch (InvalidArgumentException | SipException | ParseException e) { |
| | | |
| | |
| | | |
| | | callback.run(InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), |
| | | InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), |
| | | InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); |
| | | if( device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream, null, |
| | | InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), |
| | | InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); |
| | | |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId,isSubStream); |
| | | }else { |
| | | inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, |
| | | InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), |
| | | InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); |
| | | |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId) { |
| | | StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId); |
| | | private StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId,boolean isSubStream) { |
| | | StreamInfo streamInfo = null; |
| | | Device device = redisCatchStorage.getDevice(deviceId); |
| | | if( device.isSwitchPrimarySubStream() ){ |
| | | streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId,isSubStream); |
| | | }else { |
| | | streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId); |
| | | } |
| | | if (streamInfo != null) { |
| | | DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); |
| | | if (deviceChannel != null) { |
| | | deviceChannel.setStreamId(streamInfo.getStream()); |
| | | storager.startPlay(deviceId, channelId, streamInfo.getStream()); |
| | | InviteInfo inviteInfo; |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); |
| | | }else { |
| | | DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); |
| | | if (deviceChannel != null) { |
| | | deviceChannel.setStreamId(streamInfo.getStream()); |
| | | storager.startPlay(deviceId, channelId, streamInfo.getStream()); |
| | | } |
| | | inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); |
| | | } |
| | | InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); |
| | | if (inviteInfo != null) { |
| | | inviteInfo.setStatus(InviteSessionStatus.ok); |
| | | inviteInfo.setStreamInfo(streamInfo); |
| | | inviteStreamService.updateInviteInfo(inviteInfo); |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.updateInviteInfoSub(inviteInfo); |
| | | }else { |
| | | inviteStreamService.updateInviteInfo(inviteInfo); |
| | | } |
| | | |
| | | } |
| | | } |
| | | return streamInfo; |
| | |
| | | return streamInfo; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public void zlmServerOffline(String mediaServerId) { |
| | | // 处理正在向上推流的上级平台 |
| | |
| | | } |
| | | |
| | | @Override |
| | | public void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback) { |
| | | public void getSnap(String deviceId, String channelId, String fileName,boolean isSubStream, 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); |
| | | InviteInfo inviteInfo; |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); |
| | | }else { |
| | | inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); |
| | | } |
| | | if (inviteInfo != null) { |
| | | if (inviteInfo.getStreamInfo() != null) { |
| | | // 已存在线直接截图 |
| | |
| | | } |
| | | |
| | | MediaServerItem newMediaServerItem = getNewMediaServerItem(device); |
| | | play(newMediaServerItem, deviceId, channelId, (code, msg, data)->{ |
| | | play(newMediaServerItem, deviceId, channelId,isSubStream, (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); |
| | | getSnap(deviceId, channelId, fileName,isSubStream, errorCallback); |
| | | }else { |
| | | errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); |
| | | } |
| | |
| | | }); |
| | | } |
| | | |
| | | |
| | | /*======================设备主子码流逻辑START=========================*/ |
| | | public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId,boolean isSubStream) { |
| | | String streamId = resonse.getString("stream"); |
| | | JSONArray tracks = resonse.getJSONArray("tracks"); |
| | | StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem, "rtp", streamId, tracks, null); |
| | | streamInfo.setDeviceID(deviceId); |
| | | streamInfo.setChannelId(channelId); |
| | | streamInfo.setSubStream(isSubStream); |
| | | return streamInfo; |
| | | } |
| | | /*======================设备主子码流逻辑END=========================*/ |
| | | |
| | | } |
| | |
| | | |
| | | @Select("select count(1) from wvp_device_channel") |
| | | int getAllChannelCount(); |
| | | |
| | | |
| | | /*=================设备主子码流逻辑START==============*/ |
| | | @Update(value = {"UPDATE wvp_device_channel SET stream_id=null WHERE device_id=#{deviceId}"}) |
| | | void clearPlay(String deviceId); |
| | | /*=================设备主子码流逻辑END==============*/ |
| | | |
| | | } |
| | |
| | | "tree_type," + |
| | | "on_line," + |
| | | "media_server_id," + |
| | | "switch_primary_sub_stream," + |
| | | "(SELECT count(0) FROM wvp_device_channel WHERE device_id=wvp_device.device_id) as channel_count "+ |
| | | " FROM wvp_device WHERE device_id = #{deviceId}") |
| | | Device getDeviceByDeviceId(String deviceId); |
| | |
| | | "tree_type,"+ |
| | | "on_line,"+ |
| | | "media_server_id,"+ |
| | | "switch_primary_sub_stream switchPrimarySubStream,"+ |
| | | "(SELECT count(0) FROM wvp_device_channel WHERE device_id=de.device_id) as channel_count " + |
| | | "FROM wvp_device de" + |
| | | "<if test=\"onLine != null\"> where on_line=${onLine}</if>"+ |
| | |
| | | "<if test=\"asMessageChannel != null\">, as_message_channel=#{asMessageChannel}</if>" + |
| | | "<if test=\"geoCoordSys != null\">, geo_coord_sys=#{geoCoordSys}</if>" + |
| | | "<if test=\"treeType != null\">, tree_type=#{treeType}</if>" + |
| | | "<if test=\"switchPrimarySubStream != null\">, switch_primary_sub_stream=#{switchPrimarySubStream}</if>" + |
| | | "<if test=\"mediaServerId != null\">, media_server_id=#{mediaServerId}</if>" + |
| | | "WHERE device_id=#{deviceId}"+ |
| | | " </script>"}) |
| | |
| | | "geo_coord_sys,"+ |
| | | "tree_type,"+ |
| | | "on_line,"+ |
| | | "media_server_id"+ |
| | | "media_server_id,"+ |
| | | "switch_primary_sub_stream"+ |
| | | ") VALUES (" + |
| | | "#{deviceId}," + |
| | | "#{name}," + |
| | |
| | | "#{geoCoordSys}," + |
| | | "#{treeType}," + |
| | | "#{onLine}," + |
| | | "#{mediaServerId}" + |
| | | "#{mediaServerId}," + |
| | | "#{switchPrimarySubStream}" + |
| | | ")") |
| | | void addCustomDevice(Device device); |
| | | |
| | |
| | | @Operation(summary = "开始点播") |
| | | @Parameter(name = "deviceId", description = "设备国标编号", required = true) |
| | | @Parameter(name = "channelId", description = "通道国标编号", required = true) |
| | | @Parameter(name = "isSubStream", description = "是否子码流(true-子码流,false-主码流),默认为false", required = true) |
| | | @GetMapping("/start/{deviceId}/{channelId}") |
| | | public DeferredResult<WVPResult<StreamContent>> play(HttpServletRequest request, @PathVariable String deviceId, |
| | | @PathVariable String channelId) { |
| | | @PathVariable String channelId,boolean isSubStream) { |
| | | |
| | | // 获取可用的zlm |
| | | Device device = storager.queryVideoDevice(deviceId); |
| | | MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); |
| | | |
| | | RequestMessage requestMessage = new RequestMessage(); |
| | | String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId; |
| | | String key = DeferredResultHolder.getPlayKey(deviceId,channelId,device.isSwitchPrimarySubStream(),isSubStream); |
| | | requestMessage.setKey(key); |
| | | String uuid = UUID.randomUUID().toString(); |
| | | requestMessage.setId(uuid); |
| | |
| | | // 录像查询以channelId作为deviceId查询 |
| | | resultHolder.put(key, uuid, result); |
| | | |
| | | playService.play(newMediaServerItem, deviceId, channelId, (code, msg, data) -> { |
| | | playService.play(newMediaServerItem, deviceId, channelId,isSubStream, (code, msg, data) -> { |
| | | WVPResult<StreamContent> wvpResult = new WVPResult<>(); |
| | | if (code == InviteErrorCode.SUCCESS.getCode()) { |
| | | wvpResult.setCode(ErrorCode.SUCCESS.getCode()); |
| | |
| | | @Operation(summary = "停止点播") |
| | | @Parameter(name = "deviceId", description = "设备国标编号", required = true) |
| | | @Parameter(name = "channelId", description = "通道国标编号", required = true) |
| | | @Parameter(name = "isSubStream", description = "是否子码流(true-子码流,false-主码流),默认为false", required = true) |
| | | @GetMapping("/stop/{deviceId}/{channelId}") |
| | | public JSONObject playStop(@PathVariable String deviceId, @PathVariable String channelId) { |
| | | public JSONObject playStop(@PathVariable String deviceId, @PathVariable String channelId,boolean isSubStream) { |
| | | |
| | | logger.debug(String.format("设备预览/回放停止API调用,streamId:%s_%s", deviceId, channelId )); |
| | | |
| | |
| | | throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备[" + deviceId + "]不存在"); |
| | | } |
| | | |
| | | InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); |
| | | InviteInfo inviteInfo =null; |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); |
| | | }else { |
| | | inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); |
| | | } |
| | | if (inviteInfo == null) { |
| | | throw new ControllerException(ErrorCode.ERROR100.getCode(), "点播未找到"); |
| | | } |
| | |
| | | throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); |
| | | } |
| | | } |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); |
| | | if(device.isSwitchPrimarySubStream()){ |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId,isSubStream); |
| | | }else { |
| | | inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); |
| | | storager.stopPlay(deviceId, channelId); |
| | | } |
| | | |
| | | storager.stopPlay(deviceId, channelId); |
| | | JSONObject json = new JSONObject(); |
| | | json.put("deviceId", deviceId); |
| | | json.put("channelId", channelId); |
| | | json.put("isSubStream", isSubStream); |
| | | return json; |
| | | } |
| | | |
| | |
| | | @Operation(summary = "获取截图") |
| | | @Parameter(name = "deviceId", description = "设备国标编号", required = true) |
| | | @Parameter(name = "channelId", description = "通道国标编号", required = true) |
| | | @Parameter(name = "isSubStream", description = "是否子码流(true-子码流,false-主码流),默认为false", required = true) |
| | | @GetMapping("/snap") |
| | | public DeferredResult<String> getSnap(String deviceId, String channelId) { |
| | | public DeferredResult<String> getSnap(String deviceId, String channelId,boolean isSubStream) { |
| | | if (logger.isDebugEnabled()) { |
| | | logger.debug("获取截图: {}/{}", deviceId, channelId); |
| | | } |
| | | |
| | | Device device = storager.queryVideoDevice(deviceId); |
| | | DeferredResult<String> result = new DeferredResult<>(3 * 1000L); |
| | | String key = DeferredResultHolder.CALLBACK_CMD_SNAP + deviceId; |
| | | String key = DeferredResultHolder.getSnapKey(deviceId,channelId,device.isSwitchPrimarySubStream(),isSubStream); |
| | | String uuid = UUID.randomUUID().toString(); |
| | | resultHolder.put(key, uuid, result); |
| | | |
| | |
| | | message.setId(uuid); |
| | | |
| | | String fileName = deviceId + "_" + channelId + "_" + DateUtil.getNowForUrl() + "jpg"; |
| | | playService.getSnap(deviceId, channelId, fileName, (code, msg, data) -> { |
| | | playService.getSnap(deviceId, channelId, fileName,isSubStream, (code, msg, data) -> { |
| | | if (code == InviteErrorCode.SUCCESS.getCode()) { |
| | | message.setData(data); |
| | | }else { |
| | |
| | | MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); |
| | | |
| | | |
| | | playService.play(newMediaServerItem, serial, code, (errorCode, msg, data) -> { |
| | | playService.play(newMediaServerItem, serial, code,false, (errorCode, msg, data) -> { |
| | | if (errorCode == InviteErrorCode.SUCCESS.getCode()) { |
| | | InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, serial, code); |
| | | if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { |
| | |
| | | spring: |
| | | thymeleaf: |
| | | cache: false |
| | | # [可选]上传文件大小限制 |
| | | servlet: |
| | | multipart: |
| | |
| | | # [必须修改] 端口号 |
| | | port: 6379 |
| | | # [可选] 数据库 DB |
| | | database: 6 |
| | | database: 7 |
| | | # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 |
| | | password: face2020 |
| | | password: |
| | | # [可选] 超时时间 |
| | | timeout: 10000 |
| | | # mysql数据源 |
| | | datasource: |
| | | type: com.zaxxer.hikari.HikariDataSource |
| | | driver-class-name: com.mysql.cj.jdbc.Driver |
| | | url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true |
| | | url: jdbc:mysql://127.0.0.1:3306/test_gb-89wulian?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true |
| | | username: root |
| | | password: 123456 |
| | | password: root |
| | | hikari: |
| | | connection-timeout: 20000 # 是客户端等待连接池连接的最大毫秒数 |
| | | initialSize: 10 # 连接池初始化连接数 |
| | |
| | | minimum-idle: 5 # 连接池最小空闲连接数 |
| | | idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位) |
| | | max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位) |
| | | |
| | | |
| | | #[可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 |
| | | server: |
| | | port: 18080 |
| | | port: 18978 |
| | | # [可选] HTTPS配置, 默认不开启 |
| | | ssl: |
| | | # [可选] 是否开启HTTPS访问 |
| | | enabled: false |
| | | # [可选] 证书文件路径,放置在resource/目录下即可,修改xxx为文件名 |
| | | key-store: classpath:test.monitor.89iot.cn.jks |
| | | # [可选] 证书密码 |
| | | key-store-password: gpf64qmw |
| | | # [可选] 证书类型, 默认为jks,根据实际修改 |
| | | key-store-type: JKS |
| | | |
| | | # 作为28181服务器的配置 |
| | | sip: |
| | |
| | | # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4 |
| | | # 如果不明白,就使用0.0.0.0,大部分情况都是可以的 |
| | | # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。 |
| | | ip: 192.168.41.16 |
| | | ip: 192.168.1.18 |
| | | # [可选] 28181服务监听的端口 |
| | | port: 5060 |
| | | port: 8116 |
| | | # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) |
| | | # 后两位为行业编码,定义参照附录D.3 |
| | | # 3701020049标识山东济南历下区 信息行业接入 |
| | | # [可选] |
| | | domain: 4401020049 |
| | | domain: 4101050000 |
| | | # [可选] |
| | | id: 44010200492000000001 |
| | | id: 41010500002000000001 |
| | | # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 |
| | | password: admin123 |
| | | password: bajiuwulian1006 |
| | | # 是否存储alarm信息 |
| | | alarm: true |
| | | |
| | | #zlm 默认服务器配置 |
| | | media: |
| | | id: FQ3TF8yT83wh5Wvz |
| | | id: 89wulian-one |
| | | # [必须修改] zlm服务器的内网IP |
| | | ip: 192.168.41.16 |
| | | ip: 192.168.1.18 |
| | | # [必须修改] zlm服务器的http.port |
| | | http-port: 8091 |
| | | http-port: 80 |
| | | # [可选] 返回流地址时的ip,置空使用 media.ip |
| | | stream-ip: 192.168.1.18 |
| | | # [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip |
| | | sdp-ip: 192.168.1.18 |
| | | # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip |
| | | hook-ip: 192.168.1.18 |
| | | # [可选] zlm服务器的http.sslport, 置空使用zlm配置文件配置 |
| | | http-ssl-port: 443 |
| | | # [可选] zlm服务器的hook.admin_params=secret |
| | | secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc |
| | | # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 |
| | |
| | | # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 |
| | | enable: true |
| | | # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 |
| | | port-range: 30000,30500 # 端口范围 |
| | | port-range: 50000,50300 # 端口范围 |
| | | # [可选] 国标级联在此范围内选择端口发送媒体流, |
| | | send-port-range: 30000,30500 # 端口范围 |
| | | send-port-range: 50000,50300 # 端口范围 |
| | | # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 |
| | | record-assist-port: 18081 |
| | | # [根据业务需求配置] |
| | | user-settings: |
| | | # 点播/录像回放 等待超时时间,单位:毫秒 |
| | | play-timeout: 180000 |
| | | # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true |
| | | auto-apply-play: true |
| | | # 设备/通道状态变化时发送消息 |
| | | device-status-notify: true |
| | | # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个 |
| | | allowed-origins: |
| | | - http://localhost:8080 |
| | | - http://127.0.0.1:8080 |
| | | # [可选] 日志配置, 一般不需要改 |
| | | logging: |
| | | config: classpath:logback-spring-local.xml |
| | | |
| | |
| | | application: |
| | | name: wvp |
| | | profiles: |
| | | active: local |
| | | active: dev |
| | |
| | | assetsPublicPath: '/', |
| | | proxyTable: { |
| | | '/debug': { |
| | | target: 'http://localhost:18080', |
| | | target: 'http://localhost:18978', |
| | | changeOrigin: true, |
| | | pathRewrite: { |
| | | '^/debug': '/' |
| | | } |
| | | }, |
| | | '/static/snap': { |
| | | target: 'http://localhost:18080', |
| | | target: 'http://localhost:18978', |
| | | changeOrigin: true, |
| | | // pathRewrite: { |
| | | // '^/static/snap': '/static/snap' |
| | |
| | | } |
| | | }, |
| | | "node_modules/ajv": { |
| | | "version": "5.5.2", |
| | | "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-5.5.2.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-5.5.2.tgz", |
| | | "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", |
| | | "version": "6.12.6", |
| | | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", |
| | | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", |
| | | "dev": true, |
| | | "dependencies": { |
| | | "co": "^4.6.0", |
| | | "fast-deep-equal": "^1.0.0", |
| | | "fast-deep-equal": "^3.1.1", |
| | | "fast-json-stable-stringify": "^2.0.0", |
| | | "json-schema-traverse": "^0.3.0" |
| | | "json-schema-traverse": "^0.4.1", |
| | | "uri-js": "^4.2.2" |
| | | }, |
| | | "funding": { |
| | | "type": "github", |
| | | "url": "https://github.com/sponsors/epoberezkin" |
| | | } |
| | | }, |
| | | "node_modules/ajv-keywords": { |
| | |
| | | }, |
| | | "node_modules/co": { |
| | | "version": "4.6.0", |
| | | "resolved": "https://registry.npm.taobao.org/co/download/co-4.6.0.tgz", |
| | | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", |
| | | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", |
| | | "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", |
| | | "dev": true, |
| | | "engines": { |
| | | "iojs": ">= 1.0.0", |
| | |
| | | } |
| | | }, |
| | | "node_modules/fast-deep-equal": { |
| | | "version": "1.1.0", |
| | | "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-1.1.0.tgz", |
| | | "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", |
| | | "version": "3.1.3", |
| | | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", |
| | | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", |
| | | "dev": true |
| | | }, |
| | | "node_modules/fast-json-stable-stringify": { |
| | |
| | | "peerDependencies": { |
| | | "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" |
| | | } |
| | | }, |
| | | "node_modules/file-loader/node_modules/ajv": { |
| | | "version": "6.12.5", |
| | | "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", |
| | | "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", |
| | | "dev": true, |
| | | "dependencies": { |
| | | "fast-deep-equal": "^3.1.1", |
| | | "fast-json-stable-stringify": "^2.0.0", |
| | | "json-schema-traverse": "^0.4.1", |
| | | "uri-js": "^4.2.2" |
| | | } |
| | | }, |
| | | "node_modules/file-loader/node_modules/fast-deep-equal": { |
| | | "version": "3.1.3", |
| | | "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", |
| | | "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", |
| | | "dev": true |
| | | }, |
| | | "node_modules/file-loader/node_modules/json-schema-traverse": { |
| | | "version": "0.4.1", |
| | | "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", |
| | | "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", |
| | | "dev": true |
| | | }, |
| | | "node_modules/file-loader/node_modules/schema-utils": { |
| | | "version": "0.4.7", |
| | |
| | | "dev": true |
| | | }, |
| | | "node_modules/json-schema-traverse": { |
| | | "version": "0.3.1", |
| | | "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.3.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.3.1.tgz", |
| | | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", |
| | | "version": "0.4.1", |
| | | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", |
| | | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", |
| | | "dev": true |
| | | }, |
| | | "node_modules/json-stringify-pretty-compact": { |
| | |
| | | "engines": { |
| | | "node": ">= 4" |
| | | } |
| | | }, |
| | | "node_modules/postcss-loader/node_modules/ajv": { |
| | | "version": "6.12.5", |
| | | "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", |
| | | "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", |
| | | "dev": true, |
| | | "dependencies": { |
| | | "fast-deep-equal": "^3.1.1", |
| | | "fast-json-stable-stringify": "^2.0.0", |
| | | "json-schema-traverse": "^0.4.1", |
| | | "uri-js": "^4.2.2" |
| | | } |
| | | }, |
| | | "node_modules/postcss-loader/node_modules/fast-deep-equal": { |
| | | "version": "3.1.3", |
| | | "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", |
| | | "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", |
| | | "dev": true |
| | | }, |
| | | "node_modules/postcss-loader/node_modules/json-schema-traverse": { |
| | | "version": "0.4.1", |
| | | "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", |
| | | "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", |
| | | "dev": true |
| | | }, |
| | | "node_modules/postcss-loader/node_modules/schema-utils": { |
| | | "version": "0.4.7", |
| | |
| | | "node": ">= 4.3 < 5.0.0 || >= 5.10" |
| | | } |
| | | }, |
| | | "node_modules/schema-utils/node_modules/ajv": { |
| | | "version": "5.5.2", |
| | | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", |
| | | "integrity": "sha512-Ajr4IcMXq/2QmMkEmSvxqfLN5zGmJ92gHXAeOXq1OekoH2rfDNsgdDoL2f7QaRCy7G/E6TpxBVdRuNraMztGHw==", |
| | | "dev": true, |
| | | "dependencies": { |
| | | "co": "^4.6.0", |
| | | "fast-deep-equal": "^1.0.0", |
| | | "fast-json-stable-stringify": "^2.0.0", |
| | | "json-schema-traverse": "^0.3.0" |
| | | } |
| | | }, |
| | | "node_modules/schema-utils/node_modules/fast-deep-equal": { |
| | | "version": "1.1.0", |
| | | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", |
| | | "integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==", |
| | | "dev": true |
| | | }, |
| | | "node_modules/schema-utils/node_modules/json-schema-traverse": { |
| | | "version": "0.3.1", |
| | | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", |
| | | "integrity": "sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==", |
| | | "dev": true |
| | | }, |
| | | "node_modules/select": { |
| | | "version": "1.1.2", |
| | | "resolved": "https://registry.npm.taobao.org/select/download/select-1.1.2.tgz", |
| | |
| | | "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" |
| | | } |
| | | }, |
| | | "node_modules/uglifyjs-webpack-plugin/node_modules/ajv": { |
| | | "version": "6.12.5", |
| | | "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", |
| | | "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", |
| | | "dev": true, |
| | | "dependencies": { |
| | | "fast-deep-equal": "^3.1.1", |
| | | "fast-json-stable-stringify": "^2.0.0", |
| | | "json-schema-traverse": "^0.4.1", |
| | | "uri-js": "^4.2.2" |
| | | } |
| | | }, |
| | | "node_modules/uglifyjs-webpack-plugin/node_modules/commander": { |
| | | "version": "2.13.0", |
| | | "resolved": "https://registry.npm.taobao.org/commander/download/commander-2.13.0.tgz?cache=0&sync_timestamp=1598576136669&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcommander%2Fdownload%2Fcommander-2.13.0.tgz", |
| | | "integrity": "sha1-aWS8pnaF33wfFDDFhPB9dZeIW5w=", |
| | | "dev": true |
| | | }, |
| | | "node_modules/uglifyjs-webpack-plugin/node_modules/fast-deep-equal": { |
| | | "version": "3.1.3", |
| | | "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", |
| | | "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", |
| | | "dev": true |
| | | }, |
| | | "node_modules/uglifyjs-webpack-plugin/node_modules/json-schema-traverse": { |
| | | "version": "0.4.1", |
| | | "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", |
| | | "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", |
| | | "dev": true |
| | | }, |
| | | "node_modules/uglifyjs-webpack-plugin/node_modules/schema-utils": { |
| | |
| | | "source-map": "~0.6.1" |
| | | } |
| | | }, |
| | | "node_modules/webpack/node_modules/ajv": { |
| | | "version": "6.12.5", |
| | | "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", |
| | | "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", |
| | | "dev": true, |
| | | "dependencies": { |
| | | "fast-deep-equal": "^3.1.1", |
| | | "fast-json-stable-stringify": "^2.0.0", |
| | | "json-schema-traverse": "^0.4.1", |
| | | "uri-js": "^4.2.2" |
| | | } |
| | | }, |
| | | "node_modules/webpack/node_modules/fast-deep-equal": { |
| | | "version": "3.1.3", |
| | | "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", |
| | | "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", |
| | | "dev": true |
| | | }, |
| | | "node_modules/webpack/node_modules/has-flag": { |
| | | "version": "2.0.0", |
| | | "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-2.0.0.tgz", |
| | |
| | | "engines": { |
| | | "node": ">=0.10.0" |
| | | } |
| | | }, |
| | | "node_modules/webpack/node_modules/json-schema-traverse": { |
| | | "version": "0.4.1", |
| | | "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", |
| | | "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", |
| | | "dev": true |
| | | }, |
| | | "node_modules/webpack/node_modules/source-map": { |
| | | "version": "0.5.7", |
| | |
| | | } |
| | | }, |
| | | "ajv": { |
| | | "version": "5.5.2", |
| | | "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-5.5.2.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-5.5.2.tgz", |
| | | "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", |
| | | "version": "6.12.6", |
| | | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", |
| | | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", |
| | | "dev": true, |
| | | "requires": { |
| | | "co": "^4.6.0", |
| | | "fast-deep-equal": "^1.0.0", |
| | | "fast-deep-equal": "^3.1.1", |
| | | "fast-json-stable-stringify": "^2.0.0", |
| | | "json-schema-traverse": "^0.3.0" |
| | | "json-schema-traverse": "^0.4.1", |
| | | "uri-js": "^4.2.2" |
| | | } |
| | | }, |
| | | "ajv-keywords": { |
| | |
| | | }, |
| | | "co": { |
| | | "version": "4.6.0", |
| | | "resolved": "https://registry.npm.taobao.org/co/download/co-4.6.0.tgz", |
| | | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", |
| | | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", |
| | | "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", |
| | | "dev": true |
| | | }, |
| | | "coa": { |
| | |
| | | } |
| | | }, |
| | | "fast-deep-equal": { |
| | | "version": "1.1.0", |
| | | "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-1.1.0.tgz", |
| | | "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", |
| | | "version": "3.1.3", |
| | | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", |
| | | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", |
| | | "dev": true |
| | | }, |
| | | "fast-json-stable-stringify": { |
| | |
| | | "schema-utils": "^0.4.5" |
| | | }, |
| | | "dependencies": { |
| | | "ajv": { |
| | | "version": "6.12.5", |
| | | "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", |
| | | "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", |
| | | "dev": true, |
| | | "requires": { |
| | | "fast-deep-equal": "^3.1.1", |
| | | "fast-json-stable-stringify": "^2.0.0", |
| | | "json-schema-traverse": "^0.4.1", |
| | | "uri-js": "^4.2.2" |
| | | } |
| | | }, |
| | | "fast-deep-equal": { |
| | | "version": "3.1.3", |
| | | "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", |
| | | "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", |
| | | "dev": true |
| | | }, |
| | | "json-schema-traverse": { |
| | | "version": "0.4.1", |
| | | "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", |
| | | "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", |
| | | "dev": true |
| | | }, |
| | | "schema-utils": { |
| | | "version": "0.4.7", |
| | | "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-0.4.7.tgz?cache=0&sync_timestamp=1601922251376&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-0.4.7.tgz", |
| | |
| | | "dev": true |
| | | }, |
| | | "json-schema-traverse": { |
| | | "version": "0.3.1", |
| | | "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.3.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.3.1.tgz", |
| | | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", |
| | | "version": "0.4.1", |
| | | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", |
| | | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", |
| | | "dev": true |
| | | }, |
| | | "json-stringify-pretty-compact": { |
| | |
| | | "schema-utils": "^0.4.0" |
| | | }, |
| | | "dependencies": { |
| | | "ajv": { |
| | | "version": "6.12.5", |
| | | "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", |
| | | "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", |
| | | "dev": true, |
| | | "requires": { |
| | | "fast-deep-equal": "^3.1.1", |
| | | "fast-json-stable-stringify": "^2.0.0", |
| | | "json-schema-traverse": "^0.4.1", |
| | | "uri-js": "^4.2.2" |
| | | } |
| | | }, |
| | | "fast-deep-equal": { |
| | | "version": "3.1.3", |
| | | "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", |
| | | "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", |
| | | "dev": true |
| | | }, |
| | | "json-schema-traverse": { |
| | | "version": "0.4.1", |
| | | "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", |
| | | "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", |
| | | "dev": true |
| | | }, |
| | | "schema-utils": { |
| | | "version": "0.4.7", |
| | | "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-0.4.7.tgz?cache=0&sync_timestamp=1601922251376&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-0.4.7.tgz", |
| | |
| | | "dev": true, |
| | | "requires": { |
| | | "ajv": "^5.0.0" |
| | | }, |
| | | "dependencies": { |
| | | "ajv": { |
| | | "version": "5.5.2", |
| | | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", |
| | | "integrity": "sha512-Ajr4IcMXq/2QmMkEmSvxqfLN5zGmJ92gHXAeOXq1OekoH2rfDNsgdDoL2f7QaRCy7G/E6TpxBVdRuNraMztGHw==", |
| | | "dev": true, |
| | | "requires": { |
| | | "co": "^4.6.0", |
| | | "fast-deep-equal": "^1.0.0", |
| | | "fast-json-stable-stringify": "^2.0.0", |
| | | "json-schema-traverse": "^0.3.0" |
| | | } |
| | | }, |
| | | "fast-deep-equal": { |
| | | "version": "1.1.0", |
| | | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", |
| | | "integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==", |
| | | "dev": true |
| | | }, |
| | | "json-schema-traverse": { |
| | | "version": "0.3.1", |
| | | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", |
| | | "integrity": "sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==", |
| | | "dev": true |
| | | } |
| | | } |
| | | }, |
| | | "select": { |
| | |
| | | "worker-farm": "^1.5.2" |
| | | }, |
| | | "dependencies": { |
| | | "ajv": { |
| | | "version": "6.12.5", |
| | | "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", |
| | | "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", |
| | | "dev": true, |
| | | "requires": { |
| | | "fast-deep-equal": "^3.1.1", |
| | | "fast-json-stable-stringify": "^2.0.0", |
| | | "json-schema-traverse": "^0.4.1", |
| | | "uri-js": "^4.2.2" |
| | | } |
| | | }, |
| | | "commander": { |
| | | "version": "2.13.0", |
| | | "resolved": "https://registry.npm.taobao.org/commander/download/commander-2.13.0.tgz?cache=0&sync_timestamp=1598576136669&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcommander%2Fdownload%2Fcommander-2.13.0.tgz", |
| | | "integrity": "sha1-aWS8pnaF33wfFDDFhPB9dZeIW5w=", |
| | | "dev": true |
| | | }, |
| | | "fast-deep-equal": { |
| | | "version": "3.1.3", |
| | | "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", |
| | | "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", |
| | | "dev": true |
| | | }, |
| | | "json-schema-traverse": { |
| | | "version": "0.4.1", |
| | | "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", |
| | | "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", |
| | | "dev": true |
| | | }, |
| | | "schema-utils": { |
| | |
| | | "yargs": "^8.0.2" |
| | | }, |
| | | "dependencies": { |
| | | "ajv": { |
| | | "version": "6.12.5", |
| | | "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", |
| | | "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", |
| | | "dev": true, |
| | | "requires": { |
| | | "fast-deep-equal": "^3.1.1", |
| | | "fast-json-stable-stringify": "^2.0.0", |
| | | "json-schema-traverse": "^0.4.1", |
| | | "uri-js": "^4.2.2" |
| | | } |
| | | }, |
| | | "fast-deep-equal": { |
| | | "version": "3.1.3", |
| | | "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", |
| | | "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", |
| | | "dev": true |
| | | }, |
| | | "has-flag": { |
| | | "version": "2.0.0", |
| | | "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-2.0.0.tgz", |
| | | "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", |
| | | "dev": true |
| | | }, |
| | | "json-schema-traverse": { |
| | | "version": "0.4.1", |
| | | "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", |
| | | "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", |
| | | "dev": true |
| | | }, |
| | | "source-map": { |
| | |
| | | <el-option label="在线" value="true"></el-option> |
| | | <el-option label="离线" value="false"></el-option> |
| | | </el-select> |
| | | 清晰度: |
| | | <el-select size="mini" style="margin-right: 1rem;" @change="search" v-model="isSubStream" placeholder="请选择" |
| | | default-first-option> |
| | | <el-option label="原画" :value="false"></el-option> |
| | | <el-option label="流畅" :value="true"></el-option> |
| | | </el-select> |
| | | </div> |
| | | <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button> |
| | | <el-button v-if="showTree" icon="iconfont icon-list" circle size="mini" @click="switchList()"></el-button> |
| | |
| | | searchSrt: "", |
| | | channelType: "", |
| | | online: "", |
| | | isSubStream: false, |
| | | winHeight: window.innerHeight - 200, |
| | | currentPage: 1, |
| | | count: 15, |
| | |
| | | let that = this; |
| | | this.$axios({ |
| | | method: 'get', |
| | | url: '/api/play/start/' + deviceId + '/' + channelId |
| | | url: '/api/play/start/' + deviceId + '/' + channelId, |
| | | params:{ |
| | | isSubStream: this.isSubStream |
| | | } |
| | | }).then(function (res) { |
| | | console.log(res) |
| | | that.isLoging = false; |
| | |
| | | var that = this; |
| | | this.$axios({ |
| | | method: 'get', |
| | | url: '/api/play/stop/' + this.deviceId + "/" + itemData.channelId |
| | | url: '/api/play/stop/' + this.deviceId + "/" + itemData.channelId, |
| | | params:{ |
| | | isSubStream: this.isSubStream |
| | | } |
| | | }).then(function (res) { |
| | | that.initData(); |
| | | }).catch(function (error) { |
| | |
| | | <el-form-item v-if="form.subscribeCycleForMobilePosition > 0" label="移动位置报送间隔" prop="subscribeCycleForCatalog" > |
| | | <el-input v-model="form.mobilePositionSubmissionInterval" clearable ></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="主子码流开关" prop="switchPrimarySubStream" > |
| | | <el-select v-model="form.switchPrimarySubStream" style="float: left; width: 100%" > |
| | | <el-option key="true" label="开启" :value="true"></el-option> |
| | | <el-option key="false" label="关闭" :value="false"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="其他选项"> |
| | | <el-checkbox label="SSRC校验" v-model="form.ssrcCheck" style="float: left"></el-checkbox> |
| | | <el-checkbox label="作为消息通道" v-model="form.asMessageChannel" style="float: left"></el-checkbox> |