Commit db6dd3f7550c433f06475540a13598cbeed12d08

Authored by 648540858
2 parents 9889f908 19533c8f

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

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
#	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/ZLMServerFactory.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamCloseResponseListener.java
#	src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java
Showing 38 changed files with 986 additions and 229 deletions
sql/2.6.8补丁更新.sql 0 → 100644
  1 +alter table media_server
  2 + add sendRtpPortRange varchar(50) not null;
0 \ No newline at end of file 3 \ No newline at end of file
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
@@ -159,6 +159,7 @@ public class VideoManagerConstants { @@ -159,6 +159,7 @@ public class VideoManagerConstants {
159 public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_"; 159 public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_";
160 public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_"; 160 public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_";
161 public static final String WVP_OTHER_SEND_RTP_INFO = "VMP_OTHER_SEND_RTP_INFO_"; 161 public static final String WVP_OTHER_SEND_RTP_INFO = "VMP_OTHER_SEND_RTP_INFO_";
  162 + public static final String WVP_OTHER_RECEIVE_RTP_INFO = "VMP_OTHER_RECEIVE_RTP_INFO_";
162 163
163 /** 164 /**
164 * Redis Const 165 * Redis Const
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
@@ -233,5 +233,4 @@ public class MediaConfig{ @@ -233,5 +233,4 @@ public class MediaConfig{
233 } 233 }
234 return false; 234 return false;
235 } 235 }
236 -  
237 } 236 }
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181; @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181;
2 2
3 import com.genersoft.iot.vmp.conf.SipConfig; 3 import com.genersoft.iot.vmp.conf.SipConfig;
4 import com.genersoft.iot.vmp.conf.UserSetting; 4 import com.genersoft.iot.vmp.conf.UserSetting;
  5 +import com.genersoft.iot.vmp.gb28181.bean.GbStringMsgParserFactory;
5 import com.genersoft.iot.vmp.gb28181.conf.DefaultProperties; 6 import com.genersoft.iot.vmp.gb28181.conf.DefaultProperties;
6 import com.genersoft.iot.vmp.gb28181.transmit.ISIPProcessorObserver; 7 import com.genersoft.iot.vmp.gb28181.transmit.ISIPProcessorObserver;
7 import gov.nist.javax.sip.SipProviderImpl; 8 import gov.nist.javax.sip.SipProviderImpl;
@@ -63,6 +64,7 @@ public class SipLayer implements CommandLineRunner { @@ -63,6 +64,7 @@ public class SipLayer implements CommandLineRunner {
63 SipStackImpl sipStack; 64 SipStackImpl sipStack;
64 try { 65 try {
65 sipStack = (SipStackImpl)SipFactory.getInstance().createSipStack(DefaultProperties.getProperties(monitorIp, userSetting.getSipLog())); 66 sipStack = (SipStackImpl)SipFactory.getInstance().createSipStack(DefaultProperties.getProperties(monitorIp, userSetting.getSipLog()));
  67 + sipStack.setMessageParserFactory(new GbStringMsgParserFactory());
66 } catch (PeerUnavailableException e) { 68 } catch (PeerUnavailableException e) {
67 logger.error("[SIP SERVER] SIP服务启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp); 69 logger.error("[SIP SERVER] SIP服务启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp);
68 return; 70 return;
@@ -75,7 +77,6 @@ public class SipLayer implements CommandLineRunner { @@ -75,7 +77,6 @@ public class SipLayer implements CommandLineRunner {
75 tcpSipProvider.setDialogErrorsAutomaticallyHandled(); 77 tcpSipProvider.setDialogErrorsAutomaticallyHandled();
76 tcpSipProvider.addSipListener(sipProcessorObserver); 78 tcpSipProvider.addSipListener(sipProcessorObserver);
77 tcpSipProviderMap.put(monitorIp, tcpSipProvider); 79 tcpSipProviderMap.put(monitorIp, tcpSipProvider);
78 -  
79 logger.info("[SIP SERVER] tcp://{}:{} 启动成功", monitorIp, port); 80 logger.info("[SIP SERVER] tcp://{}:{} 启动成功", monitorIp, port);
80 } catch (TransportNotSupportedException 81 } catch (TransportNotSupportedException
81 | TooManyListenersException 82 | TooManyListenersException
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
@@ -244,6 +244,9 @@ public class Device { @@ -244,6 +244,9 @@ public class Device {
244 } 244 }
245 245
246 public Integer getStreamModeForParam() { 246 public Integer getStreamModeForParam() {
  247 + if (streamMode == null) {
  248 + return 0;
  249 + }
247 if (streamMode.equalsIgnoreCase("UDP")) { 250 if (streamMode.equalsIgnoreCase("UDP")) {
248 return 0; 251 return 0;
249 }else if (streamMode.equalsIgnoreCase("TCP-PASSIVE")) { 252 }else if (streamMode.equalsIgnoreCase("TCP-PASSIVE")) {
src/main/java/com/genersoft/iot/vmp/gb28181/bean/GBStringMsgParser.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.bean;
  2 +
  3 +import gov.nist.core.CommonLogger;
  4 +import gov.nist.core.Host;
  5 +import gov.nist.core.HostNameParser;
  6 +import gov.nist.core.StackLogger;
  7 +import gov.nist.javax.sip.SIPConstants;
  8 +import gov.nist.javax.sip.address.AddressImpl;
  9 +import gov.nist.javax.sip.address.GenericURI;
  10 +import gov.nist.javax.sip.address.SipUri;
  11 +import gov.nist.javax.sip.address.TelephoneNumber;
  12 +import gov.nist.javax.sip.header.*;
  13 +import gov.nist.javax.sip.message.SIPMessage;
  14 +import gov.nist.javax.sip.message.SIPRequest;
  15 +import gov.nist.javax.sip.message.SIPResponse;
  16 +import gov.nist.javax.sip.parser.*;
  17 +
  18 +import java.io.UnsupportedEncodingException;
  19 +import java.text.ParseException;
  20 +
  21 +public class GBStringMsgParser implements MessageParser {
  22 +
  23 + protected static boolean computeContentLengthFromMessage = false;
  24 +
  25 + private static StackLogger logger = CommonLogger.getLogger(StringMsgParser.class);
  26 +
  27 + /**
  28 + * @since v0.9
  29 + */
  30 + public GBStringMsgParser() {
  31 + super();
  32 + }
  33 +
  34 + /**
  35 + * Parse a buffer containing a single SIP Message where the body is an array
  36 + * of un-interpreted bytes. This is intended for parsing the message from a
  37 + * memory buffer when the buffer. Incorporates a bug fix for a bug that was
  38 + * noted by Will Sullin of Callcast
  39 + *
  40 + * @param msgBuffer
  41 + * a byte buffer containing the messages to be parsed. This can
  42 + * consist of multiple SIP Messages concatenated together.
  43 + * @return a SIPMessage[] structure (request or response) containing the
  44 + * parsed SIP message.
  45 + * @exception ParseException
  46 + * is thrown when an illegal message has been encountered
  47 + * (and the rest of the buffer is discarded).
  48 + * @see ParseExceptionListener
  49 + */
  50 + public SIPMessage parseSIPMessage(byte[] msgBuffer, boolean readBody, boolean strict, ParseExceptionListener parseExceptionListener) throws ParseException {
  51 + if (msgBuffer == null || msgBuffer.length == 0)
  52 + return null;
  53 +
  54 + int i = 0;
  55 +
  56 + // Squeeze out any leading control character.
  57 + try {
  58 + while (msgBuffer[i] < 0x20)
  59 + i++;
  60 + }
  61 + catch (ArrayIndexOutOfBoundsException e) {
  62 + // Array contains only control char, return null.
  63 + if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
  64 + logger.logDebug("handled only control char so returning null");
  65 + }
  66 + return null;
  67 + }
  68 +
  69 + // Iterate thru the request/status line and headers.
  70 + String currentLine = null;
  71 + String currentHeader = null;
  72 + boolean isFirstLine = true;
  73 + SIPMessage message = null;
  74 + do
  75 + {
  76 + int lineStart = i;
  77 +
  78 + // Find the length of the line.
  79 + try {
  80 + while (msgBuffer[i] != '\r' && msgBuffer[i] != '\n')
  81 + i++;
  82 + }
  83 + catch (ArrayIndexOutOfBoundsException e) {
  84 + // End of the message.
  85 + break;
  86 + }
  87 + int lineLength = i - lineStart;
  88 +
  89 + // Make it a String.
  90 + try {
  91 + currentLine = new String(msgBuffer, lineStart, lineLength, "UTF-8");
  92 + } catch (UnsupportedEncodingException e) {
  93 + throw new ParseException("Bad message encoding!", 0);
  94 + }
  95 +
  96 + currentLine = trimEndOfLine(currentLine);
  97 +
  98 + if (currentLine.length() == 0) {
  99 + // Last header line, process the previous buffered header.
  100 + if (currentHeader != null && message != null) {
  101 + processHeader(currentHeader, message, parseExceptionListener, msgBuffer);
  102 + }
  103 +
  104 + }
  105 + else {
  106 + if (isFirstLine) {
  107 + message = processFirstLine(currentLine, parseExceptionListener, msgBuffer);
  108 + } else {
  109 + char firstChar = currentLine.charAt(0);
  110 + if (firstChar == '\t' || firstChar == ' ') {
  111 + if (currentHeader == null)
  112 + throw new ParseException("Bad header continuation.", 0);
  113 +
  114 + // This is a continuation, append it to the previous line.
  115 + currentHeader += currentLine.substring(1);
  116 + }
  117 + else {
  118 + if (currentHeader != null && message != null) {
  119 + processHeader(currentHeader, message, parseExceptionListener, msgBuffer);
  120 + }
  121 + currentHeader = currentLine;
  122 + }
  123 + }
  124 + }
  125 +
  126 + if (msgBuffer[i] == '\r' && msgBuffer.length > i+1 && msgBuffer[i+1] == '\n')
  127 + i++;
  128 +
  129 + i++;
  130 +
  131 + isFirstLine = false;
  132 + } while (currentLine.length() > 0); // End do - while
  133 +
  134 + if (message == null) throw new ParseException("Bad message", 0);
  135 + message.setSize(i);
  136 +
  137 + // Check for content legth header
  138 + if (readBody && message.getContentLength() != null ) {
  139 + if ( message.getContentLength().getContentLength() != 0) {
  140 + int bodyLength = msgBuffer.length - i;
  141 +
  142 + byte[] body = new byte[bodyLength];
  143 + System.arraycopy(msgBuffer, i, body, 0, bodyLength);
  144 + message.setMessageContent(body,!strict,computeContentLengthFromMessage,message.getContentLength().getContentLength());
  145 + } else if (message.getCSeqHeader().getMethod().equalsIgnoreCase("MESSAGE")) {
  146 + int bodyLength = msgBuffer.length - i;
  147 +
  148 + byte[] body = new byte[bodyLength];
  149 + System.arraycopy(msgBuffer, i, body, 0, bodyLength);
  150 + message.setMessageContent(body,!strict,computeContentLengthFromMessage,bodyLength);
  151 + }else if (!computeContentLengthFromMessage && strict) {
  152 + String last4Chars = new String(msgBuffer, msgBuffer.length - 4, 4);
  153 + if(!"\r\n\r\n".equals(last4Chars)) {
  154 + throw new ParseException("Extraneous characters at the end of the message ",i);
  155 + }
  156 + }
  157 + }
  158 +
  159 + return message;
  160 + }
  161 +
  162 + protected static String trimEndOfLine(String line) {
  163 + if (line == null)
  164 + return line;
  165 +
  166 + int i = line.length() - 1;
  167 + while (i >= 0 && line.charAt(i) <= 0x20)
  168 + i--;
  169 +
  170 + if (i == line.length() - 1)
  171 + return line;
  172 +
  173 + if (i == -1)
  174 + return "";
  175 +
  176 + return line.substring(0, i+1);
  177 + }
  178 +
  179 + protected SIPMessage processFirstLine(String firstLine, ParseExceptionListener parseExceptionListener, byte[] msgBuffer) throws ParseException {
  180 + SIPMessage message;
  181 + if (!firstLine.startsWith(SIPConstants.SIP_VERSION_STRING)) {
  182 + message = new SIPRequest();
  183 + try {
  184 + RequestLine requestLine = new RequestLineParser(firstLine + "\n")
  185 + .parse();
  186 + ((SIPRequest) message).setRequestLine(requestLine);
  187 + } catch (ParseException ex) {
  188 + if (parseExceptionListener != null)
  189 + try {
  190 + parseExceptionListener.handleException(ex, message,
  191 + RequestLine.class, firstLine, new String(msgBuffer, "UTF-8"));
  192 + } catch (UnsupportedEncodingException e) {
  193 + e.printStackTrace();
  194 + }
  195 + else
  196 + throw ex;
  197 +
  198 + }
  199 + } else {
  200 + message = new SIPResponse();
  201 + try {
  202 + StatusLine sl = new StatusLineParser(firstLine + "\n").parse();
  203 + ((SIPResponse) message).setStatusLine(sl);
  204 + } catch (ParseException ex) {
  205 + if (parseExceptionListener != null) {
  206 + try {
  207 + parseExceptionListener.handleException(ex, message,
  208 + StatusLine.class, firstLine, new String(msgBuffer, "UTF-8"));
  209 + } catch (UnsupportedEncodingException e) {
  210 + e.printStackTrace();
  211 + }
  212 + } else
  213 + throw ex;
  214 +
  215 + }
  216 + }
  217 + return message;
  218 + }
  219 +
  220 + protected void processHeader(String header, SIPMessage message, ParseExceptionListener parseExceptionListener, byte[] rawMessage) throws ParseException {
  221 + if (header == null || header.length() == 0)
  222 + return;
  223 +
  224 + HeaderParser headerParser = null;
  225 + try {
  226 + headerParser = ParserFactory.createParser(header + "\n");
  227 + } catch (ParseException ex) {
  228 + // https://java.net/jira/browse/JSIP-456
  229 + if (parseExceptionListener != null) {
  230 + parseExceptionListener.handleException(ex, message, null,
  231 + header, null);
  232 + return;
  233 + } else {
  234 + throw ex;
  235 + }
  236 + }
  237 +
  238 + try {
  239 + SIPHeader sipHeader = headerParser.parse();
  240 + message.attachHeader(sipHeader, false);
  241 + } catch (ParseException ex) {
  242 + if (parseExceptionListener != null) {
  243 + String headerName = Lexer.getHeaderName(header);
  244 + Class headerClass = NameMap.getClassFromName(headerName);
  245 + if (headerClass == null) {
  246 + headerClass = ExtensionHeaderImpl.class;
  247 +
  248 + }
  249 + try {
  250 + parseExceptionListener.handleException(ex, message,
  251 + headerClass, header, new String(rawMessage, "UTF-8"));
  252 + } catch (UnsupportedEncodingException e) {
  253 + e.printStackTrace();
  254 + }
  255 +
  256 + }
  257 + }
  258 + }
  259 +
  260 + /**
  261 + * Parse an address (nameaddr or address spec) and return and address
  262 + * structure.
  263 + *
  264 + * @param address
  265 + * is a String containing the address to be parsed.
  266 + * @return a parsed address structure.
  267 + * @since v1.0
  268 + * @exception ParseException
  269 + * when the address is badly formatted.
  270 + */
  271 + public AddressImpl parseAddress(String address) throws ParseException {
  272 + AddressParser addressParser = new AddressParser(address);
  273 + return addressParser.address(true);
  274 + }
  275 +
  276 + /**
  277 + * Parse a host:port and return a parsed structure.
  278 + *
  279 + * @param hostport
  280 + * is a String containing the host:port to be parsed
  281 + * @return a parsed address structure.
  282 + * @since v1.0
  283 + * @exception throws
  284 + * a ParseException when the address is badly formatted.
  285 + *
  286 + public HostPort parseHostPort(String hostport) throws ParseException {
  287 + Lexer lexer = new Lexer("charLexer", hostport);
  288 + return new HostNameParser(lexer).hostPort();
  289 +
  290 + }
  291 + */
  292 +
  293 + /**
  294 + * Parse a host name and return a parsed structure.
  295 + *
  296 + * @param host
  297 + * is a String containing the host name to be parsed
  298 + * @return a parsed address structure.
  299 + * @since v1.0
  300 + * @exception ParseException
  301 + * a ParseException when the hostname is badly formatted.
  302 + */
  303 + public Host parseHost(String host) throws ParseException {
  304 + Lexer lexer = new Lexer("charLexer", host);
  305 + return new HostNameParser(lexer).host();
  306 +
  307 + }
  308 +
  309 + /**
  310 + * Parse a telephone number return a parsed structure.
  311 + *
  312 + * @param telephone_number
  313 + * is a String containing the telephone # to be parsed
  314 + * @return a parsed address structure.
  315 + * @since v1.0
  316 + * @exception ParseException
  317 + * a ParseException when the address is badly formatted.
  318 + */
  319 + public TelephoneNumber parseTelephoneNumber(String telephone_number)
  320 + throws ParseException {
  321 + // Bug fix contributed by Will Scullin
  322 + return new URLParser(telephone_number).parseTelephoneNumber(true);
  323 +
  324 + }
  325 +
  326 + /**
  327 + * Parse a SIP url from a string and return a URI structure for it.
  328 + *
  329 + * @param url
  330 + * a String containing the URI structure to be parsed.
  331 + * @return A parsed URI structure
  332 + * @exception ParseException
  333 + * if there was an error parsing the message.
  334 + */
  335 +
  336 + public SipUri parseSIPUrl(String url) throws ParseException {
  337 + try {
  338 + return new URLParser(url).sipURL(true);
  339 + } catch (ClassCastException ex) {
  340 + throw new ParseException(url + " Not a SIP URL ", 0);
  341 + }
  342 + }
  343 +
  344 + /**
  345 + * Parse a uri from a string and return a URI structure for it.
  346 + *
  347 + * @param url
  348 + * a String containing the URI structure to be parsed.
  349 + * @return A parsed URI structure
  350 + * @exception ParseException
  351 + * if there was an error parsing the message.
  352 + */
  353 +
  354 + public GenericURI parseUrl(String url) throws ParseException {
  355 + return new URLParser(url).parse();
  356 + }
  357 +
  358 + /**
  359 + * Parse an individual SIP message header from a string.
  360 + *
  361 + * @param header
  362 + * String containing the SIP header.
  363 + * @return a SIPHeader structure.
  364 + * @exception ParseException
  365 + * if there was an error parsing the message.
  366 + */
  367 + public static SIPHeader parseSIPHeader(String header) throws ParseException {
  368 + int start = 0;
  369 + int end = header.length() - 1;
  370 + try {
  371 + // Squeeze out any leading control character.
  372 + while (header.charAt(start) <= 0x20)
  373 + start++;
  374 +
  375 + // Squeeze out any trailing control character.
  376 + while (header.charAt(end) <= 0x20)
  377 + end--;
  378 + }
  379 + catch (ArrayIndexOutOfBoundsException e) {
  380 + // Array contains only control char.
  381 + throw new ParseException("Empty header.", 0);
  382 + }
  383 +
  384 + StringBuilder buffer = new StringBuilder(end + 1);
  385 + int i = start;
  386 + int lineStart = start;
  387 + boolean endOfLine = false;
  388 + while (i <= end) {
  389 + char c = header.charAt(i);
  390 + if (c == '\r' || c == '\n') {
  391 + if (!endOfLine) {
  392 + buffer.append(header.substring(lineStart, i));
  393 + endOfLine = true;
  394 + }
  395 + }
  396 + else {
  397 + if (endOfLine) {
  398 + endOfLine = false;
  399 + if (c == ' ' || c == '\t') {
  400 + buffer.append(' ');
  401 + lineStart = i + 1;
  402 + }
  403 + else {
  404 + lineStart = i;
  405 + }
  406 + }
  407 + }
  408 +
  409 + i++;
  410 + }
  411 + buffer.append(header.substring(lineStart, i));
  412 + buffer.append('\n');
  413 +
  414 + HeaderParser hp = ParserFactory.createParser(buffer.toString());
  415 + if (hp == null)
  416 + throw new ParseException("could not create parser", 0);
  417 + return hp.parse();
  418 + }
  419 +
  420 + /**
  421 + * Parse the SIP Request Line
  422 + *
  423 + * @param requestLine
  424 + * a String containing the request line to be parsed.
  425 + * @return a RequestLine structure that has the parsed RequestLine
  426 + * @exception ParseException
  427 + * if there was an error parsing the requestLine.
  428 + */
  429 +
  430 + public RequestLine parseSIPRequestLine(String requestLine)
  431 + throws ParseException {
  432 + requestLine += "\n";
  433 + return new RequestLineParser(requestLine).parse();
  434 + }
  435 +
  436 + /**
  437 + * Parse the SIP Response message status line
  438 + *
  439 + * @param statusLine
  440 + * a String containing the Status line to be parsed.
  441 + * @return StatusLine class corresponding to message
  442 + * @exception ParseException
  443 + * if there was an error parsing
  444 + * @see StatusLine
  445 + */
  446 +
  447 + public StatusLine parseSIPStatusLine(String statusLine)
  448 + throws ParseException {
  449 + statusLine += "\n";
  450 + return new StatusLineParser(statusLine).parse();
  451 + }
  452 +
  453 + public static void setComputeContentLengthFromMessage(
  454 + boolean computeContentLengthFromMessage) {
  455 + GBStringMsgParser.computeContentLengthFromMessage = computeContentLengthFromMessage;
  456 + }
  457 +}
src/main/java/com/genersoft/iot/vmp/gb28181/bean/WvpSipDate.java renamed to src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbSipDate.java
@@ -8,7 +8,7 @@ import java.util.*; @@ -8,7 +8,7 @@ import java.util.*;
8 /** 8 /**
9 * 重写jain sip的SIPDate解决与国标时间格式不一致的问题 9 * 重写jain sip的SIPDate解决与国标时间格式不一致的问题
10 */ 10 */
11 -public class WvpSipDate extends SIPDate { 11 +public class GbSipDate extends SIPDate {
12 12
13 /** 13 /**
14 * 14 *
@@ -17,7 +17,7 @@ public class WvpSipDate extends SIPDate { @@ -17,7 +17,7 @@ public class WvpSipDate extends SIPDate {
17 17
18 private Calendar javaCal; 18 private Calendar javaCal;
19 19
20 - public WvpSipDate(long timeMillis) { 20 + public GbSipDate(long timeMillis) {
21 this.javaCal = new GregorianCalendar(TimeZone.getDefault(), Locale.getDefault()); 21 this.javaCal = new GregorianCalendar(TimeZone.getDefault(), Locale.getDefault());
22 Date date = new Date(timeMillis); 22 Date date = new Date(timeMillis);
23 this.javaCal.setTime(date); 23 this.javaCal.setTime(date);
src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStringMsgParserFactory.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.bean;
  2 +
  3 +import gov.nist.javax.sip.parser.MessageParser;
  4 +import gov.nist.javax.sip.parser.MessageParserFactory;
  5 +import gov.nist.javax.sip.stack.SIPTransactionStack;
  6 +
  7 +public class GbStringMsgParserFactory implements MessageParserFactory {
  8 +
  9 + /**
  10 + * msg parser is completely stateless, reuse isntance for the whole stack
  11 + * fixes https://github.com/RestComm/jain-sip/issues/92
  12 + */
  13 + private static GBStringMsgParser msgParser = new GBStringMsgParser();
  14 + /*
  15 + * (non-Javadoc)
  16 + * @see gov.nist.javax.sip.parser.MessageParserFactory#createMessageParser(gov.nist.javax.sip.stack.SIPTransactionStack)
  17 + */
  18 + public MessageParser createMessageParser(SIPTransactionStack stack) {
  19 + return msgParser;
  20 + }
  21 +}
src/main/java/com/genersoft/iot/vmp/gb28181/conf/DefaultProperties.java
@@ -31,6 +31,8 @@ public class DefaultProperties { @@ -31,6 +31,8 @@ public class DefaultProperties {
31 properties.setProperty("gov.nist.javax.sip.CANCEL_CLIENT_TRANSACTION_CHECKED", "true"); 31 properties.setProperty("gov.nist.javax.sip.CANCEL_CLIENT_TRANSACTION_CHECKED", "true");
32 // 为_NULL _对话框传递_终止的_事件 32 // 为_NULL _对话框传递_终止的_事件
33 properties.setProperty("gov.nist.javax.sip.DELIVER_TERMINATED_EVENT_FOR_NULL_DIALOG", "true"); 33 properties.setProperty("gov.nist.javax.sip.DELIVER_TERMINATED_EVENT_FOR_NULL_DIALOG", "true");
  34 + // 是否自动计算content length的实际长度,默认不计算
  35 + properties.setProperty("gov.nist.javax.sip.COMPUTE_CONTENT_LENGTH_FROM_MESSAGE_BODY", "true");
34 // 会话清理策略 36 // 会话清理策略
35 properties.setProperty("gov.nist.javax.sip.RELEASE_REFERENCES_STRATEGY", "Normal"); 37 properties.setProperty("gov.nist.javax.sip.RELEASE_REFERENCES_STRATEGY", "Normal");
36 // 处理由该服务器处理的基于底层TCP的保持生存超时 38 // 处理由该服务器处理的基于底层TCP的保持生存超时
@@ -42,6 +44,8 @@ public class DefaultProperties { @@ -42,6 +44,8 @@ public class DefaultProperties {
42 // 定义应用程序打算多久审计一次 SIP 堆栈,了解其内部线程的健康状况(该属性指定连续审计之间的时间(以毫秒为单位)) 44 // 定义应用程序打算多久审计一次 SIP 堆栈,了解其内部线程的健康状况(该属性指定连续审计之间的时间(以毫秒为单位))
43 properties.setProperty("gov.nist.javax.sip.THREAD_AUDIT_INTERVAL_IN_MILLISECS", "30000"); 45 properties.setProperty("gov.nist.javax.sip.THREAD_AUDIT_INTERVAL_IN_MILLISECS", "30000");
44 46
  47 +// properties.setProperty("gov.nist.javax.sip.MESSAGE_PROCESSOR_FACTORY", "gov.nist.javax.sip.stack.NioMessageProcessorFactory");
  48 +
45 /** 49 /**
46 * sip_server_log.log 和 sip_debug_log.log ERROR, INFO, WARNING, OFF, DEBUG, TRACE 50 * sip_server_log.log 和 sip_debug_log.log ERROR, INFO, WARNING, OFF, DEBUG, TRACE
47 */ 51 */
src/main/java/com/genersoft/iot/vmp/gb28181/conf/ServerLoggerImpl.java
1 package com.genersoft.iot.vmp.gb28181.conf; 1 package com.genersoft.iot.vmp.gb28181.conf;
2 2
  3 +import gov.nist.core.CommonLogger;
3 import gov.nist.core.ServerLogger; 4 import gov.nist.core.ServerLogger;
4 import gov.nist.core.StackLogger; 5 import gov.nist.core.StackLogger;
5 import gov.nist.javax.sip.message.SIPMessage; 6 import gov.nist.javax.sip.message.SIPMessage;
@@ -84,7 +85,7 @@ public class ServerLoggerImpl implements ServerLogger { @@ -84,7 +85,7 @@ public class ServerLoggerImpl implements ServerLogger {
84 } 85 }
85 if(sipStack instanceof SIPTransactionStack) { 86 if(sipStack instanceof SIPTransactionStack) {
86 this.sipStack = (SIPTransactionStack)sipStack; 87 this.sipStack = (SIPTransactionStack)sipStack;
87 - this.stackLogger = this.sipStack.getStackLogger(); 88 + this.stackLogger = CommonLogger.getLogger(SIPTransactionStack.class);
88 } 89 }
89 } 90 }
90 } 91 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
@@ -67,7 +67,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -67,7 +67,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
67 private SipSubscribe sipSubscribe; 67 private SipSubscribe sipSubscribe;
68 68
69 @Autowired 69 @Autowired
70 - private ZLMServerFactory zlmserverfactory; 70 + private ZLMServerFactory zlmServerFactory;
71 71
72 @Autowired 72 @Autowired
73 private SipLayer sipLayer; 73 private SipLayer sipLayer;
@@ -753,7 +753,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -753,7 +753,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
753 .append("<Response>\r\n") 753 .append("<Response>\r\n")
754 .append("<CmdType>RecordInfo</CmdType>\r\n") 754 .append("<CmdType>RecordInfo</CmdType>\r\n")
755 .append("<SN>" +recordInfo.getSn() + "</SN>\r\n") 755 .append("<SN>" +recordInfo.getSn() + "</SN>\r\n")
756 - .append("<DeviceID>" + recordInfo.getDeviceId() + "</DeviceID>\r\n") 756 + .append("<DeviceID>" + recordInfo.getChannelId() + "</DeviceID>\r\n")
757 .append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n"); 757 .append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
758 if (recordInfo.getRecordList() == null ) { 758 if (recordInfo.getRecordList() == null ) {
759 recordXml.append("<RecordList Num=\"0\">\r\n"); 759 recordXml.append("<RecordList Num=\"0\">\r\n");
@@ -842,7 +842,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -842,7 +842,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
842 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); 842 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
843 if (mediaServerItem != null) { 843 if (mediaServerItem != null) {
844 mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); 844 mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
845 - zlmserverfactory.closeRtpServer(mediaServerItem, sendRtpItem.getStream()); 845 + zlmServerFactory.closeRtpServer(mediaServerItem, sendRtpItem.getStreamId());
846 } 846 }
847 SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(platform, sendRtpItem); 847 SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(platform, sendRtpItem);
848 if (byeRequest == null) { 848 if (byeRequest == null) {
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
@@ -206,7 +206,24 @@ public abstract class SIPRequestProcessorParent { @@ -206,7 +206,24 @@ public abstract class SIPRequestProcessorParent {
206 Byte[] bytes = new Byte[0]; 206 Byte[] bytes = new Byte[0];
207 byte[] bytesResult = ArrayUtils.toPrimitive(result.toArray(bytes)); 207 byte[] bytesResult = ArrayUtils.toPrimitive(result.toArray(bytes));
208 208
209 - Document xml = reader.read(new ByteArrayInputStream(bytesResult)); 209 + Document xml;
  210 + try {
  211 + xml = reader.read(new ByteArrayInputStream(bytesResult));
  212 + }catch (DocumentException e) {
  213 + logger.warn("[xml解析异常]: 愿文如下: \r\n{}", new String(bytesResult));
  214 + logger.warn("[xml解析异常]: 愿文如下: 尝试兼容性处理");
  215 + String[] xmlLineArray = new String(bytesResult).split("\\r?\\n");
  216 +
  217 + // 兼容海康的address字段带有<破换xml结构导致无法解析xml的问题
  218 + StringBuilder stringBuilder = new StringBuilder();
  219 + for (String s : xmlLineArray) {
  220 + if (s.startsWith("<Address")) {
  221 + continue;
  222 + }
  223 + stringBuilder.append(s);
  224 + }
  225 + xml = reader.read(new ByteArrayInputStream(stringBuilder.toString().getBytes()));
  226 + }
210 return xml.getRootElement(); 227 return xml.getRootElement();
211 } 228 }
212 229
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
@@ -72,7 +72,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @@ -72,7 +72,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
72 private IVideoManagerStorage storager; 72 private IVideoManagerStorage storager;
73 73
74 @Autowired 74 @Autowired
75 - private ZLMServerFactory ZLMServerFactory; 75 + private ZLMServerFactory zlmServerFactory;
76 76
77 @Autowired 77 @Autowired
78 private SSRCFactory ssrcFactory; 78 private SSRCFactory ssrcFactory;
@@ -125,7 +125,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @@ -125,7 +125,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
125 MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); 125 MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
126 redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), 126 redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(),
127 callIdHeader.getCallId(), null); 127 callIdHeader.getCallId(), null);
128 - ZLMServerFactory.stopSendRtpStream(mediaInfo, param); 128 + zlmServerFactory.stopSendRtpStream(mediaInfo, param);
129 if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) { 129 if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
130 ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId()); 130 ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId());
131 if (platform != null) { 131 if (platform != null) {
@@ -139,7 +139,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @@ -139,7 +139,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
139 } 139 }
140 } 140 }
141 141
142 - int totalReaderCount = ZLMServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId); 142 + int totalReaderCount = zlmServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId);
143 if (totalReaderCount <= 0) { 143 if (totalReaderCount <= 0) {
144 logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId); 144 logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId);
145 if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) { 145 if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) {
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -485,18 +485,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -485,18 +485,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
485 errorEvent.run(code, msg, data); 485 errorEvent.run(code, msg, data);
486 } 486 }
487 }); 487 });
488 - } else {  
489 - sendRtpItem.setPlayType(InviteStreamType.PLAY);  
490 - String streamId = null;  
491 - if (mediaServerItem.isRtpEnable()) {  
492 - streamId = String.format("%s_%s", device.getDeviceId(), channelId);  
493 - } else {  
494 - streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();  
495 - }  
496 - sendRtpItem.setStream(streamId);  
497 - redisCatchStorage.updateSendRTPSever(sendRtpItem);  
498 - playService.play(mediaServerItem, device.getDeviceId(), channelId, ((code, msg, data) -> {  
499 - if (code == InviteErrorCode.SUCCESS.getCode()) { 488 + }else {
  489 +
  490 + SSRCInfo ssrcInfo = playService.play(mediaServerItem, device.getDeviceId(), channelId, ssrc, ((code, msg, data) -> {
  491 + if (code == InviteErrorCode.SUCCESS.getCode()){
500 hookEvent.run(code, msg, data); 492 hookEvent.run(code, msg, data);
501 } else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()) { 493 } else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()) {
502 logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, channelId); 494 logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, channelId);
@@ -506,6 +498,16 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @@ -506,6 +498,16 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
506 errorEvent.run(code, msg, data); 498 errorEvent.run(code, msg, data);
507 } 499 }
508 })); 500 }));
  501 + sendRtpItem.setPlayType(InviteStreamType.PLAY);
  502 + String streamId = null;
  503 + if (mediaServerItem.isRtpEnable()) {
  504 + streamId = String.format("%s_%s", device.getDeviceId(), channelId);
  505 + }else {
  506 + streamId = String.format("%08x", Integer.parseInt(ssrcInfo.getSsrc())).toUpperCase();
  507 + }
  508 + sendRtpItem.setStream(streamId);
  509 + sendRtpItem.setSsrc(ssrcInfo.getSsrc());
  510 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
509 511
510 } 512 }
511 } else if (gbStream != null) { 513 } else if (gbStream != null) {
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
@@ -6,7 +6,7 @@ import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper; @@ -6,7 +6,7 @@ import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
6 import com.genersoft.iot.vmp.gb28181.bean.Device; 6 import com.genersoft.iot.vmp.gb28181.bean.Device;
7 import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; 7 import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo;
8 import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; 8 import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
9 -import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate; 9 +import com.genersoft.iot.vmp.gb28181.bean.GbSipDate;
10 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; 10 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
11 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; 11 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
12 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; 12 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
@@ -148,8 +148,8 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen @@ -148,8 +148,8 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
148 // 添加date头 148 // 添加date头
149 SIPDateHeader dateHeader = new SIPDateHeader(); 149 SIPDateHeader dateHeader = new SIPDateHeader();
150 // 使用自己修改的 150 // 使用自己修改的
151 - WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());  
152 - dateHeader.setDate(wvpSipDate); 151 + GbSipDate gbSipDate = new GbSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
  152 + dateHeader.setDate(gbSipDate);
153 response.addHeader(dateHeader); 153 response.addHeader(dateHeader);
154 154
155 if (request.getExpires() == null) { 155 if (request.getExpires() == null) {
@@ -169,7 +169,18 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen @@ -169,7 +169,18 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
169 device.setGeoCoordSys("WGS84"); 169 device.setGeoCoordSys("WGS84");
170 device.setDeviceId(deviceId); 170 device.setDeviceId(deviceId);
171 device.setOnLine(false); 171 device.setOnLine(false);
  172 + }else {
  173 + if (ObjectUtils.isEmpty(device.getStreamMode())) {
  174 + device.setStreamMode("UDP");
  175 + }
  176 + if (ObjectUtils.isEmpty(device.getCharset())) {
  177 + device.setCharset("GB2312");
  178 + }
  179 + if (ObjectUtils.isEmpty(device.getGeoCoordSys())) {
  180 + device.setGeoCoordSys("WGS84");
  181 + }
172 } 182 }
  183 +
173 device.setIp(remoteAddressInfo.getIp()); 184 device.setIp(remoteAddressInfo.getIp());
174 device.setPort(remoteAddressInfo.getPort()); 185 device.setPort(remoteAddressInfo.getPort());
175 device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort()))); 186 device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
@@ -210,8 +221,8 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen @@ -210,8 +221,8 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
210 // 添加date头 221 // 添加date头
211 SIPDateHeader dateHeader = new SIPDateHeader(); 222 SIPDateHeader dateHeader = new SIPDateHeader();
212 // 使用自己修改的 223 // 使用自己修改的
213 - WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());  
214 - dateHeader.setDate(wvpSipDate); 224 + GbSipDate gbSipDate = new GbSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
  225 + dateHeader.setDate(gbSipDate);
215 response.addHeader(dateHeader); 226 response.addHeader(dateHeader);
216 227
217 // 添加Contact头 228 // 添加Contact头
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
@@ -78,7 +78,9 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp @@ -78,7 +78,9 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
78 device.setKeepaliveIntervalTime(60); 78 device.setKeepaliveIntervalTime(60);
79 }else { 79 }else {
80 long lastTime = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(device.getKeepaliveTime()); 80 long lastTime = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(device.getKeepaliveTime());
81 - device.setKeepaliveIntervalTime(new Long(System.currentTimeMillis()/1000-lastTime).intValue()); 81 + if (System.currentTimeMillis()/1000-lastTime > 10) {
  82 + device.setKeepaliveIntervalTime(new Long(System.currentTimeMillis()/1000-lastTime).intValue());
  83 + }
82 } 84 }
83 85
84 device.setKeepaliveTime(DateUtil.getNow()); 86 device.setKeepaliveTime(DateUtil.getNow());
src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
@@ -380,13 +380,18 @@ public class XmlUtil { @@ -380,13 +380,18 @@ public class XmlUtil {
380 } 380 }
381 } 381 }
382 // 父设备/区域/系统ID 382 // 父设备/区域/系统ID
383 - String realParentId = parentID;  
384 - if (!ObjectUtils.isEmpty(parentID)) { 383 +
  384 + if (!ObjectUtils.isEmpty(parentID) ) {
385 if (parentID.contains("/")) { 385 if (parentID.contains("/")) {
386 String[] parentIdArray = parentID.split("/"); 386 String[] parentIdArray = parentID.split("/");
387 - realParentId = parentIdArray[parentIdArray.length - 1]; 387 + deviceChannel.setParentId(parentIdArray[parentIdArray.length - 1]);
  388 + }else {
  389 + if (parentID.length()%2 == 0) {
  390 + deviceChannel.setParentId(parentID);
  391 + }else {
  392 + logger.warn("[xml解析] 不规范的parentID:{}, 已舍弃", parentID);
  393 + }
388 } 394 }
389 - deviceChannel.setParentId(realParentId);  
390 }else { 395 }else {
391 if (!ObjectUtils.isEmpty(businessGroupID)) { 396 if (!ObjectUtils.isEmpty(businessGroupID)) {
392 deviceChannel.setParentId(businessGroupID); 397 deviceChannel.setParentId(businessGroupID);
src/main/java/com/genersoft/iot/vmp/media/zlm/SendRtpPortManager.java
1 package com.genersoft.iot.vmp.media.zlm; 1 package com.genersoft.iot.vmp.media.zlm;
2 2
  3 +import com.genersoft.iot.vmp.common.VideoManagerConstants;
3 import com.genersoft.iot.vmp.conf.UserSetting; 4 import com.genersoft.iot.vmp.conf.UserSetting;
  5 +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
4 import com.genersoft.iot.vmp.media.zlm.dto.MediaSendRtpPortInfo; 6 import com.genersoft.iot.vmp.media.zlm.dto.MediaSendRtpPortInfo;
  7 +import com.genersoft.iot.vmp.utils.redis.RedisUtil;
5 import org.slf4j.Logger; 8 import org.slf4j.Logger;
6 import org.slf4j.LoggerFactory; 9 import org.slf4j.LoggerFactory;
7 import org.springframework.beans.factory.annotation.Autowired; 10 import org.springframework.beans.factory.annotation.Autowired;
8 import org.springframework.data.redis.core.RedisTemplate; 11 import org.springframework.data.redis.core.RedisTemplate;
9 import org.springframework.stereotype.Component; 12 import org.springframework.stereotype.Component;
10 13
  14 +import java.util.HashMap;
  15 +import java.util.List;
  16 +import java.util.Map;
  17 +
11 @Component 18 @Component
12 public class SendRtpPortManager { 19 public class SendRtpPortManager {
13 20
@@ -29,27 +36,55 @@ public class SendRtpPortManager { @@ -29,27 +36,55 @@ public class SendRtpPortManager {
29 } 36 }
30 37
31 public int getNextPort(String mediaServerId) { 38 public int getNextPort(String mediaServerId) {
32 - String key = KEY + userSetting.getServerId() + "_" + mediaServerId;  
33 - MediaSendRtpPortInfo mediaSendRtpPortInfo = (MediaSendRtpPortInfo)redisTemplate.opsForValue().get(key); 39 + String sendIndexKey = KEY + userSetting.getServerId() + "_" + mediaServerId;
  40 + MediaSendRtpPortInfo mediaSendRtpPortInfo = (MediaSendRtpPortInfo)redisTemplate.opsForValue().get(sendIndexKey);
34 if (mediaSendRtpPortInfo == null) { 41 if (mediaSendRtpPortInfo == null) {
35 - logger.warn("[发送端口管理] 获取{}的发送端口时未找到端口信息", mediaSendRtpPortInfo); 42 + logger.warn("[发送端口管理] 获取{}的发送端口时未找到端口信息", mediaServerId);
36 return 0; 43 return 0;
37 } 44 }
  45 +
  46 + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX
  47 + + userSetting.getServerId() + "_*";
  48 + List<Object> queryResult = RedisUtil.scan(redisTemplate, key);
  49 + Map<Integer, SendRtpItem> sendRtpItemMap = new HashMap<>();
  50 +
  51 + for (Object o : queryResult) {
  52 + SendRtpItem sendRtpItem = (SendRtpItem) redisTemplate.opsForValue().get(o);
  53 + if (sendRtpItem != null) {
  54 + sendRtpItemMap.put(sendRtpItem.getLocalPort(), sendRtpItem);
  55 + }
  56 + }
  57 +
  58 + int port = getPort(mediaSendRtpPortInfo.getCurrent(),
  59 + mediaSendRtpPortInfo.getStart(),
  60 + mediaSendRtpPortInfo.getEnd(), checkPort -> sendRtpItemMap.get(checkPort) == null);
  61 +
  62 + mediaSendRtpPortInfo.setCurrent(port);
  63 + redisTemplate.opsForValue().set(sendIndexKey, mediaSendRtpPortInfo);
  64 + return port;
  65 + }
  66 +
  67 + interface CheckPortCallback{
  68 + boolean check(int port);
  69 + }
  70 +
  71 + private int getPort(int current, int start, int end, CheckPortCallback checkPortCallback) {
38 int port; 72 int port;
39 - if (mediaSendRtpPortInfo.getCurrent() %2 != 0) {  
40 - port = mediaSendRtpPortInfo.getCurrent() + 1; 73 + if (current %2 != 0) {
  74 + port = current + 1;
41 }else { 75 }else {
42 - port = mediaSendRtpPortInfo.getCurrent() + 2; 76 + port = current + 2;
43 } 77 }
44 - if (port > mediaSendRtpPortInfo.getEnd()) {  
45 - if (mediaSendRtpPortInfo.getStart() %2 != 0) {  
46 - port = mediaSendRtpPortInfo.getStart() + 1; 78 + if (port > end) {
  79 + if (start %2 != 0) {
  80 + port = start + 1;
47 }else { 81 }else {
48 - port = mediaSendRtpPortInfo.getStart(); 82 + port = start;
49 } 83 }
50 } 84 }
51 - mediaSendRtpPortInfo.setCurrent(port);  
52 - redisTemplate.opsForValue().set(key, mediaSendRtpPortInfo); 85 + if (!checkPortCallback.check(port)) {
  86 + return getPort(port, start, end, checkPortCallback);
  87 + }
53 return port; 88 return port;
54 } 89 }
55 } 90 }
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -5,6 +5,7 @@ import com.alibaba.fastjson2.JSONObject; @@ -5,6 +5,7 @@ import com.alibaba.fastjson2.JSONObject;
5 import com.genersoft.iot.vmp.common.InviteInfo; 5 import com.genersoft.iot.vmp.common.InviteInfo;
6 import com.genersoft.iot.vmp.common.InviteSessionType; 6 import com.genersoft.iot.vmp.common.InviteSessionType;
7 import com.genersoft.iot.vmp.common.StreamInfo; 7 import com.genersoft.iot.vmp.common.StreamInfo;
  8 +import com.genersoft.iot.vmp.common.VideoManagerConstants;
8 import com.genersoft.iot.vmp.conf.UserSetting; 9 import com.genersoft.iot.vmp.conf.UserSetting;
9 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; 10 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
10 import com.genersoft.iot.vmp.gb28181.bean.*; 11 import com.genersoft.iot.vmp.gb28181.bean.*;
@@ -27,11 +28,13 @@ import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; @@ -27,11 +28,13 @@ import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
27 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 28 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
28 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 29 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
29 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; 30 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
  31 +import com.genersoft.iot.vmp.vmanager.bean.OtherRtpSendInfo;
30 import com.genersoft.iot.vmp.vmanager.bean.StreamContent; 32 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
31 import org.slf4j.Logger; 33 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory; 34 import org.slf4j.LoggerFactory;
33 import org.springframework.beans.factory.annotation.Autowired; 35 import org.springframework.beans.factory.annotation.Autowired;
34 import org.springframework.beans.factory.annotation.Qualifier; 36 import org.springframework.beans.factory.annotation.Qualifier;
  37 +import org.springframework.data.redis.core.RedisTemplate;
35 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 38 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
36 import org.springframework.util.ObjectUtils; 39 import org.springframework.util.ObjectUtils;
37 import org.springframework.web.bind.annotation.*; 40 import org.springframework.web.bind.annotation.*;
@@ -124,6 +127,9 @@ public class ZLMHttpHookListener { @@ -124,6 +127,9 @@ public class ZLMHttpHookListener {
124 @Autowired 127 @Autowired
125 private ThreadPoolTaskExecutor taskExecutor; 128 private ThreadPoolTaskExecutor taskExecutor;
126 129
  130 + @Autowired
  131 + private RedisTemplate<Object, Object> redisTemplate;
  132 +
127 /** 133 /**
128 * 服务器定时上报时间,上报间隔可配置,默认10s上报一次 134 * 服务器定时上报时间,上报间隔可配置,默认10s上报一次
129 */ 135 */
@@ -232,10 +238,7 @@ public class ZLMHttpHookListener { @@ -232,10 +238,7 @@ public class ZLMHttpHookListener {
232 238
233 239
234 HookResultForOnPublish result = HookResultForOnPublish.SUCCESS(); 240 HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
235 - if (!"rtp".equals(param.getApp())) {  
236 - result.setEnable_audio(true);  
237 - }  
238 - 241 + result.setEnable_audio(true);
239 taskExecutor.execute(() -> { 242 taskExecutor.execute(() -> {
240 ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json); 243 ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
241 if (subscribe != null) { 244 if (subscribe != null) {
@@ -264,7 +267,6 @@ public class ZLMHttpHookListener { @@ -264,7 +267,6 @@ public class ZLMHttpHookListener {
264 // 如果是录像下载就设置视频间隔十秒 267 // 如果是录像下载就设置视频间隔十秒
265 if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) { 268 if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {
266 result.setMp4_max_second(10); 269 result.setMp4_max_second(10);
267 - result.setEnable_audio(true);  
268 result.setEnable_mp4(true); 270 result.setEnable_mp4(true);
269 } 271 }
270 // 如果是talk对讲,则默认获取声音 272 // 如果是talk对讲,则默认获取声音
@@ -291,6 +293,14 @@ public class ZLMHttpHookListener { @@ -291,6 +293,14 @@ public class ZLMHttpHookListener {
291 } 293 }
292 } 294 }
293 } 295 }
  296 + if (param.getApp().equalsIgnoreCase("rtp")) {
  297 + String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + "_" + param.getStream();
  298 + OtherRtpSendInfo otherRtpSendInfo = (OtherRtpSendInfo)redisTemplate.opsForValue().get(receiveKey);
  299 + if (otherRtpSendInfo != null) {
  300 + result.setEnable_mp4(true);
  301 + }
  302 + }
  303 + logger.info("[ZLM HOOK]推流鉴权 响应:{}->{}->>>>{}", param.getMediaServerId(), param, result);
294 return result; 304 return result;
295 } 305 }
296 306
@@ -522,8 +532,6 @@ public class ZLMHttpHookListener { @@ -522,8 +532,6 @@ public class ZLMHttpHookListener {
522 if ("rtp".equals(param.getApp())) { 532 if ("rtp".equals(param.getApp())) {
523 ret.put("close", userSetting.getStreamOnDemand()); 533 ret.put("close", userSetting.getStreamOnDemand());
524 // 国标流, 点播/录像回放/录像下载 534 // 国标流, 点播/录像回放/录像下载
525 -// StreamInfo streamInfoForPlayCatch = redisCatchStorage.queryPlayByStreamId(param.getStream());  
526 -  
527 InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream()); 535 InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
528 // 点播 536 // 点播
529 if (inviteInfo != null) { 537 if (inviteInfo != null) {
@@ -588,7 +596,7 @@ public class ZLMHttpHookListener { @@ -588,7 +596,7 @@ public class ZLMHttpHookListener {
588 // 拉流代理 596 // 拉流代理
589 StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream()); 597 StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
590 if (streamProxyItem != null) { 598 if (streamProxyItem != null) {
591 - if (streamProxyItem.isEnableDisableNoneReader()) { 599 + if (streamProxyItem.isEnableRemoveNoneReader()) {
592 // 无人观看自动移除 600 // 无人观看自动移除
593 ret.put("close", true); 601 ret.put("close", true);
594 streamProxyService.del(param.getApp(), param.getStream()); 602 streamProxyService.del(param.getApp(), param.getStream());
@@ -670,7 +678,7 @@ public class ZLMHttpHookListener { @@ -670,7 +678,7 @@ public class ZLMHttpHookListener {
670 resultHolder.put(key, uuid, result); 678 resultHolder.put(key, uuid, result);
671 679
672 if (!exist) { 680 if (!exist) {
673 - playService.play(mediaInfo, deviceId, channelId, (code, message, data) -> { 681 + playService.play(mediaInfo, deviceId, channelId, null, (code, message, data) -> {
674 msg.setData(new HookResult(code, message)); 682 msg.setData(new HookResult(code, message));
675 resultHolder.invokeResult(msg); 683 resultHolder.invokeResult(msg);
676 }); 684 });
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
@@ -61,7 +61,7 @@ public class ZLMMediaListManager { @@ -61,7 +61,7 @@ public class ZLMMediaListManager {
61 private UserSetting userSetting; 61 private UserSetting userSetting;
62 62
63 @Autowired 63 @Autowired
64 - private ZLMServerFactory ZLMServerFactory; 64 + private ZLMServerFactory zlmServerFactory;
65 65
66 @Autowired 66 @Autowired
67 private IMediaServerService mediaServerService; 67 private IMediaServerService mediaServerService;
@@ -97,7 +97,7 @@ public class ZLMMediaListManager { @@ -97,7 +97,7 @@ public class ZLMMediaListManager {
97 public void sendStreamEvent(String app, String stream, String mediaServerId) { 97 public void sendStreamEvent(String app, String stream, String mediaServerId) {
98 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); 98 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
99 // 查看推流状态 99 // 查看推流状态
100 - Boolean streamReady = ZLMServerFactory.isStreamReady(mediaServerItem, app, stream); 100 + Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, app, stream);
101 if (streamReady != null && streamReady) { 101 if (streamReady != null && streamReady) {
102 ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(app, stream); 102 ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(app, stream);
103 if (channelOnlineEventLister != null) { 103 if (channelOnlineEventLister != null) {
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java
@@ -86,10 +86,13 @@ public class ZLMServerFactory { @@ -86,10 +86,13 @@ public class ZLMServerFactory {
86 }else { 86 }else {
87 param.put("port", port); 87 param.put("port", port);
88 } 88 }
89 - param.put("ssrc", ssrc);  
90 if (onlyAuto != null) { 89 if (onlyAuto != null) {
91 param.put("only_audio", onlyAuto?"1":"0"); 90 param.put("only_audio", onlyAuto?"1":"0");
92 } 91 }
  92 + if (ssrc != 0) {
  93 + param.put("ssrc", ssrc);
  94 + }
  95 +
93 JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param); 96 JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param);
94 logger.info(JSONObject.toJSONString(openRtpServerResultJson)); 97 logger.info(JSONObject.toJSONString(openRtpServerResultJson));
95 if (openRtpServerResultJson != null) { 98 if (openRtpServerResultJson != null) {
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java
@@ -21,5 +21,6 @@ public enum HookType { @@ -21,5 +21,6 @@ public enum HookType {
21 on_server_started, 21 on_server_started,
22 22
23 on_rtp_server_timeout, 23 on_rtp_server_timeout,
24 - on_server_keepalive 24 + on_server_keepalive,
  25 + on_send_rtp_stopped
25 } 26 }
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
@@ -51,5 +51,13 @@ public class HookResultForOnPublish extends HookResult{ @@ -51,5 +51,13 @@ public class HookResultForOnPublish extends HookResult{
51 this.mp4_save_path = mp4_save_path; 51 this.mp4_save_path = mp4_save_path;
52 } 52 }
53 53
54 - 54 + @Override
  55 + public String toString() {
  56 + return "HookResultForOnPublish{" +
  57 + "enable_audio=" + enable_audio +
  58 + ", enable_mp4=" + enable_mp4 +
  59 + ", mp4_max_second=" + mp4_max_second +
  60 + ", mp4_save_path='" + mp4_save_path + '\'' +
  61 + '}';
  62 + }
55 } 63 }
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
@@ -27,7 +27,7 @@ public interface IPlayService { @@ -27,7 +27,7 @@ public interface IPlayService {
27 27
28 void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, 28 void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
29 ErrorCallback<Object> callback); 29 ErrorCallback<Object> callback);
30 - SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, ErrorCallback<Object> callback); 30 + SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback<Object> callback);
31 31
32 StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, HookParam hookParam, String deviceId, String channelId); 32 StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, HookParam hookParam, String deviceId, String channelId);
33 33
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
@@ -518,8 +518,12 @@ public class DeviceServiceImpl implements IDeviceService { @@ -518,8 +518,12 @@ public class DeviceServiceImpl implements IDeviceService {
518 if (!ObjectUtils.isEmpty(device.getMediaServerId())) { 518 if (!ObjectUtils.isEmpty(device.getMediaServerId())) {
519 deviceInStore.setMediaServerId(device.getMediaServerId()); 519 deviceInStore.setMediaServerId(device.getMediaServerId());
520 } 520 }
521 - deviceInStore.setSdpIp(device.getSdpIp());  
522 - deviceInStore.setCharset(device.getCharset()); 521 + if (!ObjectUtils.isEmpty(device.getCharset())) {
  522 + deviceInStore.setCharset(device.getCharset());
  523 + }
  524 + if (!ObjectUtils.isEmpty(device.getSdpIp())) {
  525 + deviceInStore.setSdpIp(device.getSdpIp());
  526 + }
523 527
524 // 目录订阅相关的信息 528 // 目录订阅相关的信息
525 if (device.getSubscribeCycleForCatalog() > 0) { 529 if (device.getSubscribeCycleForCatalog() > 0) {
@@ -550,10 +554,18 @@ public class DeviceServiceImpl implements IDeviceService { @@ -550,10 +554,18 @@ public class DeviceServiceImpl implements IDeviceService {
550 removeMobilePositionSubscribe(deviceInStore); 554 removeMobilePositionSubscribe(deviceInStore);
551 } 555 }
552 } 556 }
553 - // 坐标系变化,需要重新计算GCJ02坐标和WGS84坐标  
554 - if (!deviceInStore.getGeoCoordSys().equals(device.getGeoCoordSys())) {  
555 - updateDeviceChannelGeoCoordSys(device); 557 + if (deviceInStore.getGeoCoordSys() != null) {
  558 + // 坐标系变化,需要重新计算GCJ02坐标和WGS84坐标
  559 + if (!deviceInStore.getGeoCoordSys().equals(device.getGeoCoordSys())) {
  560 + updateDeviceChannelGeoCoordSys(device);
  561 + }
  562 + }else {
  563 + device.setGeoCoordSys("WGS84");
556 } 564 }
  565 + if (device.getCharset() == null) {
  566 + device.setCharset("GB2312");
  567 + }
  568 +
557 // 更新redis 569 // 更新redis
558 redisCatchStorage.updateDevice(device); 570 redisCatchStorage.updateDevice(device);
559 deviceMapper.updateCustom(device); 571 deviceMapper.updateCustom(device);
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -183,7 +183,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @@ -183,7 +183,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
183 } 183 }
184 int rtpServerPort; 184 int rtpServerPort;
185 if (mediaServerItem.isRtpEnable()) { 185 if (mediaServerItem.isRtpEnable()) {
186 - rtpServerPort = zlmServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0, port, onlyAuto, reUsePort, tcpMode); 186 + rtpServerPort = zlmServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0, port, reUsePort, tcpMode);
187 } else { 187 } else {
188 rtpServerPort = mediaServerItem.getRtpProxyPort(); 188 rtpServerPort = mediaServerItem.getRtpProxyPort();
189 } 189 }
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
@@ -100,7 +100,7 @@ public class PlayServiceImpl implements IPlayService { @@ -100,7 +100,7 @@ public class PlayServiceImpl implements IPlayService {
100 private ZLMRESTfulUtils zlmresTfulUtils; 100 private ZLMRESTfulUtils zlmresTfulUtils;
101 101
102 @Autowired 102 @Autowired
103 - private ZLMServerFactory zlmserverfactory; 103 + private ZLMServerFactory zlmServerFactory;
104 104
105 @Autowired 105 @Autowired
106 private AssistRESTfulUtils assistRESTfulUtils; 106 private AssistRESTfulUtils assistRESTfulUtils;
@@ -148,7 +148,7 @@ public class PlayServiceImpl implements IPlayService { @@ -148,7 +148,7 @@ public class PlayServiceImpl implements IPlayService {
148 148
149 149
150 @Override 150 @Override
151 - public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, ErrorCallback<Object> callback) { 151 + public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback<Object> callback) {
152 if (mediaServerItem == null) { 152 if (mediaServerItem == null) {
153 throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm"); 153 throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm");
154 } 154 }
@@ -174,7 +174,7 @@ public class PlayServiceImpl implements IPlayService { @@ -174,7 +174,7 @@ public class PlayServiceImpl implements IPlayService {
174 String mediaServerId = streamInfo.getMediaServerId(); 174 String mediaServerId = streamInfo.getMediaServerId();
175 MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); 175 MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
176 176
177 - Boolean ready = zlmserverfactory.isStreamReady(mediaInfo, "rtp", streamId); 177 + Boolean ready = zlmServerFactory.isStreamReady(mediaInfo, "rtp", streamId);
178 if (ready != null && ready) { 178 if (ready != null && ready) {
179 callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); 179 callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo);
180 inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, 180 inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
@@ -445,18 +445,7 @@ public class PlayServiceImpl implements IPlayService { @@ -445,18 +445,7 @@ public class PlayServiceImpl implements IPlayService {
445 streamInfo); 445 streamInfo);
446 logger.info("[点播成功] deviceId: {}, channelId:{}, 码流类型:{}", device.getDeviceId(), 446 logger.info("[点播成功] deviceId: {}, channelId:{}, 码流类型:{}", device.getDeviceId(),
447 device.isSwitchPrimarySubStream() ? "辅码流" : "主码流"); 447 device.isSwitchPrimarySubStream() ? "辅码流" : "主码流");
448 - String streamUrl;  
449 - if (mediaServerItemInuse.getRtspPort() != 0) {  
450 - streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", ssrcInfo.getStream());  
451 - }else {  
452 - streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.mp4", mediaServerItemInuse.getHttpPort(), "rtp", ssrcInfo.getStream());  
453 - }  
454 - String path = "snap";  
455 - String fileName = device.getDeviceId() + "_" + channelId + ".jpg";  
456 - // 请求截图  
457 - logger.info("[请求截图]: " + fileName);  
458 - zlmresTfulUtils.getSnap(mediaServerItemInuse, streamUrl, 15, 1, path, fileName);  
459 - 448 + snapOnPlay(mediaServerItemInuse, device.getDeviceId(), channelId, ssrcInfo.getStream());
460 }, (event) -> { 449 }, (event) -> {
461 inviteInfo.setStatus(InviteSessionStatus.ok); 450 inviteInfo.setStatus(InviteSessionStatus.ok);
462 451
@@ -539,6 +528,7 @@ public class PlayServiceImpl implements IPlayService { @@ -539,6 +528,7 @@ public class PlayServiceImpl implements IPlayService {
539 InviteErrorCode.SUCCESS.getCode(), 528 InviteErrorCode.SUCCESS.getCode(),
540 InviteErrorCode.SUCCESS.getMsg(), 529 InviteErrorCode.SUCCESS.getMsg(),
541 streamInfo); 530 streamInfo);
  531 + snapOnPlay(mediaServerItemInUse, device.getDeviceId(), channelId, stream);
542 }); 532 });
543 return; 533 return;
544 } 534 }
@@ -614,11 +604,33 @@ public class PlayServiceImpl implements IPlayService { @@ -614,11 +604,33 @@ public class PlayServiceImpl implements IPlayService {
614 } 604 }
615 } 605 }
616 606
617 - @Override  
618 - public StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, HookParam hookParam, String deviceId, String channelId) {  
619 - OnStreamChangedHookParam streamChangedHookParam = (OnStreamChangedHookParam) hookParam;  
620 - StreamInfo streamInfo = onPublishHandler(mediaServerItem, streamChangedHookParam, deviceId, channelId); 607 + /**
  608 + * 点播成功时调用截图.
  609 + *
  610 + * @param mediaServerItemInuse media
  611 + * @param deviceId 设备 ID
  612 + * @param channelId 通道 ID
  613 + * @param stream ssrc
  614 + */
  615 + private void snapOnPlay(MediaServerItem mediaServerItemInuse, String deviceId, String channelId, String stream) {
  616 + String streamUrl;
  617 + if (mediaServerItemInuse.getRtspPort() != 0) {
  618 + streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", stream);
  619 + } else {
  620 + streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.mp4", mediaServerItemInuse.getHttpPort(), "rtp", stream);
  621 + }
  622 + String path = "snap";
  623 + String fileName = deviceId + "_" + channelId + ".jpg";
  624 + // 请求截图
  625 + logger.info("[请求截图]: " + fileName);
  626 + zlmresTfulUtils.getSnap(mediaServerItemInuse, streamUrl, 15, 1, path, fileName);
  627 + }
  628 +
  629 + private StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, HookParam hookParam, String deviceId, String channelId) {
  630 + StreamInfo streamInfo = null;
621 Device device = redisCatchStorage.getDevice(deviceId); 631 Device device = redisCatchStorage.getDevice(deviceId);
  632 + OnStreamChangedHookParam streamChangedHookParam = (OnStreamChangedHookParam)hookParam;
  633 + streamInfo = onPublishHandler(mediaServerItem, streamChangedHookParam, deviceId, channelId);
622 if (streamInfo != null) { 634 if (streamInfo != null) {
623 DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); 635 DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
624 if (deviceChannel != null) { 636 if (deviceChannel != null) {
@@ -1656,7 +1668,7 @@ public class PlayServiceImpl implements IPlayService { @@ -1656,7 +1668,7 @@ public class PlayServiceImpl implements IPlayService {
1656 } 1668 }
1657 1669
1658 MediaServerItem newMediaServerItem = getNewMediaServerItem(device); 1670 MediaServerItem newMediaServerItem = getNewMediaServerItem(device);
1659 - play(newMediaServerItem, deviceId, channelId, (code, msg, data)->{ 1671 + play(newMediaServerItem, deviceId, channelId, null, (code, msg, data)->{
1660 if (code == InviteErrorCode.SUCCESS.getCode()) { 1672 if (code == InviteErrorCode.SUCCESS.getCode()) {
1661 InviteInfo inviteInfoForPlay = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); 1673 InviteInfo inviteInfoForPlay = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
1662 if (inviteInfoForPlay != null && inviteInfoForPlay.getStreamInfo() != null) { 1674 if (inviteInfoForPlay != null && inviteInfoForPlay.getStreamInfo() != null) {
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
@@ -4,14 +4,13 @@ import com.alibaba.fastjson2.JSONArray; @@ -4,14 +4,13 @@ import com.alibaba.fastjson2.JSONArray;
4 import com.alibaba.fastjson2.JSONObject; 4 import com.alibaba.fastjson2.JSONObject;
5 import com.genersoft.iot.vmp.common.GeneralCallback; 5 import com.genersoft.iot.vmp.common.GeneralCallback;
6 import com.genersoft.iot.vmp.common.StreamInfo; 6 import com.genersoft.iot.vmp.common.StreamInfo;
  7 +import com.genersoft.iot.vmp.conf.DynamicTask;
7 import com.genersoft.iot.vmp.conf.UserSetting; 8 import com.genersoft.iot.vmp.conf.UserSetting;
8 import com.genersoft.iot.vmp.conf.exception.ControllerException; 9 import com.genersoft.iot.vmp.conf.exception.ControllerException;
9 import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 10 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
10 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; 11 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
11 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; 12 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
12 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; 13 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
13 -import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;  
14 -import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;  
15 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 14 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
16 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; 15 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
17 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; 16 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
@@ -41,6 +40,7 @@ import org.springframework.util.ObjectUtils; @@ -41,6 +40,7 @@ import org.springframework.util.ObjectUtils;
41 import java.util.HashMap; 40 import java.util.HashMap;
42 import java.util.List; 41 import java.util.List;
43 import java.util.Map; 42 import java.util.Map;
  43 +import java.util.UUID;
44 44
45 /** 45 /**
46 * 视频代理业务 46 * 视频代理业务
@@ -87,6 +87,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -87,6 +87,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
87 private ZlmHttpHookSubscribe hookSubscribe; 87 private ZlmHttpHookSubscribe hookSubscribe;
88 88
89 @Autowired 89 @Autowired
  90 + private DynamicTask dynamicTask;
  91 +
  92 + @Autowired
90 DataSourceTransactionManager dataSourceTransactionManager; 93 DataSourceTransactionManager dataSourceTransactionManager;
91 94
92 @Autowired 95 @Autowired
@@ -124,9 +127,6 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -124,9 +127,6 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
124 port = mediaInfo.getRtspPort(); 127 port = mediaInfo.getRtspPort();
125 schemaForUri = schema; 128 schemaForUri = schema;
126 }else if (schema.equalsIgnoreCase("flv")) { 129 }else if (schema.equalsIgnoreCase("flv")) {
127 - port = mediaInfo.getHttpPort();  
128 - schemaForUri = "http";  
129 - }else if (schema.equalsIgnoreCase("rtmp")) {  
130 port = mediaInfo.getRtmpPort(); 130 port = mediaInfo.getRtmpPort();
131 schemaForUri = schema; 131 schemaForUri = schema;
132 }else { 132 }else {
@@ -155,17 +155,28 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -155,17 +155,28 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
155 return; 155 return;
156 } 156 }
157 157
158 - HookSubscribeForStreamChange hookSubscribeForStreamChange = HookSubscribeFactory.on_stream_changed(param.getApp(), param.getStream(), true, "rtsp", mediaInfo.getId());  
159 - hookSubscribe.addSubscribe(hookSubscribeForStreamChange, (mediaServerItem, response) -> {  
160 - StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(  
161 - mediaInfo, param.getApp(), param.getStream(), null, null);  
162 - callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);  
163 - }); 158 + String talkKey = UUID.randomUUID().toString();
  159 + dynamicTask.startCron(talkKey, ()->{
  160 + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(param.getApp(), param.getStream(), mediaInfo.getId(), false);
  161 + if (streamInfo != null) {
  162 + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
  163 + }
  164 + }, 1000);
  165 + String delayTalkKey = UUID.randomUUID().toString();
  166 + dynamicTask.startDelay(delayTalkKey, ()->{
  167 + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(param.getApp(), param.getStream(), mediaInfo.getId(), false);
  168 + if (streamInfo != null) {
  169 + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
  170 + }else {
  171 + dynamicTask.stop(talkKey);
  172 + callback.run(ErrorCode.ERROR100.getCode(), "超时", null);
  173 + }
  174 + }, 5000);
164 175
165 if (param.isEnable()) { 176 if (param.isEnable()) {
166 JSONObject jsonObject = addStreamProxyToZlm(param); 177 JSONObject jsonObject = addStreamProxyToZlm(param);
167 if (jsonObject != null && jsonObject.getInteger("code") == 0) { 178 if (jsonObject != null && jsonObject.getInteger("code") == 0) {
168 - hookSubscribe.removeSubscribe(hookSubscribeForStreamChange); 179 + dynamicTask.stop(talkKey);
169 StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream( 180 StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(
170 mediaInfo, param.getApp(), param.getStream(), null, null); 181 mediaInfo, param.getApp(), param.getStream(), null, null);
171 callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo); 182 callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
@@ -292,10 +303,10 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @@ -292,10 +303,10 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
292 return null; 303 return null;
293 } 304 }
294 if ("default".equals(param.getType())){ 305 if ("default".equals(param.getType())){
295 - result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl(), 306 + result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl().trim(),
296 param.isEnableAudio(), param.isEnableMp4(), param.getRtpType()); 307 param.isEnableAudio(), param.isEnableMp4(), param.getRtpType());
297 }else if ("ffmpeg".equals(param.getType())) { 308 }else if ("ffmpeg".equals(param.getType())) {
298 - result = zlmresTfulUtils.addFFmpegSource(mediaServerItem, param.getSrcUrl(), param.getDstUrl(), 309 + result = zlmresTfulUtils.addFFmpegSource(mediaServerItem, param.getSrcUrl().trim(), param.getDstUrl(),
299 param.getTimeoutMs() + "", param.isEnableAudio(), param.isEnableMp4(), 310 param.getTimeoutMs() + "", param.isEnableAudio(), param.isEnableMp4(),
300 param.getFfmpegCmdKey()); 311 param.getFfmpegCmdKey());
301 } 312 }
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java
@@ -72,7 +72,7 @@ public class RedisGbPlayMsgListener implements MessageListener { @@ -72,7 +72,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
72 private RedisTemplate<Object, Object> redisTemplate; 72 private RedisTemplate<Object, Object> redisTemplate;
73 73
74 @Autowired 74 @Autowired
75 - private ZLMServerFactory ZLMServerFactory; 75 + private ZLMServerFactory zlmServerFactory;
76 76
77 @Autowired 77 @Autowired
78 private IMediaServerService mediaServerService; 78 private IMediaServerService mediaServerService;
@@ -230,7 +230,7 @@ public class RedisGbPlayMsgListener implements MessageListener { @@ -230,7 +230,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
230 param.put("pt", requestPushStreamMsg.getPt()); 230 param.put("pt", requestPushStreamMsg.getPt());
231 param.put("use_ps", requestPushStreamMsg.isPs() ? "1" : "0"); 231 param.put("use_ps", requestPushStreamMsg.isPs() ? "1" : "0");
232 param.put("only_audio", requestPushStreamMsg.isOnlyAudio() ? "1" : "0"); 232 param.put("only_audio", requestPushStreamMsg.isOnlyAudio() ? "1" : "0");
233 - JSONObject jsonObject = ZLMServerFactory.startSendRtpStream(mediaInfo, param); 233 + JSONObject jsonObject = zlmServerFactory.startSendRtpStream(mediaInfo, param);
234 // 回复消息 234 // 回复消息
235 responsePushStream(jsonObject, fromId, serial); 235 responsePushStream(jsonObject, fromId, serial);
236 } 236 }
@@ -267,7 +267,7 @@ public class RedisGbPlayMsgListener implements MessageListener { @@ -267,7 +267,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
267 return; 267 return;
268 } 268 }
269 // 确定流是否在线 269 // 确定流是否在线
270 - Boolean streamReady = ZLMServerFactory.isStreamReady(mediaServerItem, content.getApp(), content.getStream()); 270 + Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, content.getApp(), content.getStream());
271 if (streamReady != null && streamReady) { 271 if (streamReady != null && streamReady) {
272 logger.info("[回复推流信息] {}/{}", content.getApp(), content.getStream()); 272 logger.info("[回复推流信息] {}/{}", content.getApp(), content.getStream());
273 responseSendItem(mediaServerItem, content, toId, serial); 273 responseSendItem(mediaServerItem, content, toId, serial);
@@ -311,7 +311,7 @@ public class RedisGbPlayMsgListener implements MessageListener { @@ -311,7 +311,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
311 * 将获取到的sendItem发送出去 311 * 将获取到的sendItem发送出去
312 */ 312 */
313 private void responseSendItem(MediaServerItem mediaServerItem, RequestSendItemMsg content, String toId, String serial) { 313 private void responseSendItem(MediaServerItem mediaServerItem, RequestSendItemMsg content, String toId, String serial) {
314 - SendRtpItem sendRtpItem = ZLMServerFactory.createSendRtpItem(mediaServerItem, content.getIp(), 314 + SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, content.getIp(),
315 content.getPort(), content.getSsrc(), content.getPlatformId(), 315 content.getPort(), content.getSsrc(), content.getPlatformId(),
316 content.getApp(), content.getStream(), content.getChannelId(), 316 content.getApp(), content.getStream(), content.getChannelId(),
317 content.getTcp(), content.getRtcp()); 317 content.getTcp(), content.getRtcp());
src/main/java/com/genersoft/iot/vmp/vmanager/bean/OtherRtpSendInfo.java
@@ -5,12 +5,17 @@ public class OtherRtpSendInfo { @@ -5,12 +5,17 @@ public class OtherRtpSendInfo {
5 /** 5 /**
6 * 发流IP 6 * 发流IP
7 */ 7 */
8 - private String ip; 8 + private String sendLocalIp;
9 9
10 /** 10 /**
11 - * 发流端口 11 + * 音频发流端口
12 */ 12 */
13 - private int port; 13 + private int sendLocalPortForAudio;
  14 +
  15 + /**
  16 + * 视频发流端口
  17 + */
  18 + private int sendLocalPortForVideo;
14 19
15 /** 20 /**
16 * 收流IP 21 * 收流IP
@@ -18,9 +23,14 @@ public class OtherRtpSendInfo { @@ -18,9 +23,14 @@ public class OtherRtpSendInfo {
18 private String receiveIp; 23 private String receiveIp;
19 24
20 /** 25 /**
21 - * 收流端口 26 + * 音频收流端口
  27 + */
  28 + private int receivePortForAudio;
  29 +
  30 + /**
  31 + * 视频收流端口
22 */ 32 */
23 - private int receivePort; 33 + private int receivePortForVideo;
24 34
25 /** 35 /**
26 * 会话ID 36 * 会话ID
@@ -48,23 +58,6 @@ public class OtherRtpSendInfo { @@ -48,23 +58,6 @@ public class OtherRtpSendInfo {
48 private String pushSSRC; 58 private String pushSSRC;
49 59
50 60
51 -  
52 - public String getIp() {  
53 - return ip;  
54 - }  
55 -  
56 - public void setIp(String ip) {  
57 - this.ip = ip;  
58 - }  
59 -  
60 - public int getPort() {  
61 - return port;  
62 - }  
63 -  
64 - public void setPort(int port) {  
65 - this.port = port;  
66 - }  
67 -  
68 public String getReceiveIp() { 61 public String getReceiveIp() {
69 return receiveIp; 62 return receiveIp;
70 } 63 }
@@ -73,12 +66,20 @@ public class OtherRtpSendInfo { @@ -73,12 +66,20 @@ public class OtherRtpSendInfo {
73 this.receiveIp = receiveIp; 66 this.receiveIp = receiveIp;
74 } 67 }
75 68
76 - public int getReceivePort() {  
77 - return receivePort; 69 + public int getReceivePortForAudio() {
  70 + return receivePortForAudio;
  71 + }
  72 +
  73 + public void setReceivePortForAudio(int receivePortForAudio) {
  74 + this.receivePortForAudio = receivePortForAudio;
  75 + }
  76 +
  77 + public int getReceivePortForVideo() {
  78 + return receivePortForVideo;
78 } 79 }
79 80
80 - public void setReceivePort(int receivePort) {  
81 - this.receivePort = receivePort; 81 + public void setReceivePortForVideo(int receivePortForVideo) {
  82 + this.receivePortForVideo = receivePortForVideo;
82 } 83 }
83 84
84 public String getCallId() { 85 public String getCallId() {
@@ -121,15 +122,45 @@ public class OtherRtpSendInfo { @@ -121,15 +122,45 @@ public class OtherRtpSendInfo {
121 this.pushSSRC = pushSSRC; 122 this.pushSSRC = pushSSRC;
122 } 123 }
123 124
  125 +
  126 + public String getSendLocalIp() {
  127 + return sendLocalIp;
  128 + }
  129 +
  130 + public void setSendLocalIp(String sendLocalIp) {
  131 + this.sendLocalIp = sendLocalIp;
  132 + }
  133 +
  134 + public int getSendLocalPortForAudio() {
  135 + return sendLocalPortForAudio;
  136 + }
  137 +
  138 + public void setSendLocalPortForAudio(int sendLocalPortForAudio) {
  139 + this.sendLocalPortForAudio = sendLocalPortForAudio;
  140 + }
  141 +
  142 + public int getSendLocalPortForVideo() {
  143 + return sendLocalPortForVideo;
  144 + }
  145 +
  146 + public void setSendLocalPortForVideo(int sendLocalPortForVideo) {
  147 + this.sendLocalPortForVideo = sendLocalPortForVideo;
  148 + }
  149 +
124 @Override 150 @Override
125 public String toString() { 151 public String toString() {
126 return "OtherRtpSendInfo{" + 152 return "OtherRtpSendInfo{" +
127 - "ip='" + ip + '\'' +  
128 - ", port=" + port + 153 + "sendLocalIp='" + sendLocalIp + '\'' +
  154 + ", sendLocalPortForAudio=" + sendLocalPortForAudio +
  155 + ", sendLocalPortForVideo=" + sendLocalPortForVideo +
129 ", receiveIp='" + receiveIp + '\'' + 156 ", receiveIp='" + receiveIp + '\'' +
130 - ", receivePort=" + receivePort + 157 + ", receivePortForAudio=" + receivePortForAudio +
  158 + ", receivePortForVideo=" + receivePortForVideo +
131 ", callId='" + callId + '\'' + 159 ", callId='" + callId + '\'' +
132 ", stream='" + stream + '\'' + 160 ", stream='" + stream + '\'' +
  161 + ", pushApp='" + pushApp + '\'' +
  162 + ", pushStream='" + pushStream + '\'' +
  163 + ", pushSSRC='" + pushSSRC + '\'' +
133 '}'; 164 '}';
134 } 165 }
135 } 166 }
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
@@ -470,7 +470,6 @@ public class DeviceQuery { @@ -470,7 +470,6 @@ public class DeviceQuery {
470 public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId, @RequestParam(required = false) String mark) { 470 public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId, @RequestParam(required = false) String mark) {
471 471
472 try { 472 try {
473 -  
474 final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + (mark == null? ".jpg": ("_" + mark + ".jpg"))).toPath()); 473 final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + (mark == null? ".jpg": ("_" + mark + ".jpg"))).toPath());
475 resp.setContentType(MediaType.IMAGE_PNG_VALUE); 474 resp.setContentType(MediaType.IMAGE_PNG_VALUE);
476 IOUtils.copy(in, resp.getOutputStream()); 475 IOUtils.copy(in, resp.getOutputStream());
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
@@ -121,7 +121,7 @@ public class PlayController { @@ -121,7 +121,7 @@ public class PlayController {
121 // 录像查询以channelId作为deviceId查询 121 // 录像查询以channelId作为deviceId查询
122 resultHolder.put(key, uuid, result); 122 resultHolder.put(key, uuid, result);
123 123
124 - playService.play(newMediaServerItem, deviceId, channelId, (code, msg, data) -> { 124 + playService.play(newMediaServerItem, deviceId, channelId, null, (code, msg, data) -> {
125 WVPResult<StreamContent> wvpResult = new WVPResult<>(); 125 WVPResult<StreamContent> wvpResult = new WVPResult<>();
126 if (code == InviteErrorCode.SUCCESS.getCode()) { 126 if (code == InviteErrorCode.SUCCESS.getCode()) {
127 wvpResult.setCode(ErrorCode.SUCCESS.getCode()); 127 wvpResult.setCode(ErrorCode.SUCCESS.getCode());
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
@@ -11,11 +11,9 @@ import com.genersoft.iot.vmp.gb28181.bean.Device; @@ -11,11 +11,9 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
11 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; 11 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
12 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; 12 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
13 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; 13 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
14 -import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;  
15 import com.genersoft.iot.vmp.service.IInviteStreamService; 14 import com.genersoft.iot.vmp.service.IInviteStreamService;
16 import com.genersoft.iot.vmp.service.IPlayService; 15 import com.genersoft.iot.vmp.service.IPlayService;
17 import com.genersoft.iot.vmp.service.bean.InviteErrorCode; 16 import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
18 -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;  
19 import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 17 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
20 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; 18 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
21 import com.genersoft.iot.vmp.vmanager.bean.StreamContent; 19 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
@@ -54,15 +52,9 @@ public class PlaybackController { @@ -54,15 +52,9 @@ public class PlaybackController {
54 private SIPCommander cmder; 52 private SIPCommander cmder;
55 53
56 @Autowired 54 @Autowired
57 - private ZLMServerFactory ZLMServerFactory;  
58 -  
59 - @Autowired  
60 private IVideoManagerStorage storager; 55 private IVideoManagerStorage storager;
61 56
62 @Autowired 57 @Autowired
63 - private IRedisCatchStorage redisCatchStorage;  
64 -  
65 - @Autowired  
66 private IInviteStreamService inviteStreamService; 58 private IInviteStreamService inviteStreamService;
67 59
68 @Autowired 60 @Autowired
src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java
@@ -3,21 +3,18 @@ package com.genersoft.iot.vmp.vmanager.rtp; @@ -3,21 +3,18 @@ package com.genersoft.iot.vmp.vmanager.rtp;
3 import com.alibaba.fastjson2.JSONObject; 3 import com.alibaba.fastjson2.JSONObject;
4 import com.genersoft.iot.vmp.common.VideoManagerConstants; 4 import com.genersoft.iot.vmp.common.VideoManagerConstants;
5 import com.genersoft.iot.vmp.conf.DynamicTask; 5 import com.genersoft.iot.vmp.conf.DynamicTask;
6 -import com.genersoft.iot.vmp.conf.SipConfig;  
7 import com.genersoft.iot.vmp.conf.UserSetting; 6 import com.genersoft.iot.vmp.conf.UserSetting;
8 -import com.genersoft.iot.vmp.conf.VersionInfo;  
9 import com.genersoft.iot.vmp.conf.exception.ControllerException; 7 import com.genersoft.iot.vmp.conf.exception.ControllerException;
10 import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; 8 import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager;
11 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory; 9 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
12 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; 10 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
13 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; 11 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
14 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForRtpServerTimeout; 12 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForRtpServerTimeout;
  13 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
15 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 14 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
16 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRtpServerTimeoutHookParam; 15 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRtpServerTimeoutHookParam;
17 -import com.genersoft.iot.vmp.service.IDeviceChannelService;  
18 -import com.genersoft.iot.vmp.service.IDeviceService;  
19 import com.genersoft.iot.vmp.service.IMediaServerService; 16 import com.genersoft.iot.vmp.service.IMediaServerService;
20 -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 17 +import com.genersoft.iot.vmp.utils.redis.RedisUtil;
21 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; 18 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
22 import com.genersoft.iot.vmp.vmanager.bean.OtherRtpSendInfo; 19 import com.genersoft.iot.vmp.vmanager.bean.OtherRtpSendInfo;
23 import io.swagger.v3.oas.annotations.Operation; 20 import io.swagger.v3.oas.annotations.Operation;
@@ -28,16 +25,15 @@ import okhttp3.Request; @@ -28,16 +25,15 @@ import okhttp3.Request;
28 import org.slf4j.Logger; 25 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory; 26 import org.slf4j.LoggerFactory;
30 import org.springframework.beans.factory.annotation.Autowired; 27 import org.springframework.beans.factory.annotation.Autowired;
31 -import org.springframework.beans.factory.annotation.Value;  
32 import org.springframework.data.redis.core.RedisTemplate; 28 import org.springframework.data.redis.core.RedisTemplate;
33 -import org.springframework.web.bind.annotation.GetMapping;  
34 -import org.springframework.web.bind.annotation.RequestMapping;  
35 -import org.springframework.web.bind.annotation.ResponseBody;  
36 -import org.springframework.web.bind.annotation.RestController; 29 +import org.springframework.util.ObjectUtils;
  30 +import org.springframework.web.bind.annotation.*;
37 31
38 import java.io.IOException; 32 import java.io.IOException;
39 import java.util.HashMap; 33 import java.util.HashMap;
  34 +import java.util.List;
40 import java.util.Map; 35 import java.util.Map;
  36 +import java.util.UUID;
41 import java.util.concurrent.TimeUnit; 37 import java.util.concurrent.TimeUnit;
42 38
43 @SuppressWarnings("rawtypes") 39 @SuppressWarnings("rawtypes")
@@ -62,36 +58,15 @@ public class RtpController { @@ -62,36 +58,15 @@ public class RtpController {
62 private IMediaServerService mediaServerService; 58 private IMediaServerService mediaServerService;
63 59
64 @Autowired 60 @Autowired
65 - private VersionInfo versionInfo;  
66 -  
67 - @Autowired  
68 - private SipConfig sipConfig;  
69 -  
70 - @Autowired  
71 private UserSetting userSetting; 61 private UserSetting userSetting;
72 62
73 @Autowired 63 @Autowired
74 - private IDeviceService deviceService;  
75 -  
76 - @Autowired  
77 - private IDeviceChannelService channelService;  
78 -  
79 - @Autowired  
80 private DynamicTask dynamicTask; 64 private DynamicTask dynamicTask;
81 65
82 -  
83 @Autowired 66 @Autowired
84 private RedisTemplate<Object, Object> redisTemplate; 67 private RedisTemplate<Object, Object> redisTemplate;
85 68
86 69
87 - @Value("${server.port}")  
88 - private int serverPort;  
89 -  
90 -  
91 - @Autowired  
92 - private IRedisCatchStorage redisCatchStorage;  
93 -  
94 -  
95 @GetMapping(value = "/receive/open") 70 @GetMapping(value = "/receive/open")
96 @ResponseBody 71 @ResponseBody
97 @Operation(summary = "开启收流和获取发流信息") 72 @Operation(summary = "开启收流和获取发流信息")
@@ -101,7 +76,7 @@ public class RtpController { @@ -101,7 +76,7 @@ public class RtpController {
101 @Parameter(name = "stream", description = "形成的流的ID", required = true) 76 @Parameter(name = "stream", description = "形成的流的ID", required = true)
102 @Parameter(name = "tcpMode", description = "收流模式, 0为UDP, 1为TCP被动", required = true) 77 @Parameter(name = "tcpMode", description = "收流模式, 0为UDP, 1为TCP被动", required = true)
103 @Parameter(name = "callBack", description = "回调地址,如果收流超时会通道回调通知,回调为get请求,参数为callId", required = true) 78 @Parameter(name = "callBack", description = "回调地址,如果收流超时会通道回调通知,回调为get请求,参数为callId", required = true)
104 - public OtherRtpSendInfo openRtpServer(Boolean isSend, String ssrc, String callId, String stream, Integer tcpMode, String callBack) { 79 + public OtherRtpSendInfo openRtpServer(Boolean isSend, @RequestParam(required = false)String ssrc, String callId, String stream, Integer tcpMode, String callBack) {
105 80
106 logger.info("[第三方服务对接->开启收流和获取发流信息] isSend->{}, ssrc->{}, callId->{}, stream->{}, tcpMode->{}, callBack->{}", 81 logger.info("[第三方服务对接->开启收流和获取发流信息] isSend->{}, ssrc->{}, callId->{}, stream->{}, tcpMode->{}, callBack->{}",
107 isSend, ssrc, callId, stream, tcpMode==0?"UDP":"TCP被动", callBack); 82 isSend, ssrc, callId, stream, tcpMode==0?"UDP":"TCP被动", callBack);
@@ -123,12 +98,16 @@ public class RtpController { @@ -123,12 +98,16 @@ public class RtpController {
123 }catch (NumberFormatException e) { 98 }catch (NumberFormatException e) {
124 throw new ControllerException(ErrorCode.ERROR100.getCode(),"ssrc格式错误"); 99 throw new ControllerException(ErrorCode.ERROR100.getCode(),"ssrc格式错误");
125 } 100 }
126 -  
127 } 101 }
128 - int localPort = zlmServerFactory.createRTPServer(mediaServerItem, stream, ssrcInt, null, false, false, tcpMode); 102 + String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + "_" + callId + "_" + stream;
  103 + int localPortForVideo = zlmServerFactory.createRTPServer(mediaServerItem, stream, ssrcInt, null, false, tcpMode);
  104 + int localPortForAudio = zlmServerFactory.createRTPServer(mediaServerItem, stream + "_a" , ssrcInt, null, false, tcpMode);
  105 + if (localPortForVideo == 0 || localPortForAudio == 0) {
  106 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "获取端口失败");
  107 + }
129 // 注册回调如果rtp收流超时则通过回调发送通知 108 // 注册回调如果rtp收流超时则通过回调发送通知
130 if (callBack != null) { 109 if (callBack != null) {
131 - HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, mediaServerItem.getId()); 110 + HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(stream, String.valueOf(ssrcInt), mediaServerItem.getId());
132 // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 111 // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
133 hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout, 112 hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout,
134 (mediaServerItemInUse, hookParam)->{ 113 (mediaServerItemInUse, hookParam)->{
@@ -142,22 +121,33 @@ public class RtpController { @@ -142,22 +121,33 @@ public class RtpController {
142 try { 121 try {
143 client.newCall(request).execute(); 122 client.newCall(request).execute();
144 } catch (IOException e) { 123 } catch (IOException e) {
145 - logger.error("[开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调失败", callId, e); 124 + logger.error("[第三方服务对接->开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调失败", callId, e);
146 } 125 }
  126 + hookSubscribe.removeSubscribe(hookSubscribeForRtpServerTimeout);
147 } 127 }
148 }); 128 });
149 } 129 }
150 - String key = VideoManagerConstants.WVP_OTHER_SEND_RTP_INFO + userSetting.getServerId() + callId; 130 + String key = VideoManagerConstants.WVP_OTHER_SEND_RTP_INFO + userSetting.getServerId() + "_" + callId;
151 OtherRtpSendInfo otherRtpSendInfo = new OtherRtpSendInfo(); 131 OtherRtpSendInfo otherRtpSendInfo = new OtherRtpSendInfo();
152 otherRtpSendInfo.setReceiveIp(mediaServerItem.getSdpIp()); 132 otherRtpSendInfo.setReceiveIp(mediaServerItem.getSdpIp());
153 - otherRtpSendInfo.setReceivePort(localPort); 133 + otherRtpSendInfo.setReceivePortForVideo(localPortForVideo);
  134 + otherRtpSendInfo.setReceivePortForAudio(localPortForAudio);
154 otherRtpSendInfo.setCallId(callId); 135 otherRtpSendInfo.setCallId(callId);
155 otherRtpSendInfo.setStream(stream); 136 otherRtpSendInfo.setStream(stream);
  137 +
  138 + // 将信息写入redis中,以备后用
  139 + redisTemplate.opsForValue().set(receiveKey, otherRtpSendInfo);
156 if (isSend != null && isSend) { 140 if (isSend != null && isSend) {
157 - int port = sendRtpPortManager.getNextPort(mediaServerItem.getId());  
158 - otherRtpSendInfo.setIp(mediaServerItem.getSdpIp());  
159 - otherRtpSendInfo.setPort(port);  
160 - logger.info("[开启收流和获取发流信息] 结果,callId->{}, {}", callId, otherRtpSendInfo); 141 + // 预创建发流信息
  142 + int portForVideo = sendRtpPortManager.getNextPort(mediaServerItem.getId());
  143 + int portForAudio = sendRtpPortManager.getNextPort(mediaServerItem.getId());
  144 +
  145 + otherRtpSendInfo.setSendLocalIp(mediaServerItem.getSdpIp());
  146 + otherRtpSendInfo.setSendLocalPortForVideo(portForVideo);
  147 + otherRtpSendInfo.setSendLocalPortForAudio(portForAudio);
  148 + // 将信息写入redis中,以备后用
  149 + redisTemplate.opsForValue().set(key, otherRtpSendInfo, 300, TimeUnit.SECONDS);
  150 + logger.info("[第三方服务对接->开启收流和获取发流信息] 结果,callId->{}, {}", callId, otherRtpSendInfo);
161 } 151 }
162 // 将信息写入redis中,以备后用 152 // 将信息写入redis中,以备后用
163 redisTemplate.opsForValue().set(key, otherRtpSendInfo, 300, TimeUnit.SECONDS); 153 redisTemplate.opsForValue().set(key, otherRtpSendInfo, 300, TimeUnit.SECONDS);
@@ -172,25 +162,69 @@ public class RtpController { @@ -172,25 +162,69 @@ public class RtpController {
172 logger.info("[第三方服务对接->关闭收流] stream->{}", stream); 162 logger.info("[第三方服务对接->关闭收流] stream->{}", stream);
173 MediaServerItem mediaServerItem = mediaServerService.getDefaultMediaServer(); 163 MediaServerItem mediaServerItem = mediaServerService.getDefaultMediaServer();
174 zlmServerFactory.closeRtpServer(mediaServerItem,stream); 164 zlmServerFactory.closeRtpServer(mediaServerItem,stream);
  165 + zlmServerFactory.closeRtpServer(mediaServerItem,stream + "_a");
  166 + String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + "_*_" + stream;
  167 + List<Object> scan = RedisUtil.scan(redisTemplate, receiveKey);
  168 + if (scan.size() > 0) {
  169 + for (Object key : scan) {
  170 + // 将信息写入redis中,以备后用
  171 + redisTemplate.delete(key);
  172 + }
  173 + }
175 } 174 }
176 175
177 @GetMapping(value = "/send/start") 176 @GetMapping(value = "/send/start")
178 @ResponseBody 177 @ResponseBody
179 @Operation(summary = "发送流") 178 @Operation(summary = "发送流")
180 @Parameter(name = "ssrc", description = "发送流的SSRC", required = true) 179 @Parameter(name = "ssrc", description = "发送流的SSRC", required = true)
181 - @Parameter(name = "ip", description = "目标IP", required = true)  
182 - @Parameter(name = "port", description = "目标端口", required = true) 180 + @Parameter(name = "dstIpForAudio", description = "目标音频收流IP", required = false)
  181 + @Parameter(name = "dstIpForVideo", description = "目标视频收流IP", required = false)
  182 + @Parameter(name = "dstPortForAudio", description = "目标音频收流端口", required = false)
  183 + @Parameter(name = "dstPortForVideo", description = "目标视频收流端口", required = false)
183 @Parameter(name = "app", description = "待发送应用名", required = true) 184 @Parameter(name = "app", description = "待发送应用名", required = true)
184 @Parameter(name = "stream", description = "待发送流Id", required = true) 185 @Parameter(name = "stream", description = "待发送流Id", required = true)
185 @Parameter(name = "callId", description = "整个过程的唯一标识,不传则使用随机端口发流", required = true) 186 @Parameter(name = "callId", description = "整个过程的唯一标识,不传则使用随机端口发流", required = true)
186 - @Parameter(name = "onlyAudio", description = "是否只有音频", required = true)  
187 @Parameter(name = "isUdp", description = "是否为UDP", required = true) 187 @Parameter(name = "isUdp", description = "是否为UDP", required = true)
188 - @Parameter(name = "streamType", description = "流类型,1为es流,2为ps流, 默认es流", required = false)  
189 - public void sendRTP(String ssrc, String ip, Integer port, String app, String stream, String callId, Boolean onlyAudio, Boolean isUdp, Integer streamType) {  
190 - logger.info("[第三方服务对接->发送流] ssrc->{}, ip->{}, port->{}, app->{}, stream->{}, callId->{}, onlyAudio->{}, streamType->{}",  
191 - ssrc, ip, port, app, stream, callId, onlyAudio, streamType == 1? "ES":"PS"); 188 + @Parameter(name = "ptForAudio", description = "rtp的音频pt", required = false)
  189 + @Parameter(name = "ptForVideo", description = "rtp的视频pt", required = false)
  190 + public void sendRTP(String ssrc,
  191 + @RequestParam(required = false)String dstIpForAudio,
  192 + @RequestParam(required = false)String dstIpForVideo,
  193 + @RequestParam(required = false)Integer dstPortForAudio,
  194 + @RequestParam(required = false)Integer dstPortForVideo,
  195 + String app,
  196 + String stream,
  197 + String callId,
  198 + Boolean isUdp,
  199 + @RequestParam(required = false)Integer ptForAudio,
  200 + @RequestParam(required = false)Integer ptForVideo
  201 + ) {
  202 + logger.info("[第三方服务对接->发送流] " +
  203 + "ssrc->{}, \r\n" +
  204 + "dstIpForAudio->{}, \n" +
  205 + "dstIpForAudio->{}, \n" +
  206 + "dstPortForAudio->{}, \n" +
  207 + "dstPortForVideo->{}, \n" +
  208 + "app->{}, \n" +
  209 + "stream->{}, \n" +
  210 + "callId->{}, \n" +
  211 + "ptForAudio->{}, \n" +
  212 + "ptForVideo->{}",
  213 + ssrc,
  214 + dstIpForAudio,
  215 + dstIpForVideo,
  216 + dstPortForAudio,
  217 + dstPortForVideo,
  218 + app,
  219 + stream,
  220 + callId,
  221 + ptForAudio,
  222 + ptForVideo);
  223 + if (!((dstPortForAudio > 0 && !ObjectUtils.isEmpty(dstPortForAudio) || (dstPortForVideo > 0 && !ObjectUtils.isEmpty(dstIpForVideo))))) {
  224 + throw new ControllerException(ErrorCode.ERROR400.getCode(), "至少应该存在一组音频或视频发送参数");
  225 + }
192 MediaServerItem mediaServerItem = mediaServerService.getDefaultMediaServer(); 226 MediaServerItem mediaServerItem = mediaServerService.getDefaultMediaServer();
193 - String key = VideoManagerConstants.WVP_OTHER_SEND_RTP_INFO + userSetting.getServerId() + callId; 227 + String key = VideoManagerConstants.WVP_OTHER_SEND_RTP_INFO + userSetting.getServerId() + "_" + callId;
194 OtherRtpSendInfo sendInfo = (OtherRtpSendInfo)redisTemplate.opsForValue().get(key); 228 OtherRtpSendInfo sendInfo = (OtherRtpSendInfo)redisTemplate.opsForValue().get(key);
195 if (sendInfo == null) { 229 if (sendInfo == null) {
196 sendInfo = new OtherRtpSendInfo(); 230 sendInfo = new OtherRtpSendInfo();
@@ -199,32 +233,123 @@ public class RtpController { @@ -199,32 +233,123 @@ public class RtpController {
199 sendInfo.setPushStream(stream); 233 sendInfo.setPushStream(stream);
200 sendInfo.setPushSSRC(ssrc); 234 sendInfo.setPushSSRC(ssrc);
201 235
202 - Map<String, Object> param = new HashMap<>(12);  
203 - param.put("vhost","__defaultVhost__");  
204 - param.put("app",app);  
205 - param.put("stream",stream);  
206 - param.put("ssrc", ssrc);  
207 -  
208 - param.put("dst_url",ip);  
209 - param.put("dst_port", port);  
210 - String is_Udp = isUdp ? "1" : "0";  
211 - param.put("is_udp", is_Udp);  
212 - param.put("src_port", sendInfo.getPort());  
213 - param.put("use_ps", streamType==2 ? "1" : "0");  
214 - param.put("only_audio", onlyAudio ? "1" : "0");  
215 -  
216 - JSONObject jsonObject = zlmServerFactory.startSendRtpStream(mediaServerItem, param);  
217 - if (jsonObject.getInteger("code") == 0) {  
218 - logger.info("[第三方服务对接->发送流] 发流成功,callId->{}", callId);  
219 - redisTemplate.opsForValue().set(key, sendInfo);  
220 - }else {  
221 - redisTemplate.delete(key);  
222 - logger.info("[第三方服务对接->发送流] 发流失败,callId->{}, {}", callId, jsonObject.getString("msg"));  
223 - throw new ControllerException(ErrorCode.ERROR100.getCode(), "[发流失败] " + jsonObject.getString("msg")); 236 + Map<String, Object> paramForAudio;
  237 + Map<String, Object> paramForVideo;
  238 + if (!ObjectUtils.isEmpty(dstIpForAudio) && dstPortForAudio > 0) {
  239 + paramForAudio = new HashMap<>();
  240 + paramForAudio.put("vhost","__defaultVhost__");
  241 + paramForAudio.put("app",app);
  242 + paramForAudio.put("stream",stream);
  243 + paramForAudio.put("ssrc", ssrc);
  244 +
  245 + paramForAudio.put("dst_url", dstIpForAudio);
  246 + paramForAudio.put("dst_port", dstPortForAudio);
  247 + String is_Udp = isUdp ? "1" : "0";
  248 + paramForAudio.put("is_udp", is_Udp);
  249 + paramForAudio.put("src_port", sendInfo.getSendLocalPortForAudio());
  250 + paramForAudio.put("use_ps", "0");
  251 + paramForAudio.put("only_audio", "1");
  252 + if (ptForAudio != null) {
  253 + paramForAudio.put("pt", ptForAudio);
  254 + }
  255 +
  256 + } else {
  257 + paramForAudio = null;
  258 + }
  259 + if (!ObjectUtils.isEmpty(dstIpForVideo) && dstPortForVideo > 0) {
  260 + paramForVideo = new HashMap<>();
  261 + paramForVideo.put("vhost","__defaultVhost__");
  262 + paramForVideo.put("app",app);
  263 + paramForVideo.put("stream",stream);
  264 + paramForVideo.put("ssrc", ssrc);
  265 +
  266 + paramForVideo.put("dst_url", dstIpForVideo);
  267 + paramForVideo.put("dst_port", dstPortForVideo);
  268 + String is_Udp = isUdp ? "1" : "0";
  269 + paramForVideo.put("is_udp", is_Udp);
  270 + paramForVideo.put("src_port", sendInfo.getSendLocalPortForVideo());
  271 + paramForVideo.put("use_ps", "0");
  272 + paramForVideo.put("only_audio", "0");
  273 + if (ptForVideo != null) {
  274 + paramForVideo.put("pt", ptForVideo);
  275 + }
  276 +
  277 + } else {
  278 + paramForVideo = null;
224 } 279 }
225 - }  
226 280
  281 + Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, app, stream);
  282 + if (streamReady) {
  283 + if (paramForVideo != null) {
  284 + JSONObject jsonObject = zlmServerFactory.startSendRtpStream(mediaServerItem, paramForVideo);
  285 + if (jsonObject.getInteger("code") == 0) {
  286 + logger.info("[第三方服务对接->发送流] 视频流发流成功,callId->{},param->{}", callId, paramForVideo);
  287 + redisTemplate.opsForValue().set(key, sendInfo);
  288 + }else {
  289 + redisTemplate.delete(key);
  290 + logger.info("[第三方服务对接->发送流] 视频流发流失败,callId->{}, {}", callId, jsonObject.getString("msg"));
  291 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "[视频流发流失败] " + jsonObject.getString("msg"));
  292 + }
  293 + }
  294 + if(paramForAudio != null) {
  295 + JSONObject jsonObject = zlmServerFactory.startSendRtpStream(mediaServerItem, paramForAudio);
  296 + if (jsonObject.getInteger("code") == 0) {
  297 + logger.info("[第三方服务对接->发送流] 音频流发流成功,callId->{},param->{}", callId, paramForAudio);
  298 + redisTemplate.opsForValue().set(key, sendInfo);
  299 + }else {
  300 + redisTemplate.delete(key);
  301 + logger.info("[第三方服务对接->发送流] 音频流发流失败,callId->{}, {}", callId, jsonObject.getString("msg"));
  302 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "[音频流发流失败] " + jsonObject.getString("msg"));
  303 + }
  304 + }
  305 + }else {
  306 + logger.info("[第三方服务对接->发送流] 流不存在,等待流上线,callId->{}", callId);
  307 + String uuid = UUID.randomUUID().toString();
  308 + HookSubscribeForStreamChange hookSubscribeForStreamChange = HookSubscribeFactory.on_stream_changed(app, stream, true, "rtsp", mediaServerItem.getId());
  309 + dynamicTask.startDelay(uuid, ()->{
  310 + logger.info("[第三方服务对接->发送流] 等待流上线超时 callId->{}", callId);
  311 + redisTemplate.delete(key);
  312 + hookSubscribe.removeSubscribe(hookSubscribeForStreamChange);
  313 + }, 10000);
227 314
  315 + // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
  316 + OtherRtpSendInfo finalSendInfo = sendInfo;
  317 + hookSubscribe.removeSubscribe(hookSubscribeForStreamChange);
  318 + hookSubscribe.addSubscribe(hookSubscribeForStreamChange,
  319 + (mediaServerItemInUse, response)->{
  320 + dynamicTask.stop(uuid);
  321 + logger.info("[第三方服务对接->发送流] 流上线,开始发流 callId->{}", callId);
  322 + try {
  323 + Thread.sleep(400);
  324 + } catch (InterruptedException e) {
  325 + throw new RuntimeException(e);
  326 + }
  327 + if (paramForVideo != null) {
  328 + JSONObject jsonObject = zlmServerFactory.startSendRtpStream(mediaServerItem, paramForVideo);
  329 + if (jsonObject.getInteger("code") == 0) {
  330 + logger.info("[第三方服务对接->发送流] 视频流发流成功,callId->{},param->{}", callId, paramForVideo);
  331 + redisTemplate.opsForValue().set(key, finalSendInfo);
  332 + }else {
  333 + redisTemplate.delete(key);
  334 + logger.info("[第三方服务对接->发送流] 视频流发流失败,callId->{}, {}", callId, jsonObject.getString("msg"));
  335 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "[视频流发流失败] " + jsonObject.getString("msg"));
  336 + }
  337 + }
  338 + if(paramForAudio != null) {
  339 + JSONObject jsonObject = zlmServerFactory.startSendRtpStream(mediaServerItem, paramForAudio);
  340 + if (jsonObject.getInteger("code") == 0) {
  341 + logger.info("[第三方服务对接->发送流] 音频流发流成功,callId->{},param->{}", callId, paramForAudio);
  342 + redisTemplate.opsForValue().set(key, finalSendInfo);
  343 + }else {
  344 + redisTemplate.delete(key);
  345 + logger.info("[第三方服务对接->发送流] 音频流发流失败,callId->{}, {}", callId, jsonObject.getString("msg"));
  346 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "[音频流发流失败] " + jsonObject.getString("msg"));
  347 + }
  348 + }
  349 + hookSubscribe.removeSubscribe(hookSubscribeForStreamChange);
  350 + });
  351 + }
  352 + }
228 353
229 @GetMapping(value = "/send/stop") 354 @GetMapping(value = "/send/stop")
230 @ResponseBody 355 @ResponseBody
@@ -232,7 +357,7 @@ public class RtpController { @@ -232,7 +357,7 @@ public class RtpController {
232 @Parameter(name = "callId", description = "整个过程的唯一标识,不传则使用随机端口发流", required = true) 357 @Parameter(name = "callId", description = "整个过程的唯一标识,不传则使用随机端口发流", required = true)
233 public void closeSendRTP(String callId) { 358 public void closeSendRTP(String callId) {
234 logger.info("[第三方服务对接->关闭发送流] callId->{}", callId); 359 logger.info("[第三方服务对接->关闭发送流] callId->{}", callId);
235 - String key = VideoManagerConstants.WVP_OTHER_SEND_RTP_INFO + userSetting.getServerId() + callId; 360 + String key = VideoManagerConstants.WVP_OTHER_SEND_RTP_INFO + userSetting.getServerId() + "_" + callId;
236 OtherRtpSendInfo sendInfo = (OtherRtpSendInfo)redisTemplate.opsForValue().get(key); 361 OtherRtpSendInfo sendInfo = (OtherRtpSendInfo)redisTemplate.opsForValue().get(key);
237 if (sendInfo == null){ 362 if (sendInfo == null){
238 throw new ControllerException(ErrorCode.ERROR100.getCode(), "未开启发流"); 363 throw new ControllerException(ErrorCode.ERROR100.getCode(), "未开启发流");
@@ -250,6 +375,7 @@ public class RtpController { @@ -250,6 +375,7 @@ public class RtpController {
250 }else { 375 }else {
251 logger.info("[第三方服务对接->关闭发送流] 成功 callId->{}", callId); 376 logger.info("[第三方服务对接->关闭发送流] 成功 callId->{}", callId);
252 } 377 }
  378 + redisTemplate.delete(key);
253 } 379 }
254 380
255 } 381 }
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java
@@ -114,17 +114,8 @@ public class ApiDeviceController { @@ -114,17 +114,8 @@ public class ApiDeviceController {
114 @RequestParam(required = false)String q, 114 @RequestParam(required = false)String q,
115 @RequestParam(required = false)Boolean online ){ 115 @RequestParam(required = false)Boolean online ){
116 116
117 -// if (logger.isDebugEnabled()) {  
118 -// logger.debug("查询所有视频设备API调用");  
119 -// } 117 +
120 JSONObject result = new JSONObject(); 118 JSONObject result = new JSONObject();
121 - // 查询设备是否存在  
122 -// Device device = storager.queryVideoDevice(serial);  
123 -// if (device == null) {  
124 -// result.put("ChannelCount", 0);  
125 -// result.put("ChannelList", "[]");  
126 -// return result;  
127 -// }  
128 List<DeviceChannelExtend> deviceChannels; 119 List<DeviceChannelExtend> deviceChannels;
129 List<String> channelIds = null; 120 List<String> channelIds = null;
130 if (!ObjectUtils.isEmpty(code)) { 121 if (!ObjectUtils.isEmpty(code)) {
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java
@@ -122,7 +122,7 @@ public class ApiStreamController { @@ -122,7 +122,7 @@ public class ApiStreamController {
122 MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); 122 MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
123 123
124 124
125 - playService.play(newMediaServerItem, serial, code, (errorCode, msg, data) -> { 125 + playService.play(newMediaServerItem, serial, code, null, (errorCode, msg, data) -> {
126 if (errorCode == InviteErrorCode.SUCCESS.getCode()) { 126 if (errorCode == InviteErrorCode.SUCCESS.getCode()) {
127 InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, serial, code); 127 InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, serial, code);
128 if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { 128 if (inviteInfo != null && inviteInfo.getStreamInfo() != null) {
src/main/resources/all-application.yml
@@ -145,6 +145,8 @@ media: @@ -145,6 +145,8 @@ media:
145 enable: true 145 enable: true
146 # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 146 # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功
147 port-range: 30000,30500 # 端口范围 147 port-range: 30000,30500 # 端口范围
  148 + # [可选] 国标级联在此范围内选择端口发送媒体流
  149 + send-port-range: 30000,30500 # 端口范围
148 # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 150 # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用
149 record-assist-port: 0 151 record-assist-port: 0
150 152
src/main/resources/wvpssl.jks deleted 100644 → 0
No preview for this file type