Commit 3cada227435934da8a2db49939695f870b7f477e

Authored by 648540858
2 parents 7d3cbb82 59d8f2f9

Merge branch 'wvp-28181-2.0' into main-dev

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
sql/2.6.9更新.sql 0 → 100644
  1 +alter table wvp_device_channel
  2 + change stream_id stream_id varying(255)
0 \ No newline at end of file 3 \ No newline at end of file
sql/初始化.sql
@@ -79,7 +79,7 @@ create table wvp_device_channel ( @@ -79,7 +79,7 @@ create table wvp_device_channel (
79 custom_longitude double precision, 79 custom_longitude double precision,
80 latitude double precision, 80 latitude double precision,
81 custom_latitude double precision, 81 custom_latitude double precision,
82 - stream_id character varying(50), 82 + stream_id character varying(255),
83 device_id character varying(50) not null, 83 device_id character varying(50) not null,
84 parental character varying(50), 84 parental character varying(50),
85 has_audio bool default false, 85 has_audio bool default false,
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
@@ -98,6 +98,10 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In @@ -98,6 +98,10 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
98 logger.warn("[收到ACK]:未找到来自{},目标为({})的推流信息",fromUserId, toUserId); 98 logger.warn("[收到ACK]:未找到来自{},目标为({})的推流信息",fromUserId, toUserId);
99 return; 99 return;
100 } 100 }
  101 + // tcp主动时,此时是级联下级平台,在回复200ok时,本地已经请求zlm开启监听,跳过下面步骤
  102 + if (sendRtpItem.isTcpActive()) {
  103 + return;
  104 + }
101 logger.info("[收到ACK]:rtp/{}开始级推流, 目标={}:{},SSRC={}, RTCP={}", sendRtpItem.getStream(), 105 logger.info("[收到ACK]:rtp/{}开始级推流, 目标={}:{},SSRC={}, RTCP={}", sendRtpItem.getStream(),
102 sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isRtcp()); 106 sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isRtcp());
103 // 取消设置的超时任务 107 // 取消设置的超时任务
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -50,6 +50,8 @@ import javax.sip.header.CallIdHeader; @@ -50,6 +50,8 @@ import javax.sip.header.CallIdHeader;
50 import javax.sip.message.Response; 50 import javax.sip.message.Response;
51 import java.text.ParseException; 51 import java.text.ParseException;
52 import java.time.Instant; 52 import java.time.Instant;
  53 +import java.util.HashMap;
  54 +import java.util.Map;
53 import java.util.Random; 55 import java.util.Random;
54 import java.util.Vector; 56 import java.util.Vector;
55 57
@@ -406,6 +408,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -406,6 +408,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
406 content.append("y=" + sendRtpItem.getSsrc() + "\r\n"); 408 content.append("y=" + sendRtpItem.getSsrc() + "\r\n");
407 content.append("f=\r\n"); 409 content.append("f=\r\n");
408 410
  411 +
409 try { 412 try {
410 // 超时未收到Ack应该回复bye,当前等待时间为10秒 413 // 超时未收到Ack应该回复bye,当前等待时间为10秒
411 dynamicTask.startDelay(callIdHeader.getCallId(), () -> { 414 dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
@@ -418,8 +421,34 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -418,8 +421,34 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
418 logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); 421 logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
419 } 422 }
420 }, 60 * 1000); 423 }, 60 * 1000);
421 -  
422 - responseSdpAck(request, content.toString(), platform); 424 + responseSdpAck(request, content.toString(), platform);
  425 + // tcp主动模式,回复sdp后开启监听
  426 + if (sendRtpItem.isTcpActive()) {
  427 + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
  428 + Map<String, Object> param = new HashMap<>(12);
  429 + param.put("vhost","__defaultVhost__");
  430 + param.put("app",sendRtpItem.getApp());
  431 + param.put("stream",sendRtpItem.getStreamId());
  432 + param.put("ssrc", sendRtpItem.getSsrc());
  433 + if (!sendRtpItem.isTcpActive()) {
  434 + param.put("dst_url",sendRtpItem.getIp());
  435 + param.put("dst_port", sendRtpItem.getPort());
  436 + }
  437 + String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
  438 + param.put("is_udp", is_Udp);
  439 + param.put("src_port", localPort);
  440 + param.put("pt", sendRtpItem.getPt());
  441 + param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0");
  442 + param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0");
  443 + if (!sendRtpItem.isTcp()) {
  444 + // 开启rtcp保活
  445 + param.put("udp_rtcp_timeout", sendRtpItem.isRtcp()? "1":"0");
  446 + }
  447 + JSONObject startSendRtpStreamResult = zlmServerFactory.startSendRtpStreamForPassive(mediaInfo, param);
  448 + if (startSendRtpStreamResult != null) {
  449 + startSendRtpStreamHand(evt, sendRtpItem, null, startSendRtpStreamResult, param, callIdHeader);
  450 + }
  451 + }
423 } catch (SipException | InvalidArgumentException | ParseException e) { 452 } catch (SipException | InvalidArgumentException | ParseException e) {
424 logger.error("[命令发送失败] 国标级联 回复SdpAck", e); 453 logger.error("[命令发送失败] 国标级联 回复SdpAck", e);
425 } 454 }
@@ -553,6 +582,18 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -553,6 +582,18 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
553 } 582 }
554 } 583 }
555 584
  585 + private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform,
  586 + JSONObject jsonObject, Map<String, Object> param, CallIdHeader callIdHeader) {
  587 + if (jsonObject == null) {
  588 + logger.error("下级TCP被动启动监听失败: 请检查ZLM服务");
  589 + } else if (jsonObject.getInteger("code") == 0) {
  590 + logger.info("调用ZLM-TCP被动推流接口, 结果: {}", jsonObject);
  591 + logger.info("启动监听TCP被动推流成功[ {}/{} ],{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port"));
  592 + } else {
  593 + logger.error("启动监听TCP被动推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(param));
  594 + }
  595 + }
  596 +
556 /** 597 /**
557 * 安排推流 598 * 安排推流
558 */ 599 */
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
@@ -59,6 +59,7 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp @@ -59,6 +59,7 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
59 // 未注册的设备不做处理 59 // 未注册的设备不做处理
60 return; 60 return;
61 } 61 }
  62 + logger.info("[收到心跳], device: {}", device.getDeviceId());
62 SIPRequest request = (SIPRequest) evt.getRequest(); 63 SIPRequest request = (SIPRequest) evt.getRequest();
63 // 回复200 OK 64 // 回复200 OK
64 try { 65 try {
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -25,8 +25,10 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; @@ -25,8 +25,10 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
25 import com.genersoft.iot.vmp.media.zlm.dto.hook.*; 25 import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
26 import com.genersoft.iot.vmp.service.*; 26 import com.genersoft.iot.vmp.service.*;
27 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; 27 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
  28 +import com.genersoft.iot.vmp.service.bean.SSRCInfo;
28 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 29 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
29 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 30 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  31 +import com.genersoft.iot.vmp.utils.DateUtil;
30 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; 32 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
31 import com.genersoft.iot.vmp.vmanager.bean.OtherRtpSendInfo; 33 import com.genersoft.iot.vmp.vmanager.bean.OtherRtpSendInfo;
32 import com.genersoft.iot.vmp.vmanager.bean.StreamContent; 34 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
@@ -641,7 +643,7 @@ public class ZLMHttpHookListener { @@ -641,7 +643,7 @@ public class ZLMHttpHookListener {
641 643
642 if ("rtp".equals(param.getApp())) { 644 if ("rtp".equals(param.getApp())) {
643 String[] s = param.getStream().split("_"); 645 String[] s = param.getStream().split("_");
644 - if (!mediaInfo.isRtpEnable() || s.length != 2) { 646 + if (!mediaInfo.isRtpEnable() || (s.length != 2 && s.length != 4)) {
645 defaultResult.setResult(HookResult.SUCCESS()); 647 defaultResult.setResult(HookResult.SUCCESS());
646 return defaultResult; 648 return defaultResult;
647 } 649 }
@@ -657,33 +659,79 @@ public class ZLMHttpHookListener { @@ -657,33 +659,79 @@ public class ZLMHttpHookListener {
657 defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg())); 659 defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
658 return defaultResult; 660 return defaultResult;
659 } 661 }
660 - logger.info("[ZLM HOOK] 流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());  
661 -  
662 - RequestMessage msg = new RequestMessage();  
663 - String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;  
664 - boolean exist = resultHolder.exist(key, null);  
665 - msg.setKey(key);  
666 - String uuid = UUID.randomUUID().toString();  
667 - msg.setId(uuid);  
668 - DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());  
669 -  
670 - result.onTimeout(() -> {  
671 - logger.info("[ZLM HOOK] 自动点播, 等待超时");  
672 - // 释放rtpserver  
673 - msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));  
674 - resultHolder.invokeResult(msg);  
675 - });  
676 -  
677 - // 录像查询以channelId作为deviceId查询  
678 - resultHolder.put(key, uuid, result);  
679 -  
680 - if (!exist) {  
681 - playService.play(mediaInfo, deviceId, channelId, null, (code, message, data) -> {  
682 - msg.setData(new HookResult(code, message)); 662 + if (s.length == 2) {
  663 + logger.info("[ZLM HOOK] 预览流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  664 +
  665 + RequestMessage msg = new RequestMessage();
  666 + String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
  667 + boolean exist = resultHolder.exist(key, null);
  668 + msg.setKey(key);
  669 + String uuid = UUID.randomUUID().toString();
  670 + msg.setId(uuid);
  671 + DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
  672 +
  673 + result.onTimeout(() -> {
  674 + logger.info("[ZLM HOOK] 预览流自动点播, 等待超时");
  675 + // 释放rtpserver
  676 + msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
683 resultHolder.invokeResult(msg); 677 resultHolder.invokeResult(msg);
684 }); 678 });
  679 +
  680 + resultHolder.put(key, uuid, result);
  681 +
  682 + if (!exist) {
  683 + playService.play(mediaInfo, deviceId, channelId, null, (code, message, data) -> {
  684 + msg.setData(new HookResult(code, message));
  685 + resultHolder.invokeResult(msg);
  686 + });
  687 + }
  688 + return result;
  689 + }else if(s.length == 4){
  690 + // 此时为录像回放, 录像回放格式为> 设备ID_通道ID_开始时间_结束时间
  691 + String startTimeStr = s[2];
  692 + String endTimeStr = s[3];
  693 + if (startTimeStr == null || endTimeStr == null || startTimeStr.length() != 14 || endTimeStr.length() != 14) {
  694 + defaultResult.setResult(HookResult.SUCCESS());
  695 + return defaultResult;
  696 + }
  697 + String startTime = DateUtil.urlToyyyy_MM_dd_HH_mm_ss(startTimeStr);
  698 + String endTime = DateUtil.urlToyyyy_MM_dd_HH_mm_ss(endTimeStr);
  699 + logger.info("[ZLM HOOK] 回放流未找到, 发起自动点播:{}->{}->{}/{}-{}-{}",
  700 + param.getMediaServerId(), param.getSchema(),
  701 + param.getApp(), param.getStream(),
  702 + startTime, endTime
  703 + );
  704 + RequestMessage msg = new RequestMessage();
  705 + String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId;
  706 + boolean exist = resultHolder.exist(key, null);
  707 + msg.setKey(key);
  708 + String uuid = UUID.randomUUID().toString();
  709 + msg.setId(uuid);
  710 + DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
  711 +
  712 + result.onTimeout(() -> {
  713 + logger.info("[ZLM HOOK] 回放流自动点播, 等待超时");
  714 + // 释放rtpserver
  715 + msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
  716 + resultHolder.invokeResult(msg);
  717 + });
  718 +
  719 + resultHolder.put(key, uuid, result);
  720 +
  721 + if (!exist) {
  722 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaInfo, param.getStream(), null,
  723 + device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam());
  724 + playService.playBack(mediaInfo, ssrcInfo, deviceId, channelId, startTime, endTime, (code, message, data) -> {
  725 + msg.setData(new HookResult(code, message));
  726 + resultHolder.invokeResult(msg);
  727 + });
  728 + }
  729 + return result;
  730 + }else {
  731 + defaultResult.setResult(HookResult.SUCCESS());
  732 + return defaultResult;
685 } 733 }
686 - return result; 734 +
687 } else { 735 } else {
688 // 拉流代理 736 // 拉流代理
689 StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream()); 737 StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
@@ -40,7 +40,6 @@ public interface IPlayService { @@ -40,7 +40,6 @@ public interface IPlayService {
40 40
41 void playBack(String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback); 41 void playBack(String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback);
42 void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback); 42 void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback);
43 -  
44 void zlmServerOffline(String mediaServerId); 43 void zlmServerOffline(String mediaServerId);
45 44
46 void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback); 45 void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback);
@@ -72,4 +71,6 @@ public interface IPlayService { @@ -72,4 +71,6 @@ public interface IPlayService {
72 void stopTalk(Device device, String channelId, Boolean streamIsReady); 71 void stopTalk(Device device, String channelId, Boolean streamIsReady);
73 72
74 void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback); 73 void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback);
  74 +
  75 +
75 } 76 }
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java
@@ -126,7 +126,15 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService { @@ -126,7 +126,15 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService {
126 List<DeviceChannel> deviceChannelList = new ArrayList<>(); 126 List<DeviceChannel> deviceChannelList = new ArrayList<>();
127 if (channelReduces.size() > 0){ 127 if (channelReduces.size() > 0){
128 PlatformCatalog catalog = catalogManager.selectByPlatFormAndCatalogId(platform.getServerGBId(),catalogId); 128 PlatformCatalog catalog = catalogManager.selectByPlatFormAndCatalogId(platform.getServerGBId(),catalogId);
129 - if (catalog == null || !catalogId.equals(platform.getDeviceGBId())) { 129 + if (catalog == null && catalogId.equals(platform.getDeviceGBId())) {
  130 + for (ChannelReduce channelReduce : channelReduces) {
  131 + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId());
  132 + deviceChannel.setParental(0);
  133 + deviceChannel.setCivilCode(platform.getServerGBDomain());
  134 + deviceChannelList.add(deviceChannel);
  135 + }
  136 + return deviceChannelList;
  137 + } else if (catalog == null || !catalogId.equals(platform.getDeviceGBId())) {
130 logger.warn("未查询到目录{}的信息", catalogId); 138 logger.warn("未查询到目录{}的信息", catalogId);
131 return null; 139 return null;
132 } 140 }
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
@@ -710,7 +710,19 @@ public class PlayServiceImpl implements IPlayService { @@ -710,7 +710,19 @@ public class PlayServiceImpl implements IPlayService {
710 return; 710 return;
711 } 711 }
712 MediaServerItem newMediaServerItem = getNewMediaServerItem(device); 712 MediaServerItem newMediaServerItem = getNewMediaServerItem(device);
713 - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, null, device.isSsrcCheck(), true, 0, false,false, device.getStreamModeForParam()); 713 + String stream = null;
  714 + if (newMediaServerItem.isRtpEnable()) {
  715 + String startTimeStr = startTime.replace("-", "")
  716 + .replace(":", "")
  717 + .replace(" ", "");
  718 + System.out.println(startTimeStr);
  719 + String endTimeTimeStr = endTime.replace("-", "")
  720 + .replace(":", "")
  721 + .replace(" ", "");
  722 + System.out.println(endTimeTimeStr);
  723 + stream = deviceId + "_" + channelId + "_" + startTimeStr + "_" + endTimeTimeStr;
  724 + }
  725 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, stream, null, device.isSsrcCheck(), true, 0, false,false, device.getStreamModeForParam());
714 playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, callback); 726 playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, callback);
715 } 727 }
716 728
src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java
@@ -53,6 +53,10 @@ public class DateUtil { @@ -53,6 +53,10 @@ public class DateUtil {
53 return formatter.format(formatterCompatibleISO8601.parse(formatTime)); 53 return formatter.format(formatterCompatibleISO8601.parse(formatTime));
54 } 54 }
55 55
  56 + public static String urlToyyyy_MM_dd_HH_mm_ss(String formatTime) {
  57 + return formatter.format(urlFormatter.parse(formatTime));
  58 + }
  59 +
56 /** 60 /**
57 * yyyy_MM_dd_HH_mm_ss 转时间戳 61 * yyyy_MM_dd_HH_mm_ss 转时间戳
58 * @param formatTime 62 * @param formatTime
@@ -82,6 +86,7 @@ public class DateUtil { @@ -82,6 +86,7 @@ public class DateUtil {
82 return urlFormatter.format(nowDateTime); 86 return urlFormatter.format(nowDateTime);
83 } 87 }
84 88
  89 +
85 /** 90 /**
86 * 格式校验 91 * 格式校验
87 * @param timeStr 时间字符串 92 * @param timeStr 时间字符串