Merge remote-tracking branch 'origin/wvp-28181-2.0' into commercial
# Conflicts: # sql/update.sql # src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.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/service/IPlayService.java # src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java # web_src/src/components/dialog/deviceEdit.vue # web_src/src/components/dialog/devicePlayer.vue
This commit is contained in:
@@ -97,4 +97,5 @@ public class VideoManagerConstants {
|
||||
//************************** 第三方 ****************************************
|
||||
public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_";
|
||||
public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_";
|
||||
|
||||
}
|
||||
|
||||
@@ -103,12 +103,12 @@ public class DynamicTask {
|
||||
|
||||
public void stop(String key) {
|
||||
if (futureMap.get(key) != null && !futureMap.get(key).isCancelled()) {
|
||||
futureMap.get(key).cancel(true);
|
||||
Runnable runnable = runnableMap.get(key);
|
||||
if (runnable instanceof ISubscribeTask) {
|
||||
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
|
||||
subscribeTask.stop();
|
||||
}
|
||||
// Runnable runnable = runnableMap.get(key);
|
||||
// if (runnable instanceof ISubscribeTask) {
|
||||
// ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
|
||||
// subscribeTask.stop();
|
||||
// }
|
||||
futureMap.get(key).cancel(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
@Configuration("mediaConfig")
|
||||
public class MediaConfig{
|
||||
@@ -161,7 +165,18 @@ public class MediaConfig{
|
||||
if (StringUtils.isEmpty(sdpIp)){
|
||||
return ip;
|
||||
}else {
|
||||
return sdpIp;
|
||||
if (isValidIPAddress(sdpIp)) {
|
||||
return sdpIp;
|
||||
}else {
|
||||
// 按照域名解析
|
||||
String hostAddress = null;
|
||||
try {
|
||||
hostAddress = InetAddress.getByName(sdpIp).getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return hostAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,4 +226,11 @@ public class MediaConfig{
|
||||
return mediaServerItem;
|
||||
}
|
||||
|
||||
private boolean isValidIPAddress(String ipAddress) {
|
||||
if ((ipAddress != null) && (!ipAddress.isEmpty())) {
|
||||
return Pattern.matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", ipAddress);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
import com.genersoft.iot.vmp.service.impl.RedisAlarmMsgListener;
|
||||
import com.genersoft.iot.vmp.service.impl.RedisGPSMsgListener;
|
||||
import com.genersoft.iot.vmp.service.impl.RedisGpsMsgListener;
|
||||
import com.genersoft.iot.vmp.service.impl.RedisGbPlayMsgListener;
|
||||
import com.genersoft.iot.vmp.service.impl.RedisStreamMsgListener;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -47,11 +49,17 @@ public class RedisConfig extends CachingConfigurerSupport {
|
||||
private int poolMaxWait;
|
||||
|
||||
@Autowired
|
||||
private RedisGPSMsgListener redisGPSMsgListener;
|
||||
private RedisGpsMsgListener redisGPSMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisAlarmMsgListener redisAlarmMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisStreamMsgListener redisStreamMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisGbPlayMsgListener redisGbPlayMsgListener;
|
||||
|
||||
@Bean
|
||||
public JedisPool jedisPool() {
|
||||
if (StringUtils.isBlank(password)) {
|
||||
@@ -98,6 +106,8 @@ public class RedisConfig extends CachingConfigurerSupport {
|
||||
container.setConnectionFactory(connectionFactory);
|
||||
container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS));
|
||||
container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE));
|
||||
container.addMessageListener(redisStreamMsgListener, new PatternTopic(VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + "PUSH"));
|
||||
container.addMessageListener(redisGbPlayMsgListener, new PatternTopic(RedisGbPlayMsgListener.WVP_PUSH_STREAM_KEY));
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* 配置Spring Security
|
||||
* @author lin
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@@ -132,15 +133,19 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
.anyRequest().authenticated()
|
||||
// 异常处理(权限拒绝、登录失效等)
|
||||
.and().exceptionHandling()
|
||||
.authenticationEntryPoint(anonymousAuthenticationEntryPoint)//匿名用户访问无权限资源时的异常处理
|
||||
//匿名用户访问无权限资源时的异常处理
|
||||
.authenticationEntryPoint(anonymousAuthenticationEntryPoint)
|
||||
// .accessDeniedHandler(accessDeniedHandler)//登录用户没有权限访问资源
|
||||
// 登入
|
||||
.and().formLogin().permitAll()//允许所有用户
|
||||
.successHandler(loginSuccessHandler)//登录成功处理逻辑
|
||||
.failureHandler(loginFailureHandler)//登录失败处理逻辑
|
||||
// 登入 允许所有用户
|
||||
.and().formLogin().permitAll()
|
||||
//登录成功处理逻辑
|
||||
.successHandler(loginSuccessHandler)
|
||||
//登录失败处理逻辑
|
||||
.failureHandler(loginFailureHandler)
|
||||
// 登出
|
||||
.and().logout().logoutUrl("/api/user/logout").permitAll()//允许所有用户
|
||||
.logoutSuccessHandler(logoutHandler)//登出成功处理逻辑
|
||||
.and().logout().logoutUrl("/api/user/logout").permitAll()
|
||||
//登出成功处理逻辑
|
||||
.logoutSuccessHandler(logoutHandler)
|
||||
.deleteCookies("JSESSIONID")
|
||||
// 会话管理
|
||||
// .and().sessionManagement().invalidSessionStrategy(invalidSessionHandler) // 超时处理
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
|
||||
/**
|
||||
* 国标设备/平台
|
||||
* @author lin
|
||||
*/
|
||||
public class Device {
|
||||
|
||||
/**
|
||||
@@ -127,7 +131,12 @@ public class Device {
|
||||
/**
|
||||
* 是否开启ssrc校验,默认关闭,开启可以防止串流
|
||||
*/
|
||||
private boolean ssrcCheck;
|
||||
private boolean ssrcCheck = true;
|
||||
|
||||
/**
|
||||
* 地理坐标系, 目前支持 WGS84,GCJ02 TODO CGCS2000
|
||||
*/
|
||||
private String geoCoordSys;
|
||||
|
||||
|
||||
public String getDeviceId() {
|
||||
@@ -322,4 +331,12 @@ public class Device {
|
||||
this.ssrcCheck = ssrcCheck;
|
||||
}
|
||||
|
||||
public String getGeoCoordSys() {
|
||||
return geoCoordSys;
|
||||
}
|
||||
|
||||
public void setGeoCoordSys(String geoCoordSys) {
|
||||
this.geoCoordSys = geoCoordSys;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -154,6 +154,26 @@ public class DeviceChannel {
|
||||
*/
|
||||
private double latitude;
|
||||
|
||||
/**
|
||||
* 经度 GCJ02
|
||||
*/
|
||||
private double longitudeGcj02;
|
||||
|
||||
/**
|
||||
* 纬度 GCJ02
|
||||
*/
|
||||
private double latitudeGcj02;
|
||||
|
||||
/**
|
||||
* 经度 WGS84
|
||||
*/
|
||||
private double longitudeWgs84;
|
||||
|
||||
/**
|
||||
* 纬度 WGS84
|
||||
*/
|
||||
private double latitudeWgs84;
|
||||
|
||||
/**
|
||||
* 子设备数
|
||||
*/
|
||||
@@ -407,6 +427,38 @@ public class DeviceChannel {
|
||||
this.latitude = latitude;
|
||||
}
|
||||
|
||||
public double getLongitudeGcj02() {
|
||||
return longitudeGcj02;
|
||||
}
|
||||
|
||||
public void setLongitudeGcj02(double longitudeGcj02) {
|
||||
this.longitudeGcj02 = longitudeGcj02;
|
||||
}
|
||||
|
||||
public double getLatitudeGcj02() {
|
||||
return latitudeGcj02;
|
||||
}
|
||||
|
||||
public void setLatitudeGcj02(double latitudeGcj02) {
|
||||
this.latitudeGcj02 = latitudeGcj02;
|
||||
}
|
||||
|
||||
public double getLongitudeWgs84() {
|
||||
return longitudeWgs84;
|
||||
}
|
||||
|
||||
public void setLongitudeWgs84(double longitudeWgs84) {
|
||||
this.longitudeWgs84 = longitudeWgs84;
|
||||
}
|
||||
|
||||
public double getLatitudeWgs84() {
|
||||
return latitudeWgs84;
|
||||
}
|
||||
|
||||
public void setLatitudeWgs84(double latitudeWgs84) {
|
||||
this.latitudeWgs84 = latitudeWgs84;
|
||||
}
|
||||
|
||||
public int getSubCount() {
|
||||
return subCount;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import org.dom4j.Element;
|
||||
|
||||
import javax.sip.RequestEvent;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
public class HandlerCatchData {
|
||||
private RequestEvent evt;
|
||||
private Device device;
|
||||
private Element rootElement;
|
||||
|
||||
public HandlerCatchData(RequestEvent evt, Device device, Element rootElement) {
|
||||
this.evt = evt;
|
||||
this.device = device;
|
||||
this.rootElement = rootElement;
|
||||
}
|
||||
|
||||
public RequestEvent getEvt() {
|
||||
return evt;
|
||||
}
|
||||
|
||||
public void setEvt(RequestEvent evt) {
|
||||
this.evt = evt;
|
||||
}
|
||||
|
||||
public Device getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
public void setDevice(Device device) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
public Element getRootElement() {
|
||||
return rootElement;
|
||||
}
|
||||
|
||||
public void setRootElement(Element rootElement) {
|
||||
this.rootElement = rootElement;
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,11 @@ public class SendRtpItem {
|
||||
*/
|
||||
private String mediaServerId;
|
||||
|
||||
/**
|
||||
* 使用的服务的ID
|
||||
*/
|
||||
private String serverId;
|
||||
|
||||
/**
|
||||
* invite的callId
|
||||
*/
|
||||
@@ -259,4 +264,12 @@ public class SendRtpItem {
|
||||
public void setOnlyAudio(boolean onlyAudio) {
|
||||
this.onlyAudio = onlyAudio;
|
||||
}
|
||||
|
||||
public String getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public void setServerId(String serverId) {
|
||||
this.serverId = serverId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
import com.genersoft.iot.vmp.conf.DynamicTask;
|
||||
import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
|
||||
import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeHandlerTask;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
@@ -38,7 +39,6 @@ public class SubscribeHolder {
|
||||
catalogMap.put(platformId, subscribeInfo);
|
||||
// 添加订阅到期
|
||||
String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId;
|
||||
dynamicTask.stop(taskOverdueKey);
|
||||
// 添加任务处理订阅过期
|
||||
dynamicTask.startDelay(taskOverdueKey, () -> removeCatalogSubscribe(subscribeInfo.getId()),
|
||||
subscribeInfo.getExpires() * 1000);
|
||||
@@ -49,10 +49,17 @@ public class SubscribeHolder {
|
||||
}
|
||||
|
||||
public void removeCatalogSubscribe(String platformId) {
|
||||
|
||||
catalogMap.remove(platformId);
|
||||
String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId;
|
||||
Runnable runnable = dynamicTask.get(taskOverdueKey);
|
||||
if (runnable instanceof ISubscribeTask) {
|
||||
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
|
||||
subscribeTask.stop();
|
||||
}
|
||||
// 添加任务处理订阅过期
|
||||
dynamicTask.stop(taskOverdueKey);
|
||||
|
||||
}
|
||||
|
||||
public void putMobilePositionSubscribe(String platformId, SubscribeInfo subscribeInfo) {
|
||||
@@ -63,7 +70,6 @@ public class SubscribeHolder {
|
||||
storager, platformId, subscribeInfo.getSn(), key, this, dynamicTask),
|
||||
subscribeInfo.getGpsInterval() * 1000);
|
||||
String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId;
|
||||
dynamicTask.stop(taskOverdueKey);
|
||||
// 添加任务处理订阅过期
|
||||
dynamicTask.startDelay(taskOverdueKey, () -> {
|
||||
removeMobilePositionSubscribe(subscribeInfo.getId());
|
||||
@@ -81,6 +87,11 @@ public class SubscribeHolder {
|
||||
// 结束任务处理GPS定时推送
|
||||
dynamicTask.stop(key);
|
||||
String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId;
|
||||
Runnable runnable = dynamicTask.get(taskOverdueKey);
|
||||
if (runnable instanceof ISubscribeTask) {
|
||||
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
|
||||
subscribeTask.stop();
|
||||
}
|
||||
// 添加任务处理订阅过期
|
||||
dynamicTask.stop(taskOverdueKey);
|
||||
}
|
||||
|
||||
@@ -88,8 +88,8 @@ public class SipSubscribe {
|
||||
this.type = "timeout";
|
||||
this.msg = "消息超时未回复";
|
||||
this.statusCode = -1024;
|
||||
this.callId = timeoutEvent.getClientTransaction().getDialog().getCallId().getCallId();
|
||||
this.dialog = timeoutEvent.getClientTransaction().getDialog();
|
||||
this.callId = this.dialog != null?timeoutEvent.getClientTransaction().getDialog().getCallId().getCallId(): null;
|
||||
}else if (event instanceof TransactionTerminatedEvent) {
|
||||
TransactionTerminatedEvent transactionTerminatedEvent = (TransactionTerminatedEvent)event;
|
||||
this.type = "transactionTerminated";
|
||||
@@ -109,8 +109,8 @@ public class SipSubscribe {
|
||||
this.type = "deviceNotFoundEvent";
|
||||
this.msg = "设备未找到";
|
||||
this.statusCode = -1024;
|
||||
this.callId = deviceNotFoundEvent.getDialog().getCallId().getCallId();
|
||||
this.dialog = deviceNotFoundEvent.getDialog();
|
||||
this.callId = this.dialog != null ?deviceNotFoundEvent.getDialog().getCallId().getCallId() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,6 +130,9 @@ public class SipSubscribe {
|
||||
}
|
||||
|
||||
public void removeErrorSubscribe(String key) {
|
||||
if(key == null){
|
||||
return;
|
||||
}
|
||||
errorSubscribes.remove(key);
|
||||
errorTimeSubscribes.remove(key);
|
||||
}
|
||||
@@ -139,6 +142,9 @@ public class SipSubscribe {
|
||||
}
|
||||
|
||||
public void removeOkSubscribe(String key) {
|
||||
if(key == null){
|
||||
return;
|
||||
}
|
||||
okSubscribes.remove(key);
|
||||
okTimeSubscribes.remove(key);
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
|
||||
subscribe = subscribeHolder.getCatalogSubscribe(event.getPlatformId());
|
||||
|
||||
if (subscribe == null) {
|
||||
logger.info("发送订阅消息时发现订阅信息已经不存在");
|
||||
logger.info("发送订阅消息时发现订阅信息已经不存在: {}", event.getPlatformId());
|
||||
return;
|
||||
}
|
||||
}else {
|
||||
|
||||
@@ -99,8 +99,8 @@ public class VideoStreamSessionManager {
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public SIPDialog getDialogByCallId(String deviceId, String channelId, String callID){
|
||||
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callID, null);
|
||||
public SIPDialog getDialogByCallId(String deviceId, String channelId, String callId){
|
||||
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, null);
|
||||
if (ssrcTransaction == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -108,11 +108,17 @@ public class VideoStreamSessionManager {
|
||||
if (dialogByteArray == null) {
|
||||
return null;
|
||||
}
|
||||
SIPDialog dialog = (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);
|
||||
return dialog;
|
||||
return (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);
|
||||
}
|
||||
|
||||
public SsrcTransaction getSsrcTransaction(String deviceId, String channelId, String callId, String stream){
|
||||
|
||||
if (StringUtils.isEmpty(deviceId)) {
|
||||
deviceId ="*";
|
||||
}
|
||||
if (StringUtils.isEmpty(channelId)) {
|
||||
channelId ="*";
|
||||
}
|
||||
if (StringUtils.isEmpty(callId)) {
|
||||
callId ="*";
|
||||
}
|
||||
@@ -179,7 +185,7 @@ public class VideoStreamSessionManager {
|
||||
|
||||
|
||||
public List<SsrcTransaction> getAllSsrc() {
|
||||
List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetting.getServerId() + "_" ));
|
||||
List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetting.getServerId()));
|
||||
List<SsrcTransaction> result= new ArrayList<>();
|
||||
for (int i = 0; i < ssrcTransactionKeys.size(); i++) {
|
||||
String key = (String)ssrcTransactionKeys.get(i);
|
||||
|
||||
@@ -71,7 +71,9 @@ public class MobilePositionSubscribeHandlerTask implements ISubscribeTask {
|
||||
String gbId = gbStream.getGbId();
|
||||
GPSMsgInfo gpsMsgInfo = redisCatchStorage.getGpsMsgInfo(gbId);
|
||||
if (gpsMsgInfo != null) { // 无最新位置不发送
|
||||
logger.info("无最新位置不发送");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("无最新位置不发送");
|
||||
}
|
||||
// 经纬度都为0不发送
|
||||
if (gpsMsgInfo.getLng() == 0 && gpsMsgInfo.getLat() == 0) {
|
||||
continue;
|
||||
|
||||
@@ -150,30 +150,24 @@ public class SIPProcessorObserver implements ISIPProcessorObserver {
|
||||
public void processTimeout(TimeoutEvent timeoutEvent) {
|
||||
logger.info("[消息发送超时]");
|
||||
ClientTransaction clientTransaction = timeoutEvent.getClientTransaction();
|
||||
eventPublisher.requestTimeOut(timeoutEvent);
|
||||
|
||||
if (clientTransaction != null) {
|
||||
logger.info("[发送错误订阅] clientTransaction != null");
|
||||
Request request = clientTransaction.getRequest();
|
||||
if (request != null) {
|
||||
logger.info("[发送错误订阅] request != null");
|
||||
CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
|
||||
if (callIdHeader != null) {
|
||||
logger.info("[发送错误订阅]");
|
||||
SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId());
|
||||
SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(timeoutEvent);
|
||||
subscribe.response(eventResult);
|
||||
sipSubscribe.removeOkSubscribe(callIdHeader.getCallId());
|
||||
sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Timeout timeout = timeoutEvent.getTimeout();
|
||||
// ServerTransaction serverTransaction = timeoutEvent.getServerTransaction();
|
||||
// if (serverTransaction != null) {
|
||||
// Request request = serverTransaction.getRequest();
|
||||
// URI requestURI = request.getRequestURI();
|
||||
// Header header = request.getHeader(FromHeader.NAME);
|
||||
// }
|
||||
// if(timeoutProcessor != null) {
|
||||
// timeoutProcessor.process(timeoutEvent);
|
||||
// }
|
||||
eventPublisher.requestTimeOut(timeoutEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -148,6 +148,14 @@ public interface ISIPCommander {
|
||||
* 回放倍速播放
|
||||
*/
|
||||
void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed);
|
||||
|
||||
/**
|
||||
* 回放控制
|
||||
* @param device
|
||||
* @param streamInfo
|
||||
* @param content
|
||||
*/
|
||||
void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -103,6 +103,14 @@ public interface ISIPCommanderForPlatform {
|
||||
*/
|
||||
boolean recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo);
|
||||
|
||||
/**
|
||||
* 录像播放推送完成时发送MediaStatus消息
|
||||
* @param platform
|
||||
* @param sendRtpItem
|
||||
* @return
|
||||
*/
|
||||
boolean sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem);
|
||||
|
||||
/**
|
||||
* 向发起点播的上级回复bye
|
||||
* @param platform 平台信息
|
||||
|
||||
@@ -32,7 +32,9 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.sip.*;
|
||||
import javax.sip.address.Address;
|
||||
import javax.sip.address.SipURI;
|
||||
import javax.sip.address.URI;
|
||||
import javax.sip.header.*;
|
||||
import javax.sip.message.Request;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -708,22 +710,19 @@ public class SIPCommander implements ISIPCommander {
|
||||
}
|
||||
SIPDialog dialog;
|
||||
if (callId != null) {
|
||||
dialog = streamSession.getDialogByCallId(deviceId, channelId, callId);
|
||||
dialog = streamSession.getDialogByCallId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), callId);
|
||||
}else {
|
||||
if (stream == null) {
|
||||
if (stream == null && ssrcTransaction == null && ssrcTransaction.getStream() == null) {
|
||||
return;
|
||||
}
|
||||
dialog = streamSession.getDialogByStream(deviceId, channelId, stream);
|
||||
}
|
||||
if (ssrcTransaction != null) {
|
||||
MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId());
|
||||
mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc());
|
||||
mediaServerService.closeRTPServer(deviceId, channelId, ssrcTransaction.getStream());
|
||||
streamSession.remove(deviceId, channelId, ssrcTransaction.getStream());
|
||||
dialog = streamSession.getDialogByStream(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
|
||||
}
|
||||
mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
|
||||
mediaServerService.closeRTPServer(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
|
||||
streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
|
||||
|
||||
if (dialog == null) {
|
||||
logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", deviceId, channelId);
|
||||
logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
|
||||
return;
|
||||
}
|
||||
SipStack sipStack = udpSipProvider.getSipStack();
|
||||
@@ -1456,12 +1455,20 @@ public class SIPCommander implements ISIPCommander {
|
||||
|
||||
Request request;
|
||||
if (dialog != null) {
|
||||
logger.info("发送移动位置订阅消息时 dialog的状态为: {}", dialog.getState());
|
||||
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
|
||||
request = dialog.createRequest(Request.SUBSCRIBE);
|
||||
ExpiresHeader expiresHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForCatalog());
|
||||
request.setExpires(expiresHeader);
|
||||
|
||||
request.setRequestURI(requestURI);
|
||||
|
||||
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
|
||||
request.setContent(subscribePostitionXml.toString(), contentTypeHeader);
|
||||
ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForMobilePosition());
|
||||
request.addHeader(expireHeader);
|
||||
|
||||
CSeqHeader cSeqHeader = (CSeqHeader)request.getHeader(CSeqHeader.NAME);
|
||||
cSeqHeader.setSeqNumber(redisCatchStorage.getCSEQ(Request.SUBSCRIBE));
|
||||
request.removeHeader(CSeqHeader.NAME);
|
||||
request.addHeader(cSeqHeader);
|
||||
}else {
|
||||
String tm = Long.toString(System.currentTimeMillis());
|
||||
CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
|
||||
@@ -1552,12 +1559,21 @@ public class SIPCommander implements ISIPCommander {
|
||||
|
||||
Request request;
|
||||
if (dialog != null) {
|
||||
logger.info("发送目录订阅消息时 dialog的状态为: {}", dialog.getState());
|
||||
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
|
||||
request = dialog.createRequest(Request.SUBSCRIBE);
|
||||
ExpiresHeader expiresHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForCatalog());
|
||||
request.setExpires(expiresHeader);
|
||||
|
||||
request.setRequestURI(requestURI);
|
||||
|
||||
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
|
||||
request.setContent(cmdXml.toString(), contentTypeHeader);
|
||||
ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForMobilePosition());
|
||||
request.addHeader(expireHeader);
|
||||
|
||||
CSeqHeader cSeqHeader = (CSeqHeader)request.getHeader(CSeqHeader.NAME);
|
||||
cSeqHeader.setSeqNumber(redisCatchStorage.getCSEQ(Request.SUBSCRIBE));
|
||||
request.removeHeader(CSeqHeader.NAME);
|
||||
request.addHeader(cSeqHeader);
|
||||
|
||||
}else {
|
||||
String tm = Long.toString(System.currentTimeMillis());
|
||||
|
||||
@@ -1779,6 +1795,43 @@ public class SIPCommander implements ISIPCommander {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) {
|
||||
try {
|
||||
Request request = headerProvider.createInfoRequest(device, streamInfo, content);
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
logger.info(request.toString());
|
||||
ClientTransaction clientTransaction = null;
|
||||
if ("TCP".equals(device.getTransport())) {
|
||||
clientTransaction = tcpSipProvider.getNewClientTransaction(request);
|
||||
} else if ("UDP".equals(device.getTransport())) {
|
||||
clientTransaction = udpSipProvider.getNewClientTransaction(request);
|
||||
}
|
||||
CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);
|
||||
if(errorEvent != null) {
|
||||
sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
|
||||
errorEvent.response(eventResult);
|
||||
sipSubscribe.removeErrorSubscribe(eventResult.callId);
|
||||
sipSubscribe.removeOkSubscribe(eventResult.callId);
|
||||
}));
|
||||
}
|
||||
|
||||
if(okEvent != null) {
|
||||
sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
|
||||
okEvent.response(eventResult);
|
||||
sipSubscribe.removeOkSubscribe(eventResult.callId);
|
||||
sipSubscribe.removeErrorSubscribe(eventResult.callId);
|
||||
});
|
||||
}
|
||||
clientTransaction.sendRequest();
|
||||
|
||||
} catch (SipException | ParseException | InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import javax.sip.address.SipURI;
|
||||
import javax.sip.header.*;
|
||||
import javax.sip.message.Request;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetAddress;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@@ -276,8 +277,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
catalogXml.append("<Owner>" + channel.getOwner() + "</Owner>\r\n");
|
||||
catalogXml.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n");
|
||||
catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n");
|
||||
catalogXml.append("<Longitude>" + channel.getLongitude() + "</Longitude>\r\n");
|
||||
catalogXml.append("<Latitude>" + channel.getLatitude() + "</Latitude>\r\n");
|
||||
catalogXml.append("<Longitude>" + channel.getLongitudeWgs84() + "</Longitude>\r\n");
|
||||
catalogXml.append("<Latitude>" + channel.getLatitudeWgs84() + "</Latitude>\r\n");
|
||||
catalogXml.append("<IPAddress>" + channel.getIpAddress() + "</IPAddress>\r\n");
|
||||
catalogXml.append("<Port>" + channel.getPort() + "</Port>\r\n");
|
||||
catalogXml.append("<Info>\r\n");
|
||||
@@ -546,14 +547,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
}
|
||||
notifyRequest.addHeader(event);
|
||||
SipURI sipURI = (SipURI) notifyRequest.getRequestURI();
|
||||
if (subscribeInfo.getTransaction() != null) {
|
||||
SIPRequest request = (SIPRequest) subscribeInfo.getTransaction().getRequest();
|
||||
sipURI.setHost(request.getRemoteAddress().getHostAddress());
|
||||
sipURI.setPort(request.getRemotePort());
|
||||
}else {
|
||||
sipURI.setHost(parentPlatform.getServerIP());
|
||||
sipURI.setPort(parentPlatform.getServerPort());
|
||||
}
|
||||
sipURI.setHost(parentPlatform.getServerIP());
|
||||
sipURI.setPort(parentPlatform.getServerPort());
|
||||
|
||||
ClientTransaction transaction = null;
|
||||
if ("TCP".equals(parentPlatform.getTransport())) {
|
||||
@@ -750,6 +745,82 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem) {
|
||||
if (sendRtpItem == null) {
|
||||
return false;
|
||||
}
|
||||
if (platform == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] dialogByteArray = sendRtpItem.getDialog();
|
||||
if (dialogByteArray == null) {
|
||||
return false;
|
||||
}
|
||||
try{
|
||||
SIPDialog dialog = (SIPDialog) SerializeUtils.deSerialize(dialogByteArray);
|
||||
SipStack sipStack;
|
||||
if ("TCP".equals(platform.getTransport())) {
|
||||
sipStack = tcpSipProvider.getSipStack();
|
||||
} else {
|
||||
sipStack = udpSipProvider.getSipStack();
|
||||
}
|
||||
SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);
|
||||
if (dialog != sipDialog) {
|
||||
dialog = sipDialog;
|
||||
}
|
||||
if ("TCP".equals(platform.getTransport())) {
|
||||
dialog.setSipProvider(tcpSipProvider);
|
||||
} else {
|
||||
dialog.setSipProvider(udpSipProvider);
|
||||
}
|
||||
|
||||
Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
|
||||
sipStackField.setAccessible(true);
|
||||
sipStackField.set(dialog, sipStack);
|
||||
Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
|
||||
eventListenersField.setAccessible(true);
|
||||
eventListenersField.set(dialog, new HashSet<>());
|
||||
|
||||
SIPRequest messageRequest = (SIPRequest)dialog.createRequest(Request.MESSAGE);
|
||||
String characterSet = platform.getCharacterSet();
|
||||
StringBuffer mediaStatusXml = new StringBuffer(200);
|
||||
mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
|
||||
mediaStatusXml.append("<Notify>\r\n");
|
||||
mediaStatusXml.append("<CmdType>MediaStatus</CmdType>\r\n");
|
||||
mediaStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
|
||||
mediaStatusXml.append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n");
|
||||
mediaStatusXml.append("<NotifyType>121</NotifyType>\r\n");
|
||||
mediaStatusXml.append("</Notify>\r\n");
|
||||
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
|
||||
messageRequest.setContent(mediaStatusXml.toString(), contentTypeHeader);
|
||||
SipURI sipURI = (SipURI) messageRequest.getRequestURI();
|
||||
sipURI.setHost(platform.getServerIP());
|
||||
sipURI.setPort(platform.getServerPort());
|
||||
ClientTransaction clientTransaction;
|
||||
if ("TCP".equals(platform.getTransport())) {
|
||||
clientTransaction = tcpSipProvider.getNewClientTransaction(messageRequest);
|
||||
}else {
|
||||
clientTransaction = udpSipProvider.getNewClientTransaction(messageRequest);
|
||||
}
|
||||
dialog.sendRequest(clientTransaction);
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return true;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void streamByeCmd(ParentPlatform platform, String callId) {
|
||||
if (platform == null) {
|
||||
@@ -766,45 +837,51 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
byte[] dialogByteArray = sendRtpItem.getDialog();
|
||||
if (dialogByteArray != null) {
|
||||
SIPDialog dialog = (SIPDialog) SerializeUtils.deSerialize(dialogByteArray);
|
||||
SipStack sipStack = udpSipProvider.getSipStack();
|
||||
SipStack sipStack;
|
||||
if ("TCP".equals(platform.getTransport())) {
|
||||
sipStack = tcpSipProvider.getSipStack();
|
||||
} else {
|
||||
sipStack = udpSipProvider.getSipStack();
|
||||
}
|
||||
SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);
|
||||
if (dialog != sipDialog) {
|
||||
dialog = sipDialog;
|
||||
} else {
|
||||
try {
|
||||
dialog.setSipProvider(udpSipProvider);
|
||||
Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
|
||||
sipStackField.setAccessible(true);
|
||||
sipStackField.set(dialog, sipStack);
|
||||
Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
|
||||
eventListenersField.setAccessible(true);
|
||||
eventListenersField.set(dialog, new HashSet<>());
|
||||
|
||||
byte[] transactionByteArray = sendRtpItem.getTransaction();
|
||||
ClientTransaction clientTransaction = (ClientTransaction) SerializeUtils.deSerialize(transactionByteArray);
|
||||
Request byeRequest = dialog.createRequest(Request.BYE);
|
||||
|
||||
SipURI byeURI = (SipURI) byeRequest.getRequestURI();
|
||||
SIPRequest request = (SIPRequest) clientTransaction.getRequest();
|
||||
byeURI.setHost(request.getRemoteAddress().getHostAddress());
|
||||
byeURI.setPort(request.getRemotePort());
|
||||
if ("TCP".equals(platform.getTransport())) {
|
||||
clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest);
|
||||
} else if ("UDP".equals(platform.getTransport())) {
|
||||
clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);
|
||||
}
|
||||
dialog.sendRequest(clientTransaction);
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
try {
|
||||
if ("TCP".equals(platform.getTransport())) {
|
||||
dialog.setSipProvider(tcpSipProvider);
|
||||
} else {
|
||||
dialog.setSipProvider(udpSipProvider);
|
||||
}
|
||||
Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
|
||||
sipStackField.setAccessible(true);
|
||||
sipStackField.set(dialog, sipStack);
|
||||
Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
|
||||
eventListenersField.setAccessible(true);
|
||||
eventListenersField.set(dialog, new HashSet<>());
|
||||
|
||||
Request byeRequest = dialog.createRequest(Request.BYE);
|
||||
|
||||
SipURI byeURI = (SipURI) byeRequest.getRequestURI();
|
||||
byeURI.setHost(platform.getServerIP());
|
||||
byeURI.setPort(platform.getServerPort());
|
||||
ClientTransaction clientTransaction;
|
||||
if ("TCP".equals(platform.getTransport())) {
|
||||
clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest);
|
||||
} else {
|
||||
clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);
|
||||
}
|
||||
dialog.sendRequest(clientTransaction);
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request;
|
||||
|
||||
import gov.nist.javax.sip.SipProviderImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
/**
|
||||
* @description:处理接收IPCamera发来的SIP协议请求消息
|
||||
* @author: songww
|
||||
* @date: 2020年5月3日 下午4:42:22
|
||||
*/
|
||||
public abstract class SIPRequestProcessorAbstract {
|
||||
|
||||
|
||||
@Autowired
|
||||
@Qualifier(value="tcpSipProvider")
|
||||
private SipProviderImpl tcpSipProvider;
|
||||
|
||||
@Autowired
|
||||
@Qualifier(value="udpSipProvider")
|
||||
private SipProviderImpl udpSipProvider;
|
||||
|
||||
}
|
||||
@@ -15,10 +15,13 @@ import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
|
||||
import com.genersoft.iot.vmp.service.impl.RedisGbPlayMsgListener;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import gov.nist.javax.sip.message.SIPRequest;
|
||||
import gov.nist.javax.sip.stack.SIPDialog;
|
||||
import com.genersoft.iot.vmp.utils.SerializeUtils;
|
||||
import org.ehcache.shadow.org.terracotta.offheapstore.storage.IntegerStorageEngine;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -46,7 +49,7 @@ import java.util.*;
|
||||
public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class);
|
||||
private String method = "ACK";
|
||||
private final String method = "ACK";
|
||||
|
||||
@Autowired
|
||||
private SIPProcessorObserver sipProcessorObserver;
|
||||
@@ -84,6 +87,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
@Autowired
|
||||
private AudioBroadcastManager audioBroadcastManager;
|
||||
|
||||
@Autowired
|
||||
private RedisGbPlayMsgListener redisGbPlayMsgListener;
|
||||
|
||||
|
||||
/**
|
||||
* 处理 ACK请求
|
||||
@@ -159,60 +165,41 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
// 向上级平台
|
||||
commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId());
|
||||
}
|
||||
if (mediaInfo == null) {
|
||||
RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance(
|
||||
sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStreamId(),
|
||||
sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(),
|
||||
sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio());
|
||||
redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, jsonObject->{
|
||||
startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader);
|
||||
});
|
||||
}else {
|
||||
JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
|
||||
startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader);
|
||||
}
|
||||
|
||||
|
||||
// if (streamInfo == null) { // 流还没上来,对方就回复ack
|
||||
// logger.info("监听流以等待流上线1 rtp/{}", sendRtpItem.getStreamId());
|
||||
// // 监听流上线
|
||||
// // 添加订阅
|
||||
// JSONObject subscribeKey = new JSONObject();
|
||||
// subscribeKey.put("app", "rtp");
|
||||
// subscribeKey.put("stream", sendRtpItem.getStreamId());
|
||||
// subscribeKey.put("regist", true);
|
||||
// subscribeKey.put("schema", "rtmp");
|
||||
// subscribeKey.put("mediaServerId", sendRtpItem.getMediaServerId());
|
||||
// subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
|
||||
// (MediaServerItem mediaServerItemInUse, JSONObject json)->{
|
||||
// Map<String, Object> param = new HashMap<>();
|
||||
// param.put("vhost","__defaultVhost__");
|
||||
// param.put("app",json.getString("app"));
|
||||
// param.put("stream",json.getString("stream"));
|
||||
// param.put("ssrc", sendRtpItem.getSsrc());
|
||||
// param.put("dst_url",sendRtpItem.getIp());
|
||||
// param.put("dst_port", sendRtpItem.getPort());
|
||||
// param.put("is_udp", is_Udp);
|
||||
// param.put("src_port", sendRtpItem.getLocalPort());
|
||||
// zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
|
||||
// });
|
||||
// }else {
|
||||
// Map<String, Object> param = new HashMap<>();
|
||||
// param.put("vhost","__defaultVhost__");
|
||||
// param.put("app",streamInfo.getApp());
|
||||
// param.put("stream",streamInfo.getStream());
|
||||
// param.put("ssrc", sendRtpItem.getSsrc());
|
||||
// param.put("dst_url",sendRtpItem.getIp());
|
||||
// param.put("dst_port", sendRtpItem.getPort());
|
||||
// param.put("is_udp", is_Udp);
|
||||
// param.put("src_port", sendRtpItem.getLocalPort());
|
||||
//
|
||||
// JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
|
||||
// if (jsonObject.getInteger("code") != 0) {
|
||||
// logger.info("监听流以等待流上线2 {}/{}", streamInfo.getApp(), streamInfo.getStream());
|
||||
// // 监听流上线
|
||||
// // 添加订阅
|
||||
// JSONObject subscribeKey = new JSONObject();
|
||||
// subscribeKey.put("app", "rtp");
|
||||
// subscribeKey.put("stream", streamInfo.getStream());
|
||||
// subscribeKey.put("regist", true);
|
||||
// subscribeKey.put("schema", "rtmp");
|
||||
// subscribeKey.put("mediaServerId", sendRtpItem.getMediaServerId());
|
||||
// subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
|
||||
// (MediaServerItem mediaServerItemInUse, JSONObject json)->{
|
||||
// zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform,
|
||||
JSONObject jsonObject, Map<String, Object> param, CallIdHeader callIdHeader) {
|
||||
if (jsonObject == null) {
|
||||
logger.error("RTP推流失败: 请检查ZLM服务");
|
||||
} else if (jsonObject.getInteger("code") == 0) {
|
||||
logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port"));
|
||||
byte[] dialogByteArray = SerializeUtils.serialize(evt.getDialog());
|
||||
sendRtpItem.setDialog(dialogByteArray);
|
||||
byte[] transactionByteArray = SerializeUtils.serialize(evt.getServerTransaction());
|
||||
sendRtpItem.setTransaction(transactionByteArray);
|
||||
redisCatchStorage.updateSendRTPSever(sendRtpItem);
|
||||
} else {
|
||||
logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"),JSONObject.toJSON(param));
|
||||
if (sendRtpItem.isOnlyAudio()) {
|
||||
// TODO 可能是语音对讲
|
||||
}else {
|
||||
// 向上级平台
|
||||
commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,13 +114,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
playService.stopAudioBroadcast(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
|
||||
}
|
||||
if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
|
||||
MessageForPushChannel messageForPushChannel = new MessageForPushChannel();
|
||||
messageForPushChannel.setType(0);
|
||||
messageForPushChannel.setGbId(sendRtpItem.getChannelId());
|
||||
messageForPushChannel.setApp(sendRtpItem.getApp());
|
||||
messageForPushChannel.setStream(sendRtpItem.getStreamId());
|
||||
messageForPushChannel.setMediaServerId(sendRtpItem.getMediaServerId());
|
||||
messageForPushChannel.setPlatFormId(sendRtpItem.getPlatformId());
|
||||
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
|
||||
sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(),
|
||||
sendRtpItem.getPlatformId(), null, null, sendRtpItem.getMediaServerId());
|
||||
redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import javax.sip.RequestEvent;
|
||||
@Component
|
||||
public class CancelRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
||||
|
||||
private String method = "CANCEL";
|
||||
private final String method = "CANCEL";
|
||||
|
||||
@Autowired
|
||||
private SIPProcessorObserver sipProcessorObserver;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
@@ -18,6 +17,7 @@ import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
|
||||
import org.dom4j.DocumentException;
|
||||
import org.dom4j.Element;
|
||||
@@ -25,6 +25,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -35,6 +37,7 @@ import javax.sip.header.FromHeader;
|
||||
import javax.sip.message.Response;
|
||||
import java.text.ParseException;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* SIP命令类型: NOTIFY请求
|
||||
@@ -63,11 +66,19 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
@Autowired
|
||||
private EventPublisher publisher;
|
||||
|
||||
private String method = "NOTIFY";
|
||||
private final String method = "NOTIFY";
|
||||
|
||||
@Autowired
|
||||
private SIPProcessorObserver sipProcessorObserver;
|
||||
|
||||
private boolean taskQueueHandlerRun = false;
|
||||
|
||||
private final ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Qualifier("taskExecutor")
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// 添加消息处理的订阅
|
||||
@@ -77,23 +88,40 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
@Override
|
||||
public void process(RequestEvent evt) {
|
||||
try {
|
||||
Element rootElement = getRootElement(evt);
|
||||
String cmd = XmlUtil.getText(rootElement, "CmdType");
|
||||
|
||||
if (CmdType.CATALOG.equals(cmd)) {
|
||||
logger.info("接收到Catalog通知");
|
||||
processNotifyCatalogList(evt);
|
||||
} else if (CmdType.ALARM.equals(cmd)) {
|
||||
logger.info("接收到Alarm通知");
|
||||
processNotifyAlarm(evt);
|
||||
} else if (CmdType.MOBILE_POSITION.equals(cmd)) {
|
||||
logger.info("接收到MobilePosition通知");
|
||||
processNotifyMobilePosition(evt);
|
||||
} else {
|
||||
logger.info("接收到消息:" + cmd);
|
||||
responseAck(evt, Response.OK);
|
||||
taskQueue.offer(new HandlerCatchData(evt, null, null));
|
||||
responseAck(evt, Response.OK);
|
||||
if (!taskQueueHandlerRun) {
|
||||
taskQueueHandlerRun = true;
|
||||
taskExecutor.execute(()-> {
|
||||
while (!taskQueue.isEmpty()) {
|
||||
try {
|
||||
HandlerCatchData take = taskQueue.poll();
|
||||
Element rootElement = getRootElement(take.getEvt());
|
||||
String cmd = XmlUtil.getText(rootElement, "CmdType");
|
||||
|
||||
if (CmdType.CATALOG.equals(cmd)) {
|
||||
logger.info("接收到Catalog通知");
|
||||
processNotifyCatalogList(take.getEvt());
|
||||
} else if (CmdType.ALARM.equals(cmd)) {
|
||||
logger.info("接收到Alarm通知");
|
||||
processNotifyAlarm(take.getEvt());
|
||||
} else if (CmdType.MOBILE_POSITION.equals(cmd)) {
|
||||
logger.info("接收到MobilePosition通知");
|
||||
processNotifyMobilePosition(take.getEvt());
|
||||
} else {
|
||||
logger.info("接收到消息:" + cmd);
|
||||
}
|
||||
} catch (DocumentException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
taskQueueHandlerRun = false;
|
||||
});
|
||||
}
|
||||
} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
|
||||
|
||||
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -166,8 +194,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
jsonObject.put("direction", mobilePosition.getDirection());
|
||||
jsonObject.put("speed", mobilePosition.getSpeed());
|
||||
redisCatchStorage.sendMobilePositionMsg(jsonObject);
|
||||
responseAck(evt, Response.OK);
|
||||
} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -188,6 +215,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
|
||||
Device device = redisCatchStorage.getDevice(deviceId);
|
||||
if (device == null) {
|
||||
logger.warn("[ NotifyAlarm ] 未找到设备:{}", deviceId);
|
||||
return;
|
||||
}
|
||||
rootElement = getRootElement(evt, device.getCharset());
|
||||
@@ -195,7 +223,12 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
deviceAlarm.setDeviceId(deviceId);
|
||||
deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority"));
|
||||
deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod"));
|
||||
deviceAlarm.setAlarmTime(XmlUtil.getText(rootElement, "AlarmTime"));
|
||||
String alarmTime = XmlUtil.getText(rootElement, "AlarmTime");
|
||||
if (alarmTime == null) {
|
||||
logger.warn("[ NotifyAlarm ] AlarmTime cannot be null");
|
||||
return;
|
||||
}
|
||||
deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime));
|
||||
if (XmlUtil.getText(rootElement, "AlarmDescription") == null) {
|
||||
deviceAlarm.setAlarmDescription("");
|
||||
} else {
|
||||
@@ -212,7 +245,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
deviceAlarm.setLatitude(0.00);
|
||||
}
|
||||
logger.info("[收到Notify-Alarm]:{}/{}", device.getDeviceId(), deviceAlarm.getChannelId());
|
||||
if (deviceAlarm.getAlarmMethod().equals("4")) {
|
||||
if ("4".equals(deviceAlarm.getAlarmMethod())) {
|
||||
MobilePosition mobilePosition = new MobilePosition();
|
||||
mobilePosition.setDeviceId(deviceAlarm.getDeviceId());
|
||||
mobilePosition.setTime(deviceAlarm.getAlarmTime());
|
||||
@@ -233,11 +266,10 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
// TODO: 需要实现存储报警信息、报警分类
|
||||
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
if (redisCatchStorage.deviceIsOnline(deviceId)) {
|
||||
publisher.deviceAlarmEventPublish(deviceAlarm);
|
||||
}
|
||||
} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -273,64 +305,60 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
continue;
|
||||
}
|
||||
Element eventElement = itemDevice.element("Event");
|
||||
DeviceChannel channel = XmlUtil.channelContentHander(itemDevice);
|
||||
String event;
|
||||
if (eventElement == null) {
|
||||
logger.warn("[收到 目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" ));
|
||||
event = CatalogEvent.ADD;
|
||||
}else {
|
||||
event = eventElement.getText().toUpperCase();
|
||||
}
|
||||
DeviceChannel channel = XmlUtil.channelContentHander(itemDevice, device);
|
||||
channel.setDeviceId(device.getDeviceId());
|
||||
logger.info("[收到 目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId());
|
||||
switch (eventElement.getText().toUpperCase()) {
|
||||
switch (event) {
|
||||
case CatalogEvent.ON:
|
||||
// 上线
|
||||
logger.info("收到来自设备【{}】的通道【{}】上线通知", device.getDeviceId(), channel.getChannelId());
|
||||
storager.deviceChannelOnline(deviceId, channel.getChannelId());
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
break;
|
||||
case CatalogEvent.OFF :
|
||||
// 离线
|
||||
logger.info("收到来自设备【{}】的通道【{}】离线通知", device.getDeviceId(), channel.getChannelId());
|
||||
storager.deviceChannelOffline(deviceId, channel.getChannelId());
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
break;
|
||||
case CatalogEvent.VLOST:
|
||||
// 视频丢失
|
||||
logger.info("收到来自设备【{}】的通道【{}】视频丢失通知", device.getDeviceId(), channel.getChannelId());
|
||||
storager.deviceChannelOffline(deviceId, channel.getChannelId());
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
break;
|
||||
case CatalogEvent.DEFECT:
|
||||
// 故障
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
break;
|
||||
case CatalogEvent.ADD:
|
||||
// 增加
|
||||
logger.info("收到来自设备【{}】的增加通道【{}】通知", device.getDeviceId(), channel.getChannelId());
|
||||
storager.updateChannel(deviceId, channel);
|
||||
responseAck(evt, Response.OK);
|
||||
break;
|
||||
case CatalogEvent.DEL:
|
||||
// 删除
|
||||
logger.info("收到来自设备【{}】的删除通道【{}】通知", device.getDeviceId(), channel.getChannelId());
|
||||
storager.delChannel(deviceId, channel.getChannelId());
|
||||
responseAck(evt, Response.OK);
|
||||
break;
|
||||
case CatalogEvent.UPDATE:
|
||||
// 更新
|
||||
logger.info("收到来自设备【{}】的更新通道【{}】通知", device.getDeviceId(), channel.getChannelId());
|
||||
storager.updateChannel(deviceId, channel);
|
||||
responseAck(evt, Response.OK);
|
||||
break;
|
||||
default:
|
||||
responseAck(evt, Response.BAD_REQUEST, "event not found");
|
||||
logger.warn("[ NotifyCatalog ] event not found : {}", event );
|
||||
|
||||
}
|
||||
// 转发变化信息
|
||||
eventPublisher.catalogEventPublish(null, channel, eventElement.getText().toUpperCase());
|
||||
eventPublisher.catalogEventPublish(null, channel, event);
|
||||
|
||||
}
|
||||
}
|
||||
} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||
import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate;
|
||||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
||||
import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
|
||||
import com.genersoft.iot.vmp.service.IDeviceService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import gov.nist.javax.sip.RequestEventExt;
|
||||
import gov.nist.javax.sip.address.AddressImpl;
|
||||
@@ -45,20 +41,11 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class);
|
||||
|
||||
public String method = "REGISTER";
|
||||
public final String method = "REGISTER";
|
||||
|
||||
@Autowired
|
||||
private SipConfig sipConfig;
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storager;
|
||||
|
||||
@Autowired
|
||||
private EventPublisher publisher;
|
||||
|
||||
@Autowired
|
||||
private SIPProcessorObserver sipProcessorObserver;
|
||||
|
||||
@@ -86,7 +73,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
|
||||
ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME);
|
||||
Response response = null;
|
||||
boolean passwordCorrect = false;
|
||||
// 注册标志 0:未携带授权头或者密码错误 1:注册成功 2:注销成功
|
||||
// 注册标志
|
||||
boolean registerFlag = false;
|
||||
FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
|
||||
AddressImpl address = (AddressImpl) fromHeader.getAddress();
|
||||
@@ -105,7 +92,6 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
|
||||
// 校验密码是否正确
|
||||
passwordCorrect = StringUtils.isEmpty(sipConfig.getPassword()) ||
|
||||
new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, sipConfig.getPassword());
|
||||
// 未携带授权头或者密码错误 回复401
|
||||
|
||||
if (!passwordCorrect) {
|
||||
// 注册失败
|
||||
@@ -154,6 +140,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
|
||||
device = new Device();
|
||||
device.setStreamMode("UDP");
|
||||
device.setCharset("GB2312");
|
||||
device.setGeoCoordSys("WGS84");
|
||||
device.setDeviceId(deviceId);
|
||||
}
|
||||
device.setIp(received);
|
||||
|
||||
@@ -82,7 +82,6 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
|
||||
@Override
|
||||
public void process(RequestEvent evt) {
|
||||
Request request = evt.getRequest();
|
||||
|
||||
try {
|
||||
Element rootElement = getRootElement(evt);
|
||||
String cmd = XmlUtil.getText(rootElement, "CmdType");
|
||||
@@ -176,6 +175,8 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
|
||||
}
|
||||
|
||||
private void processNotifyCatalogList(RequestEvent evt, Element rootElement) throws SipException {
|
||||
|
||||
System.out.println(evt.getRequest().toString());
|
||||
String platformId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
|
||||
String deviceId = XmlUtil.getText(rootElement, "DeviceID");
|
||||
ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.info;
|
||||
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import gov.nist.javax.sip.message.SIPRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.RequestEvent;
|
||||
import javax.sip.SipException;
|
||||
import javax.sip.header.*;
|
||||
import javax.sip.message.Response;
|
||||
import java.text.ParseException;
|
||||
|
||||
@Component
|
||||
public class InfoRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(InfoRequestProcessor.class);
|
||||
|
||||
private final String method = "INFO";
|
||||
|
||||
@Autowired
|
||||
private SIPProcessorObserver sipProcessorObserver;
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storage;
|
||||
|
||||
@Autowired
|
||||
private SipSubscribe sipSubscribe;
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storager;
|
||||
|
||||
@Autowired
|
||||
private SIPCommander cmder;
|
||||
|
||||
@Autowired
|
||||
private VideoStreamSessionManager sessionManager;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// 添加消息处理的订阅
|
||||
sipProcessorObserver.addRequestProcessor(method, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(RequestEvent evt) {
|
||||
logger.debug("接收到消息:" + evt.getRequest());
|
||||
String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
|
||||
CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
|
||||
// 先从会话内查找
|
||||
SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
|
||||
if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题
|
||||
deviceId = ssrcTransaction.getDeviceId();
|
||||
}
|
||||
// 查询设备是否存在
|
||||
Device device = redisCatchStorage.getDevice(deviceId);
|
||||
// 查询上级平台是否存在
|
||||
ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId);
|
||||
try {
|
||||
if (device != null && parentPlatform != null) {
|
||||
logger.warn("[重复]平台与设备编号重复:{}", deviceId);
|
||||
SIPRequest request = (SIPRequest) evt.getRequest();
|
||||
String hostAddress = request.getRemoteAddress().getHostAddress();
|
||||
int remotePort = request.getRemotePort();
|
||||
if (device.getHostAddress().equals(hostAddress + ":" + remotePort)) {
|
||||
parentPlatform = null;
|
||||
}else {
|
||||
device = null;
|
||||
}
|
||||
}
|
||||
if (device == null && parentPlatform == null) {
|
||||
// 不存在则回复404
|
||||
responseAck(evt, Response.NOT_FOUND, "device "+ deviceId +" not found");
|
||||
logger.warn("[设备未找到 ]: {}", deviceId);
|
||||
if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null){
|
||||
SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new DeviceNotFoundEvent(evt.getDialog()));
|
||||
sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult);
|
||||
};
|
||||
}else {
|
||||
ContentTypeHeader header = (ContentTypeHeader)evt.getRequest().getHeader(ContentTypeHeader.NAME);
|
||||
String contentType = header.getContentType();
|
||||
String contentSubType = header.getContentSubType();
|
||||
if ("Application".equals(contentType) && "MANSRTSP".equals(contentSubType)) {
|
||||
SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId());
|
||||
String streamId = sendRtpItem.getStreamId();
|
||||
StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null);
|
||||
if (null == streamInfo) {
|
||||
responseAck(evt, Response.NOT_FOUND, "stream " + streamId + " not found");
|
||||
return;
|
||||
}
|
||||
Device device1 = storager.queryVideoDevice(streamInfo.getDeviceID());
|
||||
cmder.playbackControlCmd(device1,streamInfo,new String(evt.getRequest().getRawContent()),eventResult -> {
|
||||
// 失败的回复
|
||||
try {
|
||||
responseAck(evt, eventResult.statusCode, eventResult.msg);
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, eventResult -> {
|
||||
// 成功的回复
|
||||
try {
|
||||
responseAck(evt, eventResult.statusCode);
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (SipException e) {
|
||||
logger.warn("SIP 回复错误", e);
|
||||
} catch (InvalidArgumentException e) {
|
||||
logger.warn("参数无效", e);
|
||||
} catch (ParseException e) {
|
||||
logger.warn("SIP回复时解析异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
|
||||
String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
|
||||
CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
|
||||
// 先从会话内查找
|
||||
SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, null, callIdHeader.getCallId());
|
||||
SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
|
||||
if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题
|
||||
deviceId = ssrcTransaction.getDeviceId();
|
||||
}
|
||||
|
||||
@@ -9,9 +9,11 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessag
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
|
||||
import com.genersoft.iot.vmp.service.IDeviceAlarmService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import org.dom4j.Element;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -84,7 +86,11 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
|
||||
deviceAlarm.setChannelId(channelId);
|
||||
deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority"));
|
||||
deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod"));
|
||||
deviceAlarm.setAlarmTime(getText(rootElement, "AlarmTime"));
|
||||
String alarmTime = XmlUtil.getText(rootElement, "AlarmTime");
|
||||
if (alarmTime == null) {
|
||||
return;
|
||||
}
|
||||
deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime));
|
||||
String alarmDescription = getText(rootElement, "AlarmDescription");
|
||||
if (alarmDescription == null) {
|
||||
deviceAlarm.setAlarmDescription("");
|
||||
@@ -175,7 +181,11 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
|
||||
deviceAlarm.setChannelId(channelId);
|
||||
deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority"));
|
||||
deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod"));
|
||||
deviceAlarm.setAlarmTime(getText(rootElement, "AlarmTime"));
|
||||
String alarmTime = XmlUtil.getText(rootElement, "AlarmTime");
|
||||
if (alarmTime == null) {
|
||||
return;
|
||||
}
|
||||
deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime));
|
||||
String alarmDescription = getText(rootElement, "AlarmDescription");
|
||||
if (alarmDescription == null) {
|
||||
deviceAlarm.setAlarmDescription("");
|
||||
|
||||
@@ -64,16 +64,14 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
|
||||
device.setHostAddress(received.concat(":").concat(String.valueOf(rPort)));
|
||||
}
|
||||
device.setKeepaliveTime(DateUtil.getNow());
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
if (device.getOnline() == 1) {
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
deviceService.updateDevice(device);
|
||||
}else {
|
||||
// 对于已经离线的设备判断他的注册是否已经过期
|
||||
if (!deviceService.expire(device)){
|
||||
deviceService.online(device);
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
}
|
||||
}
|
||||
} catch (SipException e) {
|
||||
|
||||
@@ -3,11 +3,16 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import org.dom4j.Element;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -36,9 +41,18 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
|
||||
@Autowired
|
||||
private SIPCommander cmder;
|
||||
|
||||
@Autowired
|
||||
private SIPCommanderFroPlatform sipCommanderFroPlatform;
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storage;
|
||||
|
||||
@Autowired
|
||||
private VideoStreamSessionManager sessionManager;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
notifyMessageHandler.addHandler(cmdType, this);
|
||||
@@ -59,17 +73,32 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
|
||||
}
|
||||
CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
|
||||
String NotifyType =getText(rootElement, "NotifyType");
|
||||
if (NotifyType.equals("121")){
|
||||
if ("121".equals(NotifyType)){
|
||||
logger.info("[录像流]推送完毕,收到关流通知");
|
||||
String channelId =getText(rootElement, "DeviceID");
|
||||
// 查询是设备
|
||||
StreamInfo streamInfo = redisCatchStorage.queryDownload(device.getDeviceId(), channelId, null, callIdHeader.getCallId());
|
||||
// 设置进度100%
|
||||
streamInfo.setProgress(1);
|
||||
redisCatchStorage.startDownload(streamInfo, callIdHeader.getCallId());
|
||||
cmder.streamByeCmd(device.getDeviceId(), channelId, null, callIdHeader.getCallId());
|
||||
// TODO 如果级联播放,需要给上级发送此通知
|
||||
StreamInfo streamInfo = redisCatchStorage.queryDownload(null, null, null, callIdHeader.getCallId());
|
||||
if (streamInfo != null) {
|
||||
// 设置进度100%
|
||||
streamInfo.setProgress(1);
|
||||
redisCatchStorage.startDownload(streamInfo, callIdHeader.getCallId());
|
||||
}
|
||||
|
||||
// 先从会话内查找
|
||||
SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
|
||||
if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题
|
||||
cmder.streamByeCmd(device.getDeviceId(), ssrcTransaction.getChannelId(), null, callIdHeader.getCallId());
|
||||
|
||||
// 如果级联播放,需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题,需要将点播CallId进行上下级绑定
|
||||
SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, ssrcTransaction.getChannelId(), null, null);
|
||||
if (sendRtpItem != null) {
|
||||
ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
|
||||
if (parentPlatform == null) {
|
||||
logger.warn("[级联消息发送]:发送MediaStatus发现上级平台{}不存在", sendRtpItem.getPlatformId());
|
||||
return;
|
||||
}
|
||||
sipCommanderFroPlatform.sendMediaStatusNotify(parentPlatform, sendRtpItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -31,6 +33,7 @@ import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
@Component
|
||||
public class CatalogResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
|
||||
@@ -38,9 +41,13 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
|
||||
private Logger logger = LoggerFactory.getLogger(CatalogResponseMessageHandler.class);
|
||||
private final String cmdType = "Catalog";
|
||||
|
||||
private boolean taskQueueHandlerRun = false;
|
||||
|
||||
@Autowired
|
||||
private ResponseMessageHandler responseMessageHandler;
|
||||
|
||||
private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storager;
|
||||
|
||||
@@ -63,6 +70,10 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Qualifier("taskExecutor")
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
responseMessageHandler.addHandler(cmdType, this);
|
||||
@@ -70,68 +81,88 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
|
||||
|
||||
@Override
|
||||
public void handForDevice(RequestEvent evt, Device device, Element element) {
|
||||
String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + device.getDeviceId();
|
||||
Element rootElement = null;
|
||||
taskQueue.offer(new HandlerCatchData(evt, device, element));
|
||||
// 回复200 OK
|
||||
try {
|
||||
rootElement = getRootElement(evt, device.getCharset());
|
||||
Element deviceListElement = rootElement.element("DeviceList");
|
||||
Element sumNumElement = rootElement.element("SumNum");
|
||||
Element snElement = rootElement.element("SN");
|
||||
if (snElement == null || sumNumElement == null || deviceListElement == null) {
|
||||
responseAck(evt, Response.BAD_REQUEST, "xml error");
|
||||
return;
|
||||
}
|
||||
int sumNum = Integer.parseInt(sumNumElement.getText());
|
||||
|
||||
if (sumNum == 0) {
|
||||
// 数据已经完整接收
|
||||
storager.cleanChannelsForDevice(device.getDeviceId());
|
||||
catalogDataCatch.setChannelSyncEnd(device.getDeviceId(), null);
|
||||
}else {
|
||||
Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
|
||||
if (deviceListIterator != null) {
|
||||
List<DeviceChannel> channelList = new ArrayList<>();
|
||||
// 遍历DeviceList
|
||||
while (deviceListIterator.hasNext()) {
|
||||
Element itemDevice = deviceListIterator.next();
|
||||
Element channelDeviceElement = itemDevice.element("DeviceID");
|
||||
if (channelDeviceElement == null) {
|
||||
continue;
|
||||
responseAck(evt, Response.OK);
|
||||
} catch (SipException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvalidArgumentException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (!taskQueueHandlerRun) {
|
||||
taskQueueHandlerRun = true;
|
||||
taskExecutor.execute(()-> {
|
||||
while (!taskQueue.isEmpty()) {
|
||||
HandlerCatchData take = taskQueue.poll();
|
||||
String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + take.getDevice().getDeviceId();
|
||||
Element rootElement = null;
|
||||
try {
|
||||
rootElement = getRootElement(take.getEvt(), take.getDevice().getCharset());
|
||||
Element deviceListElement = rootElement.element("DeviceList");
|
||||
Element sumNumElement = rootElement.element("SumNum");
|
||||
Element snElement = rootElement.element("SN");
|
||||
if (snElement == null || sumNumElement == null || deviceListElement == null) {
|
||||
responseAck(take.getEvt(), Response.BAD_REQUEST, "xml error");
|
||||
return;
|
||||
}
|
||||
//by brewswang
|
||||
// if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {//如果包含位置信息,就更新一下位置
|
||||
// processNotifyMobilePosition(evt, itemDevice);
|
||||
// }
|
||||
DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice);
|
||||
deviceChannel.setDeviceId(device.getDeviceId());
|
||||
int sumNum = Integer.parseInt(sumNumElement.getText());
|
||||
|
||||
channelList.add(deviceChannel);
|
||||
}
|
||||
int sn = Integer.parseInt(snElement.getText());
|
||||
catalogDataCatch.put(device.getDeviceId(), sn, sumNum, device, channelList);
|
||||
logger.info("收到来自设备【{}】的通道: {}个,{}/{}", device.getDeviceId(), channelList.size(), catalogDataCatch.get(device.getDeviceId()) == null ? 0 :catalogDataCatch.get(device.getDeviceId()).size(), sumNum);
|
||||
if (catalogDataCatch.get(device.getDeviceId()).size() == sumNum) {
|
||||
// 数据已经完整接收
|
||||
boolean resetChannelsResult = storager.resetChannels(device.getDeviceId(), catalogDataCatch.get(device.getDeviceId()));
|
||||
if (!resetChannelsResult) {
|
||||
String errorMsg = "接收成功,写入失败,共" + sumNum + "条,已接收" + catalogDataCatch.get(device.getDeviceId()).size() + "条";
|
||||
catalogDataCatch.setChannelSyncEnd(device.getDeviceId(), errorMsg);
|
||||
if (sumNum == 0) {
|
||||
// 数据已经完整接收
|
||||
storager.cleanChannelsForDevice(take.getDevice().getDeviceId());
|
||||
catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null);
|
||||
}else {
|
||||
catalogDataCatch.setChannelSyncEnd(device.getDeviceId(), null);
|
||||
Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
|
||||
if (deviceListIterator != null) {
|
||||
List<DeviceChannel> channelList = new ArrayList<>();
|
||||
// 遍历DeviceList
|
||||
while (deviceListIterator.hasNext()) {
|
||||
Element itemDevice = deviceListIterator.next();
|
||||
Element channelDeviceElement = itemDevice.element("DeviceID");
|
||||
if (channelDeviceElement == null) {
|
||||
continue;
|
||||
}
|
||||
//by brewswang
|
||||
// if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {//如果包含位置信息,就更新一下位置
|
||||
// processNotifyMobilePosition(evt, itemDevice);
|
||||
// }
|
||||
DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device);
|
||||
deviceChannel.setDeviceId(take.getDevice().getDeviceId());
|
||||
|
||||
channelList.add(deviceChannel);
|
||||
}
|
||||
int sn = Integer.parseInt(snElement.getText());
|
||||
catalogDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, take.getDevice(), channelList);
|
||||
logger.info("收到来自设备【{}】的通道: {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.get(take.getDevice().getDeviceId()) == null ? 0 :catalogDataCatch.get(take.getDevice().getDeviceId()).size(), sumNum);
|
||||
if (catalogDataCatch.get(take.getDevice().getDeviceId()).size() == sumNum) {
|
||||
// 数据已经完整接收
|
||||
boolean resetChannelsResult = storager.resetChannels(take.getDevice().getDeviceId(), catalogDataCatch.get(take.getDevice().getDeviceId()));
|
||||
if (!resetChannelsResult) {
|
||||
String errorMsg = "接收成功,写入失败,共" + sumNum + "条,已接收" + catalogDataCatch.get(take.getDevice().getDeviceId()).size() + "条";
|
||||
catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), errorMsg);
|
||||
}else {
|
||||
catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
// 回复200 OK
|
||||
responseAck(evt, Response.OK);
|
||||
}
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
taskQueueHandlerRun = false;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParen
|
||||
deviceService.offline(device.getDeviceId());
|
||||
}
|
||||
RequestMessage msg = new RequestMessage();
|
||||
msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId() + channelId);
|
||||
msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId());
|
||||
msg.setData(json);
|
||||
deferredResultHolder.invokeAllResult(msg);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.RecordItem;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||
import com.genersoft.iot.vmp.gb28181.session.RecordDataCatch;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||
@@ -19,6 +16,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -28,6 +27,9 @@ import javax.sip.SipException;
|
||||
import javax.sip.message.Response;
|
||||
import java.text.ParseException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
|
||||
|
||||
@@ -38,10 +40,11 @@ import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
|
||||
public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(RecordInfoResponseMessageHandler.class);
|
||||
public static volatile List<String> threadNameList = new ArrayList();
|
||||
private final String cmdType = "RecordInfo";
|
||||
private final static String CACHE_RECORDINFO_KEY = "CACHE_RECORDINFO_";
|
||||
|
||||
private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private boolean taskQueueHandlerRun = false;
|
||||
@Autowired
|
||||
private ResponseMessageHandler responseMessageHandler;
|
||||
|
||||
@@ -51,11 +54,13 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
|
||||
@Autowired
|
||||
private DeferredResultHolder deferredResultHolder;
|
||||
|
||||
|
||||
|
||||
@Autowired
|
||||
private EventPublisher eventPublisher;
|
||||
|
||||
@Qualifier("taskExecutor")
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
responseMessageHandler.addHandler(cmdType, this);
|
||||
@@ -67,67 +72,89 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
|
||||
// 回复200 OK
|
||||
try {
|
||||
responseAck(evt, Response.OK);
|
||||
taskQueue.offer(new HandlerCatchData(evt, device, rootElement));
|
||||
if (!taskQueueHandlerRun) {
|
||||
taskQueueHandlerRun = true;
|
||||
taskExecutor.execute(()->{
|
||||
try {
|
||||
while (!taskQueue.isEmpty()) {
|
||||
HandlerCatchData take = taskQueue.poll();
|
||||
Element rootElementForCharset = getRootElement(take.getEvt(), take.getDevice().getCharset());
|
||||
String sn = getText(rootElementForCharset, "SN");
|
||||
String channelId = getText(rootElementForCharset, "DeviceID");
|
||||
RecordInfo recordInfo = new RecordInfo();
|
||||
recordInfo.setChannelId(channelId);
|
||||
recordInfo.setDeviceId(take.getDevice().getDeviceId());
|
||||
recordInfo.setSn(sn);
|
||||
recordInfo.setName(getText(rootElementForCharset, "Name"));
|
||||
String sumNumStr = getText(rootElementForCharset, "SumNum");
|
||||
int sumNum = 0;
|
||||
if (!StringUtils.isEmpty(sumNumStr)) {
|
||||
sumNum = Integer.parseInt(sumNumStr);
|
||||
}
|
||||
recordInfo.setSumNum(sumNum);
|
||||
Element recordListElement = rootElementForCharset.element("RecordList");
|
||||
if (recordListElement == null || sumNum == 0) {
|
||||
logger.info("无录像数据");
|
||||
eventPublisher.recordEndEventPush(recordInfo);
|
||||
recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, new ArrayList<>());
|
||||
releaseRequest(take.getDevice().getDeviceId(), sn);
|
||||
} else {
|
||||
Iterator<Element> recordListIterator = recordListElement.elementIterator();
|
||||
if (recordListIterator != null) {
|
||||
List<RecordItem> recordList = new ArrayList<>();
|
||||
// 遍历DeviceList
|
||||
while (recordListIterator.hasNext()) {
|
||||
Element itemRecord = recordListIterator.next();
|
||||
Element recordElement = itemRecord.element("DeviceID");
|
||||
if (recordElement == null) {
|
||||
logger.info("记录为空,下一个...");
|
||||
continue;
|
||||
}
|
||||
RecordItem record = new RecordItem();
|
||||
record.setDeviceId(getText(itemRecord, "DeviceID"));
|
||||
record.setName(getText(itemRecord, "Name"));
|
||||
record.setFilePath(getText(itemRecord, "FilePath"));
|
||||
record.setFileSize(getText(itemRecord, "FileSize"));
|
||||
record.setAddress(getText(itemRecord, "Address"));
|
||||
|
||||
rootElement = getRootElement(evt, device.getCharset());
|
||||
String sn = getText(rootElement, "SN");
|
||||
String startTimeStr = getText(itemRecord, "StartTime");
|
||||
record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr));
|
||||
|
||||
String sumNumStr = getText(rootElement, "SumNum");
|
||||
int sumNum = 0;
|
||||
if (!StringUtils.isEmpty(sumNumStr)) {
|
||||
sumNum = Integer.parseInt(sumNumStr);
|
||||
}
|
||||
Element recordListElement = rootElement.element("RecordList");
|
||||
if (recordListElement == null || sumNum == 0) {
|
||||
logger.info("无录像数据");
|
||||
recordDataCatch.put(device.getDeviceId(), sn, sumNum, new ArrayList<>());
|
||||
releaseRequest(device.getDeviceId(), sn);
|
||||
} else {
|
||||
Iterator<Element> recordListIterator = recordListElement.elementIterator();
|
||||
if (recordListIterator != null) {
|
||||
List<RecordItem> recordList = new ArrayList<>();
|
||||
// 遍历DeviceList
|
||||
while (recordListIterator.hasNext()) {
|
||||
Element itemRecord = recordListIterator.next();
|
||||
Element recordElement = itemRecord.element("DeviceID");
|
||||
if (recordElement == null) {
|
||||
logger.info("记录为空,下一个...");
|
||||
continue;
|
||||
String endTimeStr = getText(itemRecord, "EndTime");
|
||||
record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr));
|
||||
|
||||
record.setSecrecy(itemRecord.element("Secrecy") == null ? 0
|
||||
: Integer.parseInt(getText(itemRecord, "Secrecy")));
|
||||
record.setType(getText(itemRecord, "Type"));
|
||||
record.setRecorderId(getText(itemRecord, "RecorderID"));
|
||||
recordList.add(record);
|
||||
}
|
||||
recordInfo.setRecordList(recordList);
|
||||
// 发送消息,如果是上级查询此录像,则会通过这里通知给上级
|
||||
eventPublisher.recordEndEventPush(recordInfo);
|
||||
int count = recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, recordList);
|
||||
logger.info("[国标录像], {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum);
|
||||
}
|
||||
|
||||
if (recordDataCatch.isComplete(take.getDevice().getDeviceId(), sn)){
|
||||
releaseRequest(take.getDevice().getDeviceId(), sn);
|
||||
}
|
||||
}
|
||||
}
|
||||
RecordItem record = new RecordItem();
|
||||
record.setDeviceId(getText(itemRecord, "DeviceID"));
|
||||
record.setName(getText(itemRecord, "Name"));
|
||||
record.setFilePath(getText(itemRecord, "FilePath"));
|
||||
record.setFileSize(getText(itemRecord, "FileSize"));
|
||||
record.setAddress(getText(itemRecord, "Address"));
|
||||
|
||||
String startTimeStr = getText(itemRecord, "StartTime");
|
||||
record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr));
|
||||
|
||||
String endTimeStr = getText(itemRecord, "EndTime");
|
||||
record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr));
|
||||
|
||||
record.setSecrecy(itemRecord.element("Secrecy") == null ? 0
|
||||
: Integer.parseInt(getText(itemRecord, "Secrecy")));
|
||||
record.setType(getText(itemRecord, "Type"));
|
||||
record.setRecorderId(getText(itemRecord, "RecorderID"));
|
||||
recordList.add(record);
|
||||
taskQueueHandlerRun = false;
|
||||
}catch (DocumentException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
int count = recordDataCatch.put(device.getDeviceId(), sn, sumNum, recordList);
|
||||
logger.info("[国标录像], {}->{}: {}/{}", device.getDeviceId(), sn, count, sumNum);
|
||||
}
|
||||
|
||||
if (recordDataCatch.isComplete(device.getDeviceId(), sn)){
|
||||
releaseRequest(device.getDeviceId(), sn);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import javax.sip.ResponseEvent;
|
||||
@Component
|
||||
public class ByeResponseProcessor extends SIPResponseProcessorAbstract {
|
||||
|
||||
private String method = "BYE";
|
||||
private final String method = "BYE";
|
||||
|
||||
@Autowired
|
||||
private SipLayer sipLayer;
|
||||
|
||||
@@ -17,7 +17,7 @@ import javax.sip.ResponseEvent;
|
||||
@Component
|
||||
public class CancelResponseProcessor extends SIPResponseProcessorAbstract {
|
||||
|
||||
private String method = "CANCEL";
|
||||
private final String method = "CANCEL";
|
||||
|
||||
@Autowired
|
||||
private SipLayer sipLayer;
|
||||
|
||||
@@ -31,7 +31,7 @@ import java.text.ParseException;
|
||||
public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
|
||||
private String method = "INVITE";
|
||||
private final String method = "INVITE";
|
||||
|
||||
@Autowired
|
||||
private SipLayer sipLayer;
|
||||
|
||||
@@ -27,7 +27,7 @@ import javax.sip.message.Response;
|
||||
public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class);
|
||||
private String method = "REGISTER";
|
||||
private final String method = "REGISTER";
|
||||
|
||||
@Autowired
|
||||
private ISIPCommanderForPlatform sipCommanderForPlatform;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||
import org.dom4j.Attribute;
|
||||
import org.dom4j.Document;
|
||||
@@ -180,7 +181,7 @@ public class XmlUtil {
|
||||
return xml.getRootElement();
|
||||
}
|
||||
|
||||
public static DeviceChannel channelContentHander(Element itemDevice){
|
||||
public static DeviceChannel channelContentHander(Element itemDevice, Device device){
|
||||
Element channdelNameElement = itemDevice.element("Name");
|
||||
String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : "";
|
||||
Element statusElement = itemDevice.element("Status");
|
||||
@@ -254,6 +255,8 @@ public class XmlUtil {
|
||||
}else if (deviceChannel.getChannelId().length() == 20) {
|
||||
if (Integer.parseInt(deviceChannel.getChannelId().substring(10, 13)) == 216) { // 虚拟组织
|
||||
deviceChannel.setParentId(businessGroupID);
|
||||
}else if (Integer.parseInt(device.getDeviceId().substring(10, 13) )== 118) {//NVR 如果上级设备编号是NVR则直接将NVR的编号设置给通道的上级编号
|
||||
deviceChannel.setParentId(device.getDeviceId());
|
||||
}else if (deviceChannel.getCivilCode() != null) {
|
||||
// 设备, 无parentId的20位是使用CivilCode表示上级的设备,
|
||||
// 注:215 业务分组是需要有parentId的
|
||||
@@ -308,6 +311,31 @@ public class XmlUtil {
|
||||
} else {
|
||||
deviceChannel.setLatitude(0.00);
|
||||
}
|
||||
if (deviceChannel.getLongitude()*deviceChannel.getLatitude() > 0) {
|
||||
if ("WGS84".equals(device.getGeoCoordSys())) {
|
||||
deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
|
||||
deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
|
||||
Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude());
|
||||
deviceChannel.setLongitudeGcj02(position[0]);
|
||||
deviceChannel.setLatitudeGcj02(position[1]);
|
||||
}else if ("GCJ02".equals(device.getGeoCoordSys())) {
|
||||
deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
|
||||
deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
|
||||
Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude());
|
||||
deviceChannel.setLongitudeWgs84(position[0]);
|
||||
deviceChannel.setLatitudeWgs84(position[1]);
|
||||
}else {
|
||||
deviceChannel.setLongitudeGcj02(0.00);
|
||||
deviceChannel.setLatitudeGcj02(0.00);
|
||||
deviceChannel.setLongitudeWgs84(0.00);
|
||||
deviceChannel.setLatitudeWgs84(0.00);
|
||||
}
|
||||
}else {
|
||||
deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
|
||||
deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
|
||||
deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
|
||||
deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
|
||||
}
|
||||
if (XmlUtil.getText(itemDevice, "PTZType") == null || "".equals(XmlUtil.getText(itemDevice, "PTZType"))) {
|
||||
//兼容INFO中的信息
|
||||
Element info = itemDevice.element("Info");
|
||||
|
||||
@@ -11,7 +11,6 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
|
||||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.*;
|
||||
import com.genersoft.iot.vmp.service.*;
|
||||
@@ -92,10 +91,9 @@ public class ZLMHttpHookListener {
|
||||
public ResponseEntity<String> onServerKeepalive(@RequestBody JSONObject json){
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("[ ZLM HOOK ]on_server_keepalive API调用,参数:" + json.toString());
|
||||
logger.debug("[ ZLM HOOK ] on_server_keepalive API调用,参数:" + json.toString());
|
||||
}
|
||||
String mediaServerId = json.getString("mediaServerId");
|
||||
|
||||
List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_keepalive);
|
||||
if (subscribes != null && subscribes.size() > 0) {
|
||||
for (ZLMHttpHookSubscribe.Event subscribe : subscribes) {
|
||||
@@ -165,7 +163,6 @@ public class ZLMHttpHookListener {
|
||||
if (mediaInfo != null) {
|
||||
subscribe.response(mediaInfo, json);
|
||||
}
|
||||
|
||||
}
|
||||
JSONObject ret = new JSONObject();
|
||||
ret.put("code", 0);
|
||||
@@ -248,6 +245,23 @@ public class ZLMHttpHookListener {
|
||||
ret.put("msg", "success");
|
||||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
|
||||
}
|
||||
/**
|
||||
* 录制hls完成后通知事件;此事件对回复不敏感。
|
||||
*
|
||||
*/
|
||||
@ResponseBody
|
||||
@PostMapping(value = "/on_record_ts", produces = "application/json;charset=UTF-8")
|
||||
public ResponseEntity<String> onRecordTs(@RequestBody JSONObject json){
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("[ ZLM HOOK ]on_record_ts API调用,参数:" + json.toString());
|
||||
}
|
||||
String mediaServerId = json.getString("mediaServerId");
|
||||
JSONObject ret = new JSONObject();
|
||||
ret.put("code", 0);
|
||||
ret.put("msg", "success");
|
||||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件。
|
||||
@@ -383,21 +397,22 @@ public class ZLMHttpHookListener {
|
||||
if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal()
|
||||
|| item.getOriginType() == OriginType.RTMP_PUSH.ordinal()
|
||||
|| item.getOriginType() == OriginType.RTC_PUSH.ordinal() ) {
|
||||
streamPushItem = zlmMediaListManager.addPush(item);
|
||||
item.setSeverId(userSetting.getServerId());
|
||||
zlmMediaListManager.addPush(item);
|
||||
}
|
||||
|
||||
List<GbStream> gbStreams = new ArrayList<>();
|
||||
if (streamPushItem == null || streamPushItem.getGbId() == null) {
|
||||
GbStream gbStream = storager.getGbStream(app, streamId);
|
||||
gbStreams.add(gbStream);
|
||||
}else {
|
||||
if (streamPushItem.getGbId() != null) {
|
||||
gbStreams.add(streamPushItem);
|
||||
}
|
||||
}
|
||||
if (gbStreams.size() > 0) {
|
||||
// List<GbStream> gbStreams = new ArrayList<>();
|
||||
// if (streamPushItem == null || streamPushItem.getGbId() == null) {
|
||||
// GbStream gbStream = storager.getGbStream(app, streamId);
|
||||
// gbStreams.add(gbStream);
|
||||
// }else {
|
||||
// if (streamPushItem.getGbId() != null) {
|
||||
// gbStreams.add(streamPushItem);
|
||||
// }
|
||||
// }
|
||||
// if (gbStreams.size() > 0) {
|
||||
// eventPublisher.catalogEventPublishForStream(null, gbStreams, CatalogEvent.ON);
|
||||
}
|
||||
// }
|
||||
|
||||
}else {
|
||||
// 兼容流注销时类型从redis记录获取
|
||||
|
||||
@@ -24,6 +24,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class ZLMMediaListManager {
|
||||
|
||||
@@ -147,7 +150,6 @@ public class ZLMMediaListManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
// StreamProxyItem streamProxyItem = gbStreamMapper.selectOne(transform.getApp(), transform.getStream());
|
||||
List<GbStream> gbStreamList = gbStreamMapper.selectByGBId(transform.getGbId());
|
||||
if (gbStreamList != null && gbStreamList.size() == 1) {
|
||||
transform.setGbStreamId(gbStreamList.get(0).getGbStreamId());
|
||||
@@ -162,13 +164,12 @@ public class ZLMMediaListManager {
|
||||
}
|
||||
if (transform != null) {
|
||||
if (channelOnlineEvents.get(transform.getGbId()) != null) {
|
||||
channelOnlineEvents.get(transform.getGbId()).run(transform.getApp(), transform.getStream());
|
||||
channelOnlineEvents.get(transform.getGbId()).run(transform.getApp(), transform.getStream(), transform.getServerId());
|
||||
channelOnlineEvents.remove(transform.getGbId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
storager.updateMedia(transform);
|
||||
return transform;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -28,6 +29,9 @@ public class ZLMRESTfulUtils {
|
||||
|
||||
private OkHttpClient getClient(){
|
||||
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
|
||||
//todo 暂时写死超时时间 均为5s
|
||||
httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS); //设置连接超时时间
|
||||
httpClientBuilder.readTimeout(5,TimeUnit.SECONDS); //设置读取超时时间
|
||||
if (logger.isDebugEnabled()) {
|
||||
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> {
|
||||
logger.debug("http请求参数:" + message);
|
||||
@@ -47,7 +51,10 @@ public class ZLMRESTfulUtils {
|
||||
return null;
|
||||
}
|
||||
String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
|
||||
JSONObject responseJSON = null;
|
||||
JSONObject responseJSON = new JSONObject();
|
||||
//-2自定义流媒体 调用错误码
|
||||
responseJSON.put("code",-2);
|
||||
responseJSON.put("msg","流媒体调用失败");
|
||||
|
||||
FormBody.Builder builder = new FormBody.Builder();
|
||||
builder.add("secret",mediaServerItem.getSecret());
|
||||
@@ -78,11 +85,20 @@ public class ZLMRESTfulUtils {
|
||||
response.close();
|
||||
Objects.requireNonNull(response.body()).close();
|
||||
}
|
||||
} catch (ConnectException e) {
|
||||
logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage()));
|
||||
logger.info("请检查media配置并确认ZLM已启动...");
|
||||
}catch (IOException e) {
|
||||
logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage()));
|
||||
|
||||
if(e instanceof SocketTimeoutException){
|
||||
//读取超时超时异常
|
||||
logger.error(String.format("读取ZLM数据失败: %s, %s", url, e.getMessage()));
|
||||
}
|
||||
if(e instanceof ConnectException){
|
||||
//判断连接异常,我这里是报Failed to connect to 10.7.5.144
|
||||
logger.error(String.format("连接ZLM失败: %s, %s", url, e.getMessage()));
|
||||
}
|
||||
|
||||
}catch (Exception e){
|
||||
logger.error(String.format("访问ZLM失败: %s, %s", url, e.getMessage()));
|
||||
}
|
||||
}else {
|
||||
client.newCall(request).enqueue(new Callback(){
|
||||
@@ -105,8 +121,16 @@ public class ZLMRESTfulUtils {
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Call call, @NotNull IOException e) {
|
||||
logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage()));
|
||||
logger.info("请检查media配置并确认ZLM已启动...");
|
||||
logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage()));
|
||||
|
||||
if(e instanceof SocketTimeoutException){
|
||||
//读取超时超时异常
|
||||
logger.error(String.format("读取ZLM数据失败: %s, %s", call.request().toString(), e.getMessage()));
|
||||
}
|
||||
if(e instanceof ConnectException){
|
||||
//判断连接异常,我这里是报Failed to connect to 10.7.5.144
|
||||
logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -151,7 +175,7 @@ public class ZLMRESTfulUtils {
|
||||
}
|
||||
|
||||
}
|
||||
File snapFile = new File(targetPath + "/" + fileName);
|
||||
File snapFile = new File(targetPath + File.separator + fileName);
|
||||
FileOutputStream outStream = new FileOutputStream(snapFile);
|
||||
|
||||
outStream.write(Objects.requireNonNull(response.body()).bytes());
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.media.zlm;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import org.slf4j.Logger;
|
||||
@@ -20,6 +21,9 @@ public class ZLMRTPServerFactory {
|
||||
@Autowired
|
||||
private ZLMRESTfulUtils zlmresTfulUtils;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
private int[] portRangeArray = new int[2];
|
||||
|
||||
public int getFreePort(MediaServerItem mediaServerItem, int startPort, int endPort, List<Integer> usedFreelist) {
|
||||
@@ -87,10 +91,15 @@ public class ZLMRTPServerFactory {
|
||||
int result = -1;
|
||||
// 查询此rtp server 是否已经存在
|
||||
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId);
|
||||
if (rtpInfo != null && rtpInfo.getInteger("code") == 0 && rtpInfo.getBoolean("exist")) {
|
||||
result = rtpInfo.getInteger("local_port");
|
||||
if(rtpInfo.getInteger("code") == 0){
|
||||
if (rtpInfo.getBoolean("exist")) {
|
||||
result = rtpInfo.getInteger("local_port");
|
||||
return result;
|
||||
}
|
||||
}else if(rtpInfo.getInteger("code") == -2){
|
||||
return result;
|
||||
}
|
||||
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
// 推流端口设置0则使用随机端口
|
||||
param.put("enable_tcp", 1);
|
||||
@@ -197,6 +206,7 @@ public class ZLMRTPServerFactory {
|
||||
sendRtpItem.setTcp(tcp);
|
||||
sendRtpItem.setApp("rtp");
|
||||
sendRtpItem.setLocalPort(localPort);
|
||||
sendRtpItem.setServerId(userSetting.getServerId());
|
||||
sendRtpItem.setMediaServerId(serverItem.getId());
|
||||
return sendRtpItem;
|
||||
}
|
||||
@@ -238,6 +248,7 @@ public class ZLMRTPServerFactory {
|
||||
sendRtpItem.setChannelId(channelId);
|
||||
sendRtpItem.setTcp(tcp);
|
||||
sendRtpItem.setLocalPort(localPort);
|
||||
sendRtpItem.setServerId(userSetting.getServerId());
|
||||
sendRtpItem.setMediaServerId(serverItem.getId());
|
||||
return sendRtpItem;
|
||||
}
|
||||
@@ -279,10 +290,10 @@ public class ZLMRTPServerFactory {
|
||||
*/
|
||||
public int totalReaderCount(MediaServerItem mediaServerItem, String app, String streamId) {
|
||||
JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId);
|
||||
Integer code = mediaInfo.getInteger("code");
|
||||
if (mediaInfo == null) {
|
||||
return 0;
|
||||
}
|
||||
Integer code = mediaInfo.getInteger("code");
|
||||
if ( code < 0) {
|
||||
logger.warn("查询流({}/{})是否有其它观看者时得到: {}", app, streamId, mediaInfo.getString("msg"));
|
||||
return -1;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.genersoft.iot.vmp.media.zlm.dto;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
public interface ChannelOnlineEvent {
|
||||
|
||||
void run(String app, String stream);
|
||||
void run(String app, String stream, String serverId);
|
||||
}
|
||||
|
||||
@@ -61,10 +61,15 @@ public class MediaItem {
|
||||
private String originUrl;
|
||||
|
||||
/**
|
||||
* 服务器id
|
||||
* 流媒体服务器id
|
||||
*/
|
||||
private String mediaServerId;
|
||||
|
||||
/**
|
||||
* 服务器id
|
||||
*/
|
||||
private String severId;
|
||||
|
||||
/**
|
||||
* GMT unix系统时间戳,单位秒
|
||||
*/
|
||||
@@ -414,4 +419,12 @@ public class MediaItem {
|
||||
public void setStreamInfo(StreamInfo streamInfo) {
|
||||
this.streamInfo = streamInfo;
|
||||
}
|
||||
|
||||
public String getSeverId() {
|
||||
return severId;
|
||||
}
|
||||
|
||||
public void setSeverId(String severId) {
|
||||
this.severId = severId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,11 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
|
||||
*/
|
||||
private String mediaServerId;
|
||||
|
||||
/**
|
||||
* 使用的服务ID
|
||||
*/
|
||||
private String serverId;
|
||||
|
||||
public String getVhost() {
|
||||
return vhost;
|
||||
}
|
||||
@@ -219,5 +224,13 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
|
||||
public void setMediaServerId(String mediaServerId) {
|
||||
this.mediaServerId = mediaServerId;
|
||||
}
|
||||
|
||||
public String getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public void setServerId(String serverId) {
|
||||
this.serverId = serverId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,13 +61,12 @@ public class ZLMKeepliveTimeoutListener extends RedisKeyExpirationEventMessageLi
|
||||
// 发起http请求验证zlm是否确实无法连接,如果确实无法连接则发送离线事件,否则不作处理
|
||||
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
|
||||
JSONObject mediaServerConfig = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
|
||||
if (mediaServerConfig == null) {
|
||||
publisher.zlmOfflineEventPublish(mediaServerId);
|
||||
}else {
|
||||
if (mediaServerConfig != null && mediaServerConfig.getInteger("code") == 0) {
|
||||
logger.info("[zlm心跳到期]:{}验证后zlm仍在线,恢复心跳信息", mediaServerId);
|
||||
// 添加zlm信息
|
||||
mediaServerService.updateMediaServerKeepalive(mediaServerId, mediaServerConfig);
|
||||
}else {
|
||||
publisher.zlmOfflineEventPublish(mediaServerId);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class ZLMStatusEventListener {
|
||||
logger.info("[ZLM] 上线 ID:" + event.getMediaServerId());
|
||||
streamPushService.zlmServerOnline(event.getMediaServerId());
|
||||
streamProxyService.zlmServerOnline(event.getMediaServerId());
|
||||
|
||||
playService.zlmServerOnline(event.getMediaServerId());
|
||||
}
|
||||
|
||||
@Async
|
||||
|
||||
@@ -42,6 +42,8 @@ public interface IPlayService {
|
||||
|
||||
StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream);
|
||||
|
||||
void zlmServerOnline(String mediaServerId);
|
||||
|
||||
void audioBroadcast(Device device, String channelId, int timeout, AudioBroadcastEvent event);
|
||||
void stopAudioBroadcast(String deviceId, String channelId);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ public class StreamGPSSubscribeTask {
|
||||
private IVideoManagerStorage storager;
|
||||
|
||||
|
||||
|
||||
@Scheduled(fixedRate = 30 * 1000) //每30秒执行一次
|
||||
public void execute(){
|
||||
List<GPSMsgInfo> gpsMsgInfo = redisCatchStorage.getAllGpsMsgInfo();
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package com.genersoft.iot.vmp.service.bean;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 当上级平台
|
||||
* @author lin
|
||||
*/
|
||||
public class MessageForPushChannel {
|
||||
/**
|
||||
@@ -45,6 +48,20 @@ public class MessageForPushChannel {
|
||||
*/
|
||||
private String mediaServerId;
|
||||
|
||||
public static MessageForPushChannel getInstance(int type, String app, String stream, String gbId,
|
||||
String platFormId, String platFormName, String serverId,
|
||||
String mediaServerId){
|
||||
MessageForPushChannel messageForPushChannel = new MessageForPushChannel();
|
||||
messageForPushChannel.setType(type);
|
||||
messageForPushChannel.setGbId(gbId);
|
||||
messageForPushChannel.setApp(app);
|
||||
messageForPushChannel.setStream(stream);
|
||||
messageForPushChannel.setMediaServerId(mediaServerId);
|
||||
messageForPushChannel.setPlatFormId(platFormId);
|
||||
messageForPushChannel.setPlatFormName(platFormName);
|
||||
return messageForPushChannel;
|
||||
}
|
||||
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
package com.genersoft.iot.vmp.service.bean;
|
||||
|
||||
/**
|
||||
* redis消息:请求下级推送流信息
|
||||
* @author lin
|
||||
*/
|
||||
public class RequestPushStreamMsg {
|
||||
|
||||
|
||||
/**
|
||||
* 下级服务ID
|
||||
*/
|
||||
private String mediaServerId;
|
||||
|
||||
/**
|
||||
* 流ID
|
||||
*/
|
||||
private String app;
|
||||
|
||||
/**
|
||||
* 应用名
|
||||
*/
|
||||
private String stream;
|
||||
|
||||
/**
|
||||
* 目标IP
|
||||
*/
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
* 目标端口
|
||||
*/
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* ssrc
|
||||
*/
|
||||
private String ssrc;
|
||||
|
||||
/**
|
||||
* 是否使用TCP方式
|
||||
*/
|
||||
private boolean tcp;
|
||||
|
||||
/**
|
||||
* 本地使用的端口
|
||||
*/
|
||||
private int srcPort;
|
||||
|
||||
/**
|
||||
* 发送时,rtp的pt(uint8_t),不传时默认为96
|
||||
*/
|
||||
private int pt;
|
||||
|
||||
/**
|
||||
* 发送时,rtp的负载类型。为true时,负载为ps;为false时,为es;
|
||||
*/
|
||||
private boolean ps;
|
||||
|
||||
/**
|
||||
* 是否只有音频
|
||||
*/
|
||||
private boolean onlyAudio;
|
||||
|
||||
|
||||
public static RequestPushStreamMsg getInstance(String mediaServerId, String app, String stream, String ip, int port, String ssrc,
|
||||
boolean tcp, int srcPort, int pt, boolean ps, boolean onlyAudio) {
|
||||
RequestPushStreamMsg requestPushStreamMsg = new RequestPushStreamMsg();
|
||||
requestPushStreamMsg.setMediaServerId(mediaServerId);
|
||||
requestPushStreamMsg.setApp(app);
|
||||
requestPushStreamMsg.setStream(stream);
|
||||
requestPushStreamMsg.setIp(ip);
|
||||
requestPushStreamMsg.setPort(port);
|
||||
requestPushStreamMsg.setSsrc(ssrc);
|
||||
requestPushStreamMsg.setTcp(tcp);
|
||||
requestPushStreamMsg.setSrcPort(srcPort);
|
||||
requestPushStreamMsg.setPt(pt);
|
||||
requestPushStreamMsg.setPs(ps);
|
||||
requestPushStreamMsg.setOnlyAudio(onlyAudio);
|
||||
return requestPushStreamMsg;
|
||||
}
|
||||
|
||||
public String getMediaServerId() {
|
||||
return mediaServerId;
|
||||
}
|
||||
|
||||
public void setMediaServerId(String mediaServerId) {
|
||||
this.mediaServerId = mediaServerId;
|
||||
}
|
||||
|
||||
public String getApp() {
|
||||
return app;
|
||||
}
|
||||
|
||||
public void setApp(String app) {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
public String getStream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
public void setStream(String stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getSsrc() {
|
||||
return ssrc;
|
||||
}
|
||||
|
||||
public void setSsrc(String ssrc) {
|
||||
this.ssrc = ssrc;
|
||||
}
|
||||
|
||||
public boolean isTcp() {
|
||||
return tcp;
|
||||
}
|
||||
|
||||
public void setTcp(boolean tcp) {
|
||||
this.tcp = tcp;
|
||||
}
|
||||
|
||||
public int getSrcPort() {
|
||||
return srcPort;
|
||||
}
|
||||
|
||||
public void setSrcPort(int srcPort) {
|
||||
this.srcPort = srcPort;
|
||||
}
|
||||
|
||||
public int getPt() {
|
||||
return pt;
|
||||
}
|
||||
|
||||
public void setPt(int pt) {
|
||||
this.pt = pt;
|
||||
}
|
||||
|
||||
public boolean isPs() {
|
||||
return ps;
|
||||
}
|
||||
|
||||
public void setPs(boolean ps) {
|
||||
this.ps = ps;
|
||||
}
|
||||
|
||||
public boolean isOnlyAudio() {
|
||||
return onlyAudio;
|
||||
}
|
||||
|
||||
public void setOnlyAudio(boolean onlyAudio) {
|
||||
this.onlyAudio = onlyAudio;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package com.genersoft.iot.vmp.service.bean;
|
||||
|
||||
/**
|
||||
* redis消息:请求下级回复推送信息
|
||||
* @author lin
|
||||
*/
|
||||
public class RequestSendItemMsg {
|
||||
|
||||
/**
|
||||
* 下级服务ID
|
||||
*/
|
||||
private String serverId;
|
||||
|
||||
/**
|
||||
* 下级服务ID
|
||||
*/
|
||||
private String mediaServerId;
|
||||
|
||||
/**
|
||||
* 流ID
|
||||
*/
|
||||
private String app;
|
||||
|
||||
/**
|
||||
* 应用名
|
||||
*/
|
||||
private String stream;
|
||||
|
||||
/**
|
||||
* 目标IP
|
||||
*/
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
* 目标端口
|
||||
*/
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* ssrc
|
||||
*/
|
||||
private String ssrc;
|
||||
|
||||
/**
|
||||
* 平台国标编号
|
||||
*/
|
||||
private String platformId;
|
||||
|
||||
/**
|
||||
* 平台名称
|
||||
*/
|
||||
private String platformName;
|
||||
|
||||
/**
|
||||
* 通道ID
|
||||
*/
|
||||
private String channelId;
|
||||
|
||||
|
||||
/**
|
||||
* 是否使用TCP
|
||||
*/
|
||||
private Boolean isTcp;
|
||||
|
||||
|
||||
|
||||
|
||||
public static RequestSendItemMsg getInstance(String serverId, String mediaServerId, String app, String stream, String ip, int port,
|
||||
String ssrc, String platformId, String channelId, Boolean isTcp, String platformName) {
|
||||
RequestSendItemMsg requestSendItemMsg = new RequestSendItemMsg();
|
||||
requestSendItemMsg.setServerId(serverId);
|
||||
requestSendItemMsg.setMediaServerId(mediaServerId);
|
||||
requestSendItemMsg.setApp(app);
|
||||
requestSendItemMsg.setStream(stream);
|
||||
requestSendItemMsg.setIp(ip);
|
||||
requestSendItemMsg.setPort(port);
|
||||
requestSendItemMsg.setSsrc(ssrc);
|
||||
requestSendItemMsg.setPlatformId(platformId);
|
||||
requestSendItemMsg.setPlatformName(platformName);
|
||||
requestSendItemMsg.setChannelId(channelId);
|
||||
requestSendItemMsg.setTcp(isTcp);
|
||||
|
||||
return requestSendItemMsg;
|
||||
}
|
||||
|
||||
public String getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public void setServerId(String serverId) {
|
||||
this.serverId = serverId;
|
||||
}
|
||||
|
||||
public String getMediaServerId() {
|
||||
return mediaServerId;
|
||||
}
|
||||
|
||||
public void setMediaServerId(String mediaServerId) {
|
||||
this.mediaServerId = mediaServerId;
|
||||
}
|
||||
|
||||
public String getApp() {
|
||||
return app;
|
||||
}
|
||||
|
||||
public void setApp(String app) {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
public String getStream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
public void setStream(String stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getSsrc() {
|
||||
return ssrc;
|
||||
}
|
||||
|
||||
public void setSsrc(String ssrc) {
|
||||
this.ssrc = ssrc;
|
||||
}
|
||||
|
||||
public String getPlatformId() {
|
||||
return platformId;
|
||||
}
|
||||
|
||||
public void setPlatformId(String platformId) {
|
||||
this.platformId = platformId;
|
||||
}
|
||||
|
||||
public String getPlatformName() {
|
||||
return platformName;
|
||||
}
|
||||
|
||||
public void setPlatformName(String platformName) {
|
||||
this.platformName = platformName;
|
||||
}
|
||||
|
||||
public String getChannelId() {
|
||||
return channelId;
|
||||
}
|
||||
|
||||
public void setChannelId(String channelId) {
|
||||
this.channelId = channelId;
|
||||
}
|
||||
|
||||
public Boolean getTcp() {
|
||||
return isTcp;
|
||||
}
|
||||
|
||||
public void setTcp(Boolean tcp) {
|
||||
isTcp = tcp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.genersoft.iot.vmp.service.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
|
||||
/**
|
||||
* redis消息:下级回复推送信息
|
||||
* @author lin
|
||||
*/
|
||||
public class ResponseSendItemMsg {
|
||||
|
||||
private SendRtpItem sendRtpItem;
|
||||
|
||||
private MediaServerItem mediaServerItem;
|
||||
|
||||
public SendRtpItem getSendRtpItem() {
|
||||
return sendRtpItem;
|
||||
}
|
||||
|
||||
public void setSendRtpItem(SendRtpItem sendRtpItem) {
|
||||
this.sendRtpItem = sendRtpItem;
|
||||
}
|
||||
|
||||
public MediaServerItem getMediaServerItem() {
|
||||
return mediaServerItem;
|
||||
}
|
||||
|
||||
public void setMediaServerItem(MediaServerItem mediaServerItem) {
|
||||
this.mediaServerItem = mediaServerItem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.genersoft.iot.vmp.service.bean;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
public class WvpRedisMsg {
|
||||
|
||||
public static WvpRedisMsg getInstance(String fromId, String toId, String type, String cmd, String serial, String content){
|
||||
WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
|
||||
wvpRedisMsg.setFromId(fromId);
|
||||
wvpRedisMsg.setToId(toId);
|
||||
wvpRedisMsg.setType(type);
|
||||
wvpRedisMsg.setCmd(cmd);
|
||||
wvpRedisMsg.setSerial(serial);
|
||||
wvpRedisMsg.setContent(content);
|
||||
return wvpRedisMsg;
|
||||
}
|
||||
|
||||
private String fromId;
|
||||
|
||||
private String toId;
|
||||
/**
|
||||
* req 请求, res 回复
|
||||
*/
|
||||
private String type;
|
||||
private String cmd;
|
||||
|
||||
/**
|
||||
* 消息的ID
|
||||
*/
|
||||
private String serial;
|
||||
private Object content;
|
||||
|
||||
private final static String requestTag = "req";
|
||||
private final static String responseTag = "res";
|
||||
|
||||
public static WvpRedisMsg getRequestInstance(String fromId, String toId, String cmd, String serial, Object content) {
|
||||
WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
|
||||
wvpRedisMsg.setType(requestTag);
|
||||
wvpRedisMsg.setFromId(fromId);
|
||||
wvpRedisMsg.setToId(toId);
|
||||
wvpRedisMsg.setCmd(cmd);
|
||||
wvpRedisMsg.setSerial(serial);
|
||||
wvpRedisMsg.setContent(content);
|
||||
return wvpRedisMsg;
|
||||
}
|
||||
|
||||
public static WvpRedisMsg getResponseInstance() {
|
||||
WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
|
||||
wvpRedisMsg.setType(responseTag);
|
||||
return wvpRedisMsg;
|
||||
}
|
||||
|
||||
public static WvpRedisMsg getResponseInstance(String fromId, String toId, String cmd, String serial, Object content) {
|
||||
WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
|
||||
wvpRedisMsg.setType(responseTag);
|
||||
wvpRedisMsg.setFromId(fromId);
|
||||
wvpRedisMsg.setToId(toId);
|
||||
wvpRedisMsg.setCmd(cmd);
|
||||
wvpRedisMsg.setSerial(serial);
|
||||
wvpRedisMsg.setContent(content);
|
||||
return wvpRedisMsg;
|
||||
}
|
||||
|
||||
public static boolean isRequest(WvpRedisMsg wvpRedisMsg) {
|
||||
return requestTag.equals(wvpRedisMsg.getType());
|
||||
}
|
||||
|
||||
public String getSerial() {
|
||||
return serial;
|
||||
}
|
||||
|
||||
public void setSerial(String serial) {
|
||||
this.serial = serial;
|
||||
}
|
||||
|
||||
public String getFromId() {
|
||||
return fromId;
|
||||
}
|
||||
|
||||
public void setFromId(String fromId) {
|
||||
this.fromId = fromId;
|
||||
}
|
||||
|
||||
public String getToId() {
|
||||
return toId;
|
||||
}
|
||||
|
||||
public void setToId(String toId) {
|
||||
this.toId = toId;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getCmd() {
|
||||
return cmd;
|
||||
}
|
||||
|
||||
public void setCmd(String cmd) {
|
||||
this.cmd = cmd;
|
||||
}
|
||||
|
||||
public Object getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(Object content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.genersoft.iot.vmp.service.bean;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
|
||||
public class WvpRedisMsgCmd {
|
||||
|
||||
public static final String GET_SEND_ITEM = "GetSendItem";
|
||||
public static final String REQUEST_PUSH_STREAM = "RequestPushStream";
|
||||
|
||||
}
|
||||
@@ -2,16 +2,21 @@ package com.genersoft.iot.vmp.service.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.DynamicTask;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
|
||||
import com.genersoft.iot.vmp.service.IDeviceService;
|
||||
import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask;
|
||||
import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SyncStatus;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
|
||||
import com.genersoft.iot.vmp.storager.dao.DeviceMapper;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import org.slf4j.Logger;
|
||||
@@ -49,6 +54,12 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
@Autowired
|
||||
private DeviceMapper deviceMapper;
|
||||
|
||||
@Autowired
|
||||
private DeviceChannelMapper deviceChannelMapper;
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storage;
|
||||
|
||||
@Autowired
|
||||
private ISIPCommander commander;
|
||||
|
||||
@@ -68,7 +79,6 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (deviceInRedis != null && deviceInDb == null) {
|
||||
// redis 存在脏数据
|
||||
redisCatchStorage.clearCatchByDeviceId(device.getDeviceId());
|
||||
|
||||
}
|
||||
device.setUpdateTime(now);
|
||||
device.setOnline(1);
|
||||
@@ -77,13 +87,15 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (device.getCreateTime() == null) {
|
||||
device.setCreateTime(now);
|
||||
logger.info("[设备上线,首次注册]: {},查询设备信息以及通道信息", device.getDeviceId());
|
||||
deviceMapper.add(device);
|
||||
redisCatchStorage.updateDevice(device);
|
||||
commander.deviceInfoQuery(device);
|
||||
sync(device);
|
||||
deviceMapper.add(device);
|
||||
}else {
|
||||
deviceMapper.update(device);
|
||||
redisCatchStorage.updateDevice(device);
|
||||
}
|
||||
redisCatchStorage.updateDevice(device);
|
||||
|
||||
// 上线添加订阅
|
||||
if (device.getSubscribeCycleForCatalog() > 0) {
|
||||
// 查询在线设备那些开启了订阅,为设备开启定时的目录订阅
|
||||
@@ -94,7 +106,6 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
}
|
||||
// 刷新过期任务
|
||||
String registerExpireTaskKey = registerExpireTaskKeyPrefix + device.getDeviceId();
|
||||
dynamicTask.stop(registerExpireTaskKey);
|
||||
dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId()), device.getExpires() * 1000);
|
||||
}
|
||||
|
||||
@@ -143,8 +154,16 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (device == null || device.getSubscribeCycleForCatalog() < 0) {
|
||||
return false;
|
||||
}
|
||||
logger.info("移除目录订阅: {}", device.getDeviceId());
|
||||
dynamicTask.stop(device.getDeviceId() + "catalog");
|
||||
logger.info("[移除目录订阅]: {}", device.getDeviceId());
|
||||
String taskKey = device.getDeviceId() + "catalog";
|
||||
if (device.getOnline() == 1) {
|
||||
Runnable runnable = dynamicTask.get(taskKey);
|
||||
if (runnable instanceof ISubscribeTask) {
|
||||
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
|
||||
subscribeTask.stop();
|
||||
}
|
||||
}
|
||||
dynamicTask.stop(taskKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -168,8 +187,16 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (device == null || device.getSubscribeCycleForCatalog() < 0) {
|
||||
return false;
|
||||
}
|
||||
logger.info("移除移动位置订阅: {}", device.getDeviceId());
|
||||
dynamicTask.stop(device.getDeviceId() + "mobile_position");
|
||||
logger.info("[移除移动位置订阅]: {}", device.getDeviceId());
|
||||
String taskKey = device.getDeviceId() + "mobile_position";
|
||||
if (device.getOnline() == 1) {
|
||||
Runnable runnable = dynamicTask.get(taskKey);
|
||||
if (runnable instanceof ISubscribeTask) {
|
||||
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
|
||||
subscribeTask.stop();
|
||||
}
|
||||
}
|
||||
dynamicTask.stop(taskKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -275,6 +302,10 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
removeMobilePositionSubscribe(deviceInStore);
|
||||
}
|
||||
}
|
||||
// 坐标系变化,需要重新计算GCJ02坐标和WGS84坐标
|
||||
if (!deviceInStore.getGeoCoordSys().equals(device.getGeoCoordSys())) {
|
||||
updateDeviceChannelGeoCoordSys(device);
|
||||
}
|
||||
|
||||
String now = DateUtil.getNow();
|
||||
device.setUpdateTime(now);
|
||||
@@ -282,6 +313,32 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
device.setUpdateTime(DateUtil.getNow());
|
||||
if (deviceMapper.update(device) > 0) {
|
||||
redisCatchStorage.updateDevice(device);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新通道坐标系
|
||||
*/
|
||||
private void updateDeviceChannelGeoCoordSys(Device device) {
|
||||
List<DeviceChannel> deviceChannels = deviceChannelMapper.getAllChannelWithCoordinate(device.getDeviceId());
|
||||
if (deviceChannels.size() > 0) {
|
||||
for (DeviceChannel deviceChannel : deviceChannels) {
|
||||
if ("WGS84".equals(device.getGeoCoordSys())) {
|
||||
deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
|
||||
deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
|
||||
Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude());
|
||||
deviceChannel.setLongitudeGcj02(position[0]);
|
||||
deviceChannel.setLatitudeGcj02(position[1]);
|
||||
}else if ("GCJ02".equals(device.getGeoCoordSys())) {
|
||||
deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude());
|
||||
deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude());
|
||||
Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude());
|
||||
deviceChannel.setLongitudeWgs84(position[0]);
|
||||
deviceChannel.setLatitudeWgs84(position[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
storage.updateChannels(device.getDeviceId(), deviceChannels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,8 @@ public class GbStreamServiceImpl implements IGbStreamService {
|
||||
deviceChannel.setStatus(1);
|
||||
deviceChannel.setParentId(catalogId ==null?gbStream.getCatalogId():catalogId);
|
||||
deviceChannel.setRegisterWay(1);
|
||||
if (catalogId.length() <= 10) { // 父节点是行政区划,则设置CivilCode使用此行政区划
|
||||
if (catalogId.length() > 0 && catalogId.length() <= 10) {
|
||||
// 父节点是行政区划,则设置CivilCode使用此行政区划
|
||||
deviceChannel.setCivilCode(catalogId);
|
||||
}else {
|
||||
deviceChannel.setCivilCode(platform.getAdministrativeDivision());
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSONObject;
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
|
||||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||
import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
@@ -35,7 +36,9 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 媒体服务器节点管理
|
||||
@@ -189,6 +192,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
public void clearRTPServer(MediaServerItem mediaServerItem) {
|
||||
mediaServerItem.setSsrcConfig(new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getDomain()));
|
||||
redisUtil.zAdd(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(), mediaServerItem.getId(), 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -229,11 +233,10 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
}
|
||||
result.sort((serverItem1, serverItem2)->{
|
||||
int sortResult = 0;
|
||||
try {
|
||||
sortResult = DateUtil.format.parse(serverItem1.getCreateTime()).compareTo(DateUtil.format.parse(serverItem2.getCreateTime()));
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
LocalDateTime localDateTime1 = LocalDateTime.parse(serverItem1.getCreateTime(), DateUtil.formatter);
|
||||
LocalDateTime localDateTime2 = LocalDateTime.parse(serverItem2.getCreateTime(), DateUtil.formatter);
|
||||
|
||||
sortResult = localDateTime1.compareTo(localDateTime2);
|
||||
return sortResult;
|
||||
});
|
||||
return result;
|
||||
@@ -495,14 +498,14 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
|
||||
param.put("ffmpeg.cmd","%s -fflags nobuffer -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s");
|
||||
param.put("hook.enable","1");
|
||||
param.put("hook.on_flow_report","");
|
||||
param.put("hook.on_flow_report",String.format("%s/on_flow_report", hookPrex));
|
||||
param.put("hook.on_play",String.format("%s/on_play", hookPrex));
|
||||
param.put("hook.on_http_access","");
|
||||
param.put("hook.on_http_access",String.format("%s/on_http_access", hookPrex));
|
||||
param.put("hook.on_publish", String.format("%s/on_publish", hookPrex));
|
||||
param.put("hook.on_record_mp4",recordHookPrex != null? String.format("%s/on_record_mp4", recordHookPrex): "");
|
||||
param.put("hook.on_record_ts","");
|
||||
param.put("hook.on_rtsp_auth","");
|
||||
param.put("hook.on_rtsp_realm","");
|
||||
param.put("hook.on_record_ts",String.format("%s/on_record_ts", hookPrex));
|
||||
param.put("hook.on_rtsp_auth",String.format("%s/on_rtsp_auth", hookPrex));
|
||||
param.put("hook.on_rtsp_realm",String.format("%s/on_rtsp_realm", hookPrex));
|
||||
param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex));
|
||||
param.put("hook.on_shell_login",String.format("%s/on_shell_login", hookPrex));
|
||||
param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrex));
|
||||
|
||||
@@ -21,6 +21,8 @@ import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.IMediaService;
|
||||
import com.genersoft.iot.vmp.service.IPlayService;
|
||||
import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
|
||||
import com.genersoft.iot.vmp.service.bean.PlayBackCallback;
|
||||
import com.genersoft.iot.vmp.service.bean.PlayBackResult;
|
||||
@@ -32,8 +34,6 @@ import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
|
||||
import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
|
||||
import com.genersoft.iot.vmp.service.IMediaService;
|
||||
import com.genersoft.iot.vmp.service.IPlayService;
|
||||
import gov.nist.javax.sip.stack.SIPDialog;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -49,6 +49,7 @@ import javax.sip.SipException;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -74,9 +75,6 @@ public class PlayServiceImpl implements IPlayService {
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private RedisUtil redis;
|
||||
|
||||
@Autowired
|
||||
private DeferredResultHolder resultHolder;
|
||||
|
||||
@@ -140,36 +138,19 @@ public class PlayServiceImpl implements IPlayService {
|
||||
result.onCompletion(()->{
|
||||
// 点播结束时调用截图接口
|
||||
// TODO 应该在上流时调用更好,结束也可能是错误结束
|
||||
try {
|
||||
String classPath = ResourceUtils.getURL("classpath:").getPath();
|
||||
// 兼容打包为jar的class路径
|
||||
if(classPath.contains("jar")) {
|
||||
classPath = classPath.substring(0, classPath.lastIndexOf("."));
|
||||
classPath = classPath.substring(0, classPath.lastIndexOf("/") + 1);
|
||||
String path = "static/static/snap/";
|
||||
String fileName = deviceId + "_" + channelId + ".jpg";
|
||||
ResponseEntity responseEntity = (ResponseEntity)result.getResult();
|
||||
if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
|
||||
WVPResult wvpResult = (WVPResult)responseEntity.getBody();
|
||||
if (Objects.requireNonNull(wvpResult).getCode() == 0) {
|
||||
StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();
|
||||
MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
|
||||
String streamUrl = streamInfoForSuccess.getFmp4();
|
||||
// 请求截图
|
||||
logger.info("[请求截图]: " + fileName);
|
||||
zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName);
|
||||
}
|
||||
if (classPath.startsWith("file:")) {
|
||||
classPath = classPath.substring(classPath.indexOf(":") + 1);
|
||||
}
|
||||
String path = classPath + "static/static/snap/";
|
||||
// 兼容Windows系统路径(去除前面的“/”)
|
||||
if(System.getProperty("os.name").contains("indows")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
String fileName = deviceId + "_" + channelId + ".jpg";
|
||||
ResponseEntity responseEntity = (ResponseEntity)result.getResult();
|
||||
if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
|
||||
WVPResult wvpResult = (WVPResult)responseEntity.getBody();
|
||||
if (Objects.requireNonNull(wvpResult).getCode() == 0) {
|
||||
StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();
|
||||
MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
|
||||
String streamUrl = streamInfoForSuccess.getFmp4();
|
||||
// 请求截图
|
||||
logger.info("[请求截图]: " + fileName);
|
||||
zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName);
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
if (streamInfo != null) {
|
||||
@@ -186,24 +167,33 @@ public class PlayServiceImpl implements IPlayService {
|
||||
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
|
||||
|
||||
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId);
|
||||
if (rtpInfo != null && rtpInfo.getBoolean("exist")) {
|
||||
if(rtpInfo.getInteger("code") == 0){
|
||||
if (rtpInfo.getBoolean("exist")) {
|
||||
|
||||
WVPResult wvpResult = new WVPResult();
|
||||
wvpResult.setCode(0);
|
||||
wvpResult.setMsg("success");
|
||||
wvpResult.setData(streamInfo);
|
||||
msg.setData(wvpResult);
|
||||
WVPResult wvpResult = new WVPResult();
|
||||
wvpResult.setCode(0);
|
||||
wvpResult.setMsg("success");
|
||||
wvpResult.setData(streamInfo);
|
||||
msg.setData(wvpResult);
|
||||
|
||||
resultHolder.invokeAllResult(msg);
|
||||
if (hookEvent != null) {
|
||||
hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo)));
|
||||
resultHolder.invokeAllResult(msg);
|
||||
if (hookEvent != null) {
|
||||
hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo)));
|
||||
}
|
||||
}else {
|
||||
redisCatchStorage.stopPlay(streamInfo);
|
||||
storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
|
||||
streamInfo = null;
|
||||
}
|
||||
}else {
|
||||
//zlm连接失败
|
||||
redisCatchStorage.stopPlay(streamInfo);
|
||||
storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
|
||||
streamInfo = null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (streamInfo == null) {
|
||||
String streamId = null;
|
||||
@@ -256,33 +246,41 @@ public class PlayServiceImpl implements IPlayService {
|
||||
if (ssrcInfo == null) {
|
||||
ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false);
|
||||
}
|
||||
|
||||
logger.info("[点播开始] deviceId: {}, channelId: {}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getSsrc() );
|
||||
// 超时处理
|
||||
String timeOutTaskKey = UUID.randomUUID().toString();
|
||||
SSRCInfo finalSsrcInfo = ssrcInfo;
|
||||
dynamicTask.startDelay( timeOutTaskKey,()->{
|
||||
logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", device.getDeviceId(), channelId));
|
||||
|
||||
SIPDialog dialog = streamSession.getDialogByStream(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
|
||||
if (dialog != null) {
|
||||
logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
|
||||
timeoutCallback.run(1, "收流超时");
|
||||
// 点播超时回复BYE 同时释放ssrc以及此次点播的资源
|
||||
cmder.streamByeCmd(device.getDeviceId(), channelId, finalSsrcInfo.getStream(), null);
|
||||
}else {
|
||||
logger.info("[点播超时] 消息未响应 deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
|
||||
timeoutCallback.run(0, "点播超时");
|
||||
mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
|
||||
mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
|
||||
streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
|
||||
}
|
||||
}, userSetting.getPlayTimeout()*1000);
|
||||
}, userSetting.getPlayTimeout());
|
||||
final String ssrc = ssrcInfo.getSsrc();
|
||||
final String stream = ssrcInfo.getStream();
|
||||
//端口获取失败的ssrcInfo 没有必要发送点播指令
|
||||
if(ssrcInfo.getPort() <= 0){
|
||||
logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo);
|
||||
return;
|
||||
}
|
||||
cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
|
||||
logger.info("收到订阅消息: " + response.toJSONString());
|
||||
dynamicTask.stop(timeOutTaskKey);
|
||||
// hook响应
|
||||
onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid);
|
||||
hookEvent.response(mediaServerItemInuse, response);
|
||||
logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
|
||||
|
||||
}, (event) -> {
|
||||
ResponseEvent responseEvent = (ResponseEvent)event.event;
|
||||
String contentString = new String(responseEvent.getResponse().getRawContent());
|
||||
@@ -296,8 +294,10 @@ public class PlayServiceImpl implements IPlayService {
|
||||
if (ssrc.equals(ssrcInResponse)) {
|
||||
return;
|
||||
}
|
||||
logger.info("[SIP 消息] 收到invite 200, 发现下级自定义了ssrc 开启修正");
|
||||
logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse );
|
||||
if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
|
||||
logger.info("[SIP 消息] SSRC修正 {}->{}", ssrc, ssrcInResponse);
|
||||
|
||||
if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
|
||||
// ssrc 不可用
|
||||
// 释放ssrc
|
||||
@@ -450,7 +450,7 @@ public class PlayServiceImpl implements IPlayService {
|
||||
cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
|
||||
// 回复之前所有的点播请求
|
||||
playBackCallback.call(playBackResult);
|
||||
}, userSetting.getPlayTimeout()*1000);
|
||||
}, userSetting.getPlayTimeout());
|
||||
|
||||
cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack,
|
||||
(InviteStreamInfo inviteStreamInfo) -> {
|
||||
@@ -539,7 +539,7 @@ public class PlayServiceImpl implements IPlayService {
|
||||
cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
|
||||
// 回复之前所有的点播请求
|
||||
hookCallBack.call(downloadResult);
|
||||
}, userSetting.getPlayTimeout()*1000);
|
||||
}, userSetting.getPlayTimeout());
|
||||
cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack,
|
||||
inviteStreamInfo -> {
|
||||
logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
|
||||
@@ -605,7 +605,7 @@ public class PlayServiceImpl implements IPlayService {
|
||||
|
||||
BigDecimal currentCount = new BigDecimal(duration/1000);
|
||||
BigDecimal totalCount = new BigDecimal(end-start);
|
||||
BigDecimal divide = currentCount.divide(totalCount,2, BigDecimal.ROUND_HALF_UP);
|
||||
BigDecimal divide = currentCount.divide(totalCount,2, RoundingMode.HALF_UP);
|
||||
double process = divide.doubleValue();
|
||||
streamInfo.setProgress(process);
|
||||
}
|
||||
@@ -728,4 +728,9 @@ public class PlayServiceImpl implements IPlayService {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void zlmServerOnline(String mediaServerId) {
|
||||
// 似乎没啥需要做的
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
package com.genersoft.iot.vmp.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.genersoft.iot.vmp.conf.DynamicTask;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.bean.*;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.SipException;
|
||||
import java.text.ParseException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
/**
|
||||
* 监听下级发送推送信息,并发送国标推流消息上级
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class RedisGbPlayMsgListener implements MessageListener {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(RedisGbPlayMsgListener.class);
|
||||
|
||||
public static final String WVP_PUSH_STREAM_KEY = "WVP_PUSH_STREAM";
|
||||
|
||||
/**
|
||||
* 流媒体不存在的错误玛
|
||||
*/
|
||||
public static final int ERROR_CODE_MEDIA_SERVER_NOT_FOUND = -1;
|
||||
|
||||
/**
|
||||
* 离线的错误玛
|
||||
*/
|
||||
public static final int ERROR_CODE_OFFLINE = -2;
|
||||
|
||||
/**
|
||||
* 超时的错误玛
|
||||
*/
|
||||
public static final int ERROR_CODE_TIMEOUT = -3;
|
||||
|
||||
private Map<String, PlayMsgCallback> callbacks = new ConcurrentHashMap<>();
|
||||
private Map<String, PlayMsgCallbackForStartSendRtpStream> callbacksForStartSendRtpStream = new ConcurrentHashMap<>();
|
||||
private Map<String, PlayMsgErrorCallback> callbacksForError = new ConcurrentHashMap<>();
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private RedisUtil redis;
|
||||
|
||||
@Autowired
|
||||
private ZLMMediaListManager zlmMediaListManager;
|
||||
|
||||
@Autowired
|
||||
private ZLMRTPServerFactory zlmrtpServerFactory;
|
||||
|
||||
@Autowired
|
||||
private IMediaServerService mediaServerService;
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private DynamicTask dynamicTask;
|
||||
|
||||
@Autowired
|
||||
private ZLMMediaListManager mediaListManager;
|
||||
|
||||
@Autowired
|
||||
private ZLMHttpHookSubscribe subscribe;
|
||||
|
||||
|
||||
public interface PlayMsgCallback{
|
||||
void handler(ResponseSendItemMsg responseSendItemMsg);
|
||||
}
|
||||
|
||||
public interface PlayMsgCallbackForStartSendRtpStream{
|
||||
void handler(JSONObject jsonObject);
|
||||
}
|
||||
|
||||
public interface PlayMsgErrorCallback{
|
||||
void handler(WVPResult wvpResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] bytes) {
|
||||
JSONObject msgJSON = JSON.parseObject(message.getBody(), JSONObject.class);
|
||||
WvpRedisMsg wvpRedisMsg = JSON.toJavaObject(msgJSON, WvpRedisMsg.class);
|
||||
if (!userSetting.getServerId().equals(wvpRedisMsg.getToId())) {
|
||||
return;
|
||||
}
|
||||
if (WvpRedisMsg.isRequest(wvpRedisMsg)) {
|
||||
logger.info("[收到REDIS通知] 请求: {}", new String(message.getBody()));
|
||||
|
||||
switch (wvpRedisMsg.getCmd()){
|
||||
case WvpRedisMsgCmd.GET_SEND_ITEM:
|
||||
RequestSendItemMsg content = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), RequestSendItemMsg.class);
|
||||
requestSendItemMsgHand(content, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial());
|
||||
break;
|
||||
case WvpRedisMsgCmd.REQUEST_PUSH_STREAM:
|
||||
RequestPushStreamMsg param = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), RequestPushStreamMsg.class);;
|
||||
requestPushStreamMsgHand(param, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}else {
|
||||
logger.info("[收到REDIS通知] 回复: {}", new String(message.getBody()));
|
||||
switch (wvpRedisMsg.getCmd()){
|
||||
case WvpRedisMsgCmd.GET_SEND_ITEM:
|
||||
|
||||
WVPResult content = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), WVPResult.class);
|
||||
|
||||
String key = wvpRedisMsg.getSerial();
|
||||
switch (content.getCode()) {
|
||||
case 0:
|
||||
ResponseSendItemMsg responseSendItemMsg =JSON.toJavaObject((JSONObject)content.getData(), ResponseSendItemMsg.class);
|
||||
PlayMsgCallback playMsgCallback = callbacks.get(key);
|
||||
if (playMsgCallback != null) {
|
||||
callbacksForError.remove(key);
|
||||
playMsgCallback.handler(responseSendItemMsg);
|
||||
}
|
||||
break;
|
||||
case ERROR_CODE_MEDIA_SERVER_NOT_FOUND:
|
||||
case ERROR_CODE_OFFLINE:
|
||||
case ERROR_CODE_TIMEOUT:
|
||||
PlayMsgErrorCallback errorCallback = callbacksForError.get(key);
|
||||
if (errorCallback != null) {
|
||||
callbacks.remove(key);
|
||||
errorCallback.handler(content);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WvpRedisMsgCmd.REQUEST_PUSH_STREAM:
|
||||
WVPResult wvpResult = JSON.toJavaObject((JSONObject)wvpRedisMsg.getContent(), WVPResult.class);
|
||||
String serial = wvpRedisMsg.getSerial();
|
||||
switch (wvpResult.getCode()) {
|
||||
case 0:
|
||||
JSONObject jsonObject = (JSONObject)wvpResult.getData();
|
||||
PlayMsgCallbackForStartSendRtpStream playMsgCallback = callbacksForStartSendRtpStream.get(serial);
|
||||
if (playMsgCallback != null) {
|
||||
callbacksForError.remove(serial);
|
||||
playMsgCallback.handler(jsonObject);
|
||||
}
|
||||
break;
|
||||
case ERROR_CODE_MEDIA_SERVER_NOT_FOUND:
|
||||
case ERROR_CODE_OFFLINE:
|
||||
case ERROR_CODE_TIMEOUT:
|
||||
PlayMsgErrorCallback errorCallback = callbacksForError.get(serial);
|
||||
if (errorCallback != null) {
|
||||
callbacks.remove(serial);
|
||||
errorCallback.handler(wvpResult);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理收到的请求推流的请求
|
||||
*/
|
||||
private void requestPushStreamMsgHand(RequestPushStreamMsg requestPushStreamMsg, String fromId, String serial) {
|
||||
MediaServerItem mediaInfo = mediaServerService.getOne(requestPushStreamMsg.getMediaServerId());
|
||||
if (mediaInfo == null) {
|
||||
// TODO 回复错误
|
||||
return;
|
||||
}
|
||||
String is_Udp = requestPushStreamMsg.isTcp() ? "0" : "1";
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
param.put("vhost","__defaultVhost__");
|
||||
param.put("app",requestPushStreamMsg.getApp());
|
||||
param.put("stream",requestPushStreamMsg.getStream());
|
||||
param.put("ssrc", requestPushStreamMsg.getSsrc());
|
||||
param.put("dst_url",requestPushStreamMsg.getIp());
|
||||
param.put("dst_port", requestPushStreamMsg.getPort());
|
||||
param.put("is_udp", is_Udp);
|
||||
param.put("src_port", requestPushStreamMsg.getSrcPort());
|
||||
param.put("pt", requestPushStreamMsg.getPt());
|
||||
param.put("use_ps", requestPushStreamMsg.isPs() ? "1" : "0");
|
||||
param.put("only_audio", requestPushStreamMsg.isOnlyAudio() ? "1" : "0");
|
||||
JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
|
||||
// 回复消息
|
||||
responsePushStream(jsonObject, fromId, serial);
|
||||
}
|
||||
|
||||
private void responsePushStream(JSONObject content, String toId, String serial) {
|
||||
|
||||
WVPResult<JSONObject> result = new WVPResult<>();
|
||||
result.setCode(0);
|
||||
result.setData(content);
|
||||
|
||||
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId,
|
||||
WvpRedisMsgCmd.REQUEST_PUSH_STREAM, serial, result);
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
|
||||
redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理收到的请求sendItem的请求
|
||||
*/
|
||||
private void requestSendItemMsgHand(RequestSendItemMsg content, String toId, String serial) {
|
||||
MediaServerItem mediaServerItem = mediaServerService.getOne(content.getMediaServerId());
|
||||
if (mediaServerItem == null) {
|
||||
logger.info("[回复推流信息] 流媒体{}不存在 ", content.getMediaServerId());
|
||||
|
||||
WVPResult<SendRtpItem> result = new WVPResult<>();
|
||||
result.setCode(ERROR_CODE_MEDIA_SERVER_NOT_FOUND);
|
||||
result.setMsg("流媒体不存在");
|
||||
|
||||
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId,
|
||||
WvpRedisMsgCmd.GET_SEND_ITEM, serial, result);
|
||||
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
|
||||
redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
return;
|
||||
}
|
||||
// 确定流是否在线
|
||||
boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, content.getApp(), content.getStream());
|
||||
if (streamReady) {
|
||||
logger.info("[回复推流信息] {}/{}", content.getApp(), content.getStream());
|
||||
responseSendItem(mediaServerItem, content, toId, serial);
|
||||
}else {
|
||||
// 流已经离线
|
||||
// 发送redis消息以使设备上线
|
||||
logger.info("[ app={}, stream={} ]通道离线,发送redis信息控制设备开始推流",content.getApp(), content.getStream());
|
||||
|
||||
String taskKey = UUID.randomUUID().toString();
|
||||
// 设置超时
|
||||
dynamicTask.startDelay(taskKey, ()->{
|
||||
logger.info("[ app={}, stream={} ] 等待设备开始推流超时", content.getApp(), content.getStream());
|
||||
WVPResult<SendRtpItem> result = new WVPResult<>();
|
||||
result.setCode(ERROR_CODE_TIMEOUT);
|
||||
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(
|
||||
userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result
|
||||
);
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
|
||||
redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}, userSetting.getPlatformPlayTimeout());
|
||||
|
||||
// 添加订阅
|
||||
JSONObject subscribeKey = new JSONObject();
|
||||
subscribeKey.put("app", content.getApp());
|
||||
subscribeKey.put("stream", content.getStream());
|
||||
subscribeKey.put("regist", true);
|
||||
subscribeKey.put("schema", "rtmp");
|
||||
subscribeKey.put("mediaServerId", mediaServerItem.getId());
|
||||
subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
|
||||
(MediaServerItem mediaServerItemInUse, JSONObject json)->{
|
||||
dynamicTask.stop(taskKey);
|
||||
responseSendItem(mediaServerItem, content, toId, serial);
|
||||
});
|
||||
|
||||
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, content.getApp(), content.getStream(),
|
||||
content.getChannelId(), content.getPlatformId(), content.getPlatformName(), content.getServerId(),
|
||||
content.getMediaServerId());
|
||||
redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将获取到的sendItem发送出去
|
||||
*/
|
||||
private void responseSendItem(MediaServerItem mediaServerItem, RequestSendItemMsg content, String toId, String serial) {
|
||||
SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, content.getIp(),
|
||||
content.getPort(), content.getSsrc(), content.getPlatformId(),
|
||||
content.getApp(), content.getStream(), content.getChannelId(),
|
||||
content.getTcp());
|
||||
|
||||
WVPResult<ResponseSendItemMsg> result = new WVPResult<>();
|
||||
result.setCode(0);
|
||||
ResponseSendItemMsg responseSendItemMsg = new ResponseSendItemMsg();
|
||||
responseSendItemMsg.setSendRtpItem(sendRtpItem);
|
||||
responseSendItemMsg.setMediaServerItem(mediaServerItem);
|
||||
result.setData(responseSendItemMsg);
|
||||
|
||||
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(
|
||||
userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result
|
||||
);
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
|
||||
redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息要求下级生成推流信息
|
||||
* @param serverId 下级服务ID
|
||||
* @param app 应用名
|
||||
* @param stream 流ID
|
||||
* @param ip 目标IP
|
||||
* @param port 目标端口
|
||||
* @param ssrc ssrc
|
||||
* @param platformId 平台国标编号
|
||||
* @param channelId 通道ID
|
||||
* @param isTcp 是否使用TCP
|
||||
* @param callback 得到信息的回调
|
||||
*/
|
||||
public void sendMsg(String serverId, String mediaServerId, String app, String stream, String ip, int port, String ssrc,
|
||||
String platformId, String channelId, boolean isTcp, String platformName, PlayMsgCallback callback, PlayMsgErrorCallback errorCallback) {
|
||||
RequestSendItemMsg requestSendItemMsg = RequestSendItemMsg.getInstance(
|
||||
serverId, mediaServerId, app, stream, ip, port, ssrc, platformId, channelId, isTcp, platformName);
|
||||
requestSendItemMsg.setServerId(serverId);
|
||||
String key = UUID.randomUUID().toString();
|
||||
WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId, WvpRedisMsgCmd.GET_SEND_ITEM,
|
||||
key, requestSendItemMsg);
|
||||
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
|
||||
logger.info("[请求推流SendItem] {}: {}", serverId, jsonObject);
|
||||
callbacks.put(key, callback);
|
||||
callbacksForError.put(key, errorCallback);
|
||||
dynamicTask.startDelay(key, ()->{
|
||||
callbacks.remove(key);
|
||||
callbacksForError.remove(key);
|
||||
WVPResult<Object> wvpResult = new WVPResult<>();
|
||||
wvpResult.setCode(ERROR_CODE_TIMEOUT);
|
||||
wvpResult.setMsg("timeout");
|
||||
errorCallback.handler(wvpResult);
|
||||
}, userSetting.getPlatformPlayTimeout());
|
||||
redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求推流的消息
|
||||
* @param param 推流参数
|
||||
* @param callback 回调
|
||||
*/
|
||||
public void sendMsgForStartSendRtpStream(String serverId, RequestPushStreamMsg param, PlayMsgCallbackForStartSendRtpStream callback) {
|
||||
String key = UUID.randomUUID().toString();
|
||||
WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId,
|
||||
WvpRedisMsgCmd.REQUEST_PUSH_STREAM, key, param);
|
||||
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
|
||||
logger.info("[REDIS 请求其他平台推流] {}: {}", serverId, jsonObject);
|
||||
dynamicTask.startDelay(key, ()->{
|
||||
callbacksForStartSendRtpStream.remove(key);
|
||||
callbacksForError.remove(key);
|
||||
}, userSetting.getPlatformPlayTimeout());
|
||||
callbacksForStartSendRtpStream.put(key, callback);
|
||||
callbacksForError.put(key, (wvpResult)->{
|
||||
logger.info("[REDIS 请求其他平台推流] 失败: {}", wvpResult.getMsg());
|
||||
callbacksForStartSendRtpStream.remove(key);
|
||||
callbacksForError.remove(key);
|
||||
});
|
||||
redis.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.service.impl;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -10,17 +11,23 @@ import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 接收来自redis的GPS更新通知
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class RedisGPSMsgListener implements MessageListener {
|
||||
public class RedisGpsMsgListener implements MessageListener {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(RedisGPSMsgListener.class);
|
||||
private final static Logger logger = LoggerFactory.getLogger(RedisGpsMsgListener.class);
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] bytes) {
|
||||
logger.info("收到来自REDIS的GPS通知: {}", new String(message.getBody()));
|
||||
public void onMessage(@NotNull Message message, byte[] bytes) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("收到来自REDIS的GPS通知: {}", new String(message.getBody()));
|
||||
}
|
||||
GPSMsgInfo gpsMsgInfo = JSON.parseObject(message.getBody(), GPSMsgInfo.class);
|
||||
redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.genersoft.iot.vmp.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.AlarmChannelMessage;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class RedisStreamMsgListener implements MessageListener {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(RedisStreamMsgListener.class);
|
||||
|
||||
@Autowired
|
||||
private ISIPCommander commander;
|
||||
|
||||
@Autowired
|
||||
private ISIPCommanderForPlatform commanderForPlatform;
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storage;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private ZLMMediaListManager zlmMediaListManager;
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] bytes) {
|
||||
|
||||
JSONObject steamMsgJson = JSON.parseObject(message.getBody(), JSONObject.class);
|
||||
if (steamMsgJson == null) {
|
||||
logger.warn("[REDIS的ALARM通知]消息解析失败");
|
||||
return;
|
||||
}
|
||||
String serverId = steamMsgJson.getString("serverId");
|
||||
|
||||
if (userSetting.getServerId().equals(serverId)) {
|
||||
// 自己发送的消息忽略即可
|
||||
return;
|
||||
}
|
||||
logger.info("[REDIS通知] 流变化: {}", new String(message.getBody()));
|
||||
String app = steamMsgJson.getString("app");
|
||||
String stream = steamMsgJson.getString("stream");
|
||||
boolean register = steamMsgJson.getBoolean("register");
|
||||
String mediaServerId = steamMsgJson.getString("mediaServerId");
|
||||
MediaItem mediaItem = new MediaItem();
|
||||
mediaItem.setSeverId(serverId);
|
||||
mediaItem.setApp(app);
|
||||
mediaItem.setStream(stream);
|
||||
mediaItem.setRegist(register);
|
||||
mediaItem.setMediaServerId(mediaServerId);
|
||||
mediaItem.setCreateStamp(System.currentTimeMillis()/1000);
|
||||
mediaItem.setAliveSecond(0L);
|
||||
mediaItem.setTotalReaderCount("0");
|
||||
mediaItem.setOriginType(0);
|
||||
mediaItem.setOriginTypeStr("0");
|
||||
mediaItem.setOriginTypeStr("unknown");
|
||||
|
||||
zlmMediaListManager.addPush(mediaItem);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -107,6 +107,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
|
||||
streamPushItem.setStatus(true);
|
||||
streamPushItem.setStreamType("push");
|
||||
streamPushItem.setVhost(item.getVhost());
|
||||
streamPushItem.setServerId(item.getSeverId());
|
||||
return streamPushItem;
|
||||
}
|
||||
|
||||
|
||||
@@ -356,6 +356,15 @@ public interface IVideoManagerStorage {
|
||||
int removeMedia(String app, String stream);
|
||||
|
||||
|
||||
/**
|
||||
* 获取但个推流
|
||||
* @param app
|
||||
* @param stream
|
||||
* @return
|
||||
*/
|
||||
StreamPushItem getMedia(String app, String stream);
|
||||
|
||||
|
||||
/**
|
||||
* 清空推流列表
|
||||
*/
|
||||
|
||||
@@ -17,10 +17,10 @@ public interface DeviceChannelMapper {
|
||||
|
||||
@Insert("INSERT INTO device_channel (channelId, deviceId, name, manufacture, model, owner, civilCode, block, " +
|
||||
"address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " +
|
||||
"ipAddress, port, password, PTZType, status, streamId, longitude, latitude, createTime, updateTime) " +
|
||||
"ipAddress, port, password, PTZType, status, streamId, longitude, latitude, longitudeGcj02, latitudeGcj02, longitudeWgs84, latitudeWgs84, createTime, updateTime) " +
|
||||
"VALUES ('${channelId}', '${deviceId}', '${name}', '${manufacture}', '${model}', '${owner}', '${civilCode}', '${block}'," +
|
||||
"'${address}', ${parental}, '${parentId}', ${safetyWay}, ${registerWay}, '${certNum}', ${certifiable}, ${errCode}, '${secrecy}', " +
|
||||
"'${ipAddress}', ${port}, '${password}', ${PTZType}, ${status}, '${streamId}', ${longitude}, ${latitude},'${createTime}', '${updateTime}')")
|
||||
"'${ipAddress}', ${port}, '${password}', ${PTZType}, ${status}, '${streamId}', ${longitude}, ${latitude}, ${longitudeGcj02}, ${latitudeGcj02}, ${longitudeWgs84}, ${latitudeWgs84},'${createTime}', '${updateTime}')")
|
||||
int add(DeviceChannel channel);
|
||||
|
||||
@Update(value = {" <script>" +
|
||||
@@ -50,6 +50,10 @@ public interface DeviceChannelMapper {
|
||||
"<if test='hasAudio != null'>, hasAudio=${hasAudio}</if>" +
|
||||
"<if test='longitude != null'>, longitude=${longitude}</if>" +
|
||||
"<if test='latitude != null'>, latitude=${latitude}</if>" +
|
||||
"<if test='longitudeGcj02 != null'>, longitudeGcj02=${longitudeGcj02}</if>" +
|
||||
"<if test='latitudeGcj02 != null'>, latitudeGcj02=${latitudeGcj02}</if>" +
|
||||
"<if test='longitudeWgs84 != null'>, longitudeWgs84=${longitudeWgs84}</if>" +
|
||||
"<if test='latitudeWgs84 != null'>, latitudeWgs84=${latitudeWgs84}</if>" +
|
||||
"WHERE deviceId='${deviceId}' AND channelId='${channelId}'"+
|
||||
" </script>"})
|
||||
int update(DeviceChannel channel);
|
||||
@@ -67,7 +71,7 @@ public interface DeviceChannelMapper {
|
||||
" <if test='online == false' > AND dc.status=0</if>" +
|
||||
" <if test='hasSubChannel == true' > AND dc.subCount > 0 </if>" +
|
||||
" <if test='hasSubChannel == false' > AND dc.subCount = 0 </if>" +
|
||||
"GROUP BY dc.channelId " +
|
||||
"ORDER BY dc.channelId " +
|
||||
" </script>"})
|
||||
List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online);
|
||||
|
||||
@@ -138,7 +142,8 @@ public interface DeviceChannelMapper {
|
||||
"insert into device_channel " +
|
||||
"(channelId, deviceId, name, manufacture, model, owner, civilCode, block, subCount, " +
|
||||
" address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " +
|
||||
" ipAddress, port, password, PTZType, status, streamId, longitude, latitude, createTime, updateTime) " +
|
||||
" ipAddress, port, password, PTZType, status, streamId, longitude, latitude, longitudeGcj02, latitudeGcj02, " +
|
||||
" longitudeWgs84, latitudeWgs84, createTime, updateTime) " +
|
||||
"values " +
|
||||
"<foreach collection='addChannels' index='index' item='item' separator=','> " +
|
||||
"('${item.channelId}', '${item.deviceId}', '${item.name}', '${item.manufacture}', '${item.model}', " +
|
||||
@@ -146,7 +151,8 @@ public interface DeviceChannelMapper {
|
||||
"'${item.address}', ${item.parental}, '${item.parentId}', ${item.safetyWay}, ${item.registerWay}, " +
|
||||
"'${item.certNum}', ${item.certifiable}, ${item.errCode}, '${item.secrecy}', " +
|
||||
"'${item.ipAddress}', ${item.port}, '${item.password}', ${item.PTZType}, ${item.status}, " +
|
||||
"'${item.streamId}', ${item.longitude}, ${item.latitude},'${item.createTime}', '${item.updateTime}')" +
|
||||
"'${item.streamId}', ${item.longitude}, ${item.latitude},${item.longitudeGcj02}, " +
|
||||
"${item.latitudeGcj02},${item.longitudeWgs84}, ${item.latitudeWgs84},'${item.createTime}', '${item.updateTime}')" +
|
||||
"</foreach> " +
|
||||
"ON DUPLICATE KEY UPDATE " +
|
||||
"updateTime=VALUES(updateTime), " +
|
||||
@@ -173,7 +179,11 @@ public interface DeviceChannelMapper {
|
||||
"status=VALUES(status), " +
|
||||
"streamId=VALUES(streamId), " +
|
||||
"longitude=VALUES(longitude), " +
|
||||
"latitude=VALUES(latitude)" +
|
||||
"latitude=VALUES(latitude), " +
|
||||
"longitudeGcj02=VALUES(longitudeGcj02), " +
|
||||
"latitudeGcj02=VALUES(latitudeGcj02), " +
|
||||
"longitudeWgs84=VALUES(longitudeWgs84), " +
|
||||
"latitudeWgs84=VALUES(latitudeWgs84) " +
|
||||
"</script>")
|
||||
int batchAdd(List<DeviceChannel> addChannels);
|
||||
|
||||
@@ -207,7 +217,11 @@ public interface DeviceChannelMapper {
|
||||
"<if test='item.hasAudio != null'>, hasAudio=${item.hasAudio}</if>" +
|
||||
"<if test='item.longitude != null'>, longitude=${item.longitude}</if>" +
|
||||
"<if test='item.latitude != null'>, latitude=${item.latitude}</if>" +
|
||||
"WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}"+
|
||||
"<if test='item.longitudeGcj02 != null'>, longitudeGcj02=${item.longitudeGcj02}</if>" +
|
||||
"<if test='item.latitudeGcj02 != null'>, latitudeGcj02=${item.latitudeGcj02}</if>" +
|
||||
"<if test='item.longitudeWgs84 != null'>, longitudeWgs84=${item.longitudeWgs84}</if>" +
|
||||
"<if test='item.latitudeWgs84 != null'>, latitudeWgs84=${item.latitudeWgs84}</if>" +
|
||||
"WHERE deviceId='${item.deviceId}' AND channelId='${item.channelId}'"+
|
||||
"</foreach>" +
|
||||
"</script>"})
|
||||
int batchUpdate(List<DeviceChannel> updateChannels);
|
||||
@@ -261,4 +275,6 @@ public interface DeviceChannelMapper {
|
||||
@Select("SELECT * FROM device_channel WHERE length(trim(streamId)) > 0")
|
||||
List<DeviceChannel> getAllChannelInPlay();
|
||||
|
||||
@Select("select * from device_channel where longitude*latitude > 0 and deviceId = #{deviceId}")
|
||||
List<DeviceChannel> getAllChannelWithCoordinate(String deviceId);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ public interface DeviceMapper {
|
||||
"mobilePositionSubmissionInterval," +
|
||||
"subscribeCycleForAlarm," +
|
||||
"ssrcCheck," +
|
||||
"geoCoordSys," +
|
||||
"online" +
|
||||
") VALUES (" +
|
||||
"#{deviceId}," +
|
||||
@@ -61,6 +62,7 @@ public interface DeviceMapper {
|
||||
"#{mobilePositionSubmissionInterval}," +
|
||||
"#{subscribeCycleForAlarm}," +
|
||||
"#{ssrcCheck}," +
|
||||
"#{geoCoordSys}," +
|
||||
"#{online}" +
|
||||
")")
|
||||
int add(Device device);
|
||||
@@ -87,6 +89,7 @@ public interface DeviceMapper {
|
||||
"<if test=\"mobilePositionSubmissionInterval != null\">, mobilePositionSubmissionInterval=${mobilePositionSubmissionInterval}</if>" +
|
||||
"<if test=\"subscribeCycleForAlarm != null\">, subscribeCycleForAlarm=${subscribeCycleForAlarm}</if>" +
|
||||
"<if test=\"ssrcCheck != null\">, ssrcCheck=${ssrcCheck}</if>" +
|
||||
"<if test=\"geoCoordSys != null\">, geoCoordSys=#{geoCoordSys}</if>" +
|
||||
"WHERE deviceId='${deviceId}'"+
|
||||
" </script>"})
|
||||
int update(Device device);
|
||||
|
||||
@@ -14,9 +14,9 @@ import java.util.List;
|
||||
public interface StreamPushMapper {
|
||||
|
||||
@Insert("INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " +
|
||||
"createStamp, aliveSecond, mediaServerId) VALUES" +
|
||||
"createStamp, aliveSecond, mediaServerId, serverId) VALUES" +
|
||||
"('${app}', '${stream}', '${totalReaderCount}', '${originType}', '${originTypeStr}', " +
|
||||
"'${createStamp}', '${aliveSecond}', '${mediaServerId}' )")
|
||||
"'${createStamp}', '${aliveSecond}', '${mediaServerId}' , '${serverId}' )")
|
||||
int add(StreamPushItem streamPushItem);
|
||||
|
||||
@Update("UPDATE stream_push " +
|
||||
|
||||
@@ -587,11 +587,11 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
||||
String scanKey = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_*";
|
||||
List<GPSMsgInfo> result = new ArrayList<>();
|
||||
List<Object> keys = redis.scan(scanKey);
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
String key = (String) keys.get(i);
|
||||
for (Object o : keys) {
|
||||
String key = (String) o;
|
||||
GPSMsgInfo gpsMsgInfo = (GPSMsgInfo) redis.get(key);
|
||||
if (!gpsMsgInfo.isStored()) { // 只取没有存过得
|
||||
result.add((GPSMsgInfo)redis.get(key));
|
||||
result.add((GPSMsgInfo) redis.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,7 +667,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
||||
@Override
|
||||
public void sendStreamPushRequestedMsg(MessageForPushChannel msg) {
|
||||
String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED;
|
||||
logger.info("[redis 推流被请求通知] {}: {}-{}", key, msg.getApp(), msg.getStream());
|
||||
logger.info("[redis 推流被请求通知] {}: {}/{}", key, msg.getApp(), msg.getStream());
|
||||
redis.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
|
||||
}
|
||||
|
||||
|
||||
@@ -25,12 +25,13 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
/**
|
||||
* 视频设备数据存储-jdbc实现
|
||||
* swwheihei
|
||||
* 2020年5月6日 下午2:31:42
|
||||
@@ -195,7 +196,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
|
||||
@Override
|
||||
public boolean resetChannels(String deviceId, List<DeviceChannel> deviceChannelList) {
|
||||
if (deviceChannelList == null) {
|
||||
if (CollectionUtils.isEmpty(deviceChannelList)) {
|
||||
return false;
|
||||
}
|
||||
List<DeviceChannel> allChannelInPlay = deviceChannelMapper.getAllChannelInPlay();
|
||||
@@ -246,6 +247,10 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
if (stringBuilder.length() > 0) {
|
||||
logger.info("[目录查询]收到的数据存在重复: {}" , stringBuilder);
|
||||
}
|
||||
if(CollectionUtils.isEmpty(channels)){
|
||||
logger.info("通道重设,数据为空={}" , deviceChannelList);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
int cleanChannelsResult = deviceChannelMapper.cleanChannelsNotInList(deviceId, channels);
|
||||
int limitCount = 300;
|
||||
@@ -315,6 +320,9 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
List<DeviceChannel> all;
|
||||
if (catalogUnderDevice != null && catalogUnderDevice) {
|
||||
all = deviceChannelMapper.queryChannels(deviceId, deviceId, query, hasSubChannel, online);
|
||||
// 海康设备的parentId是SIP id
|
||||
List<DeviceChannel> deviceChannels = deviceChannelMapper.queryChannels(deviceId, sipConfig.getId(), query, hasSubChannel, online);
|
||||
all.addAll(deviceChannels);
|
||||
}else {
|
||||
all = deviceChannelMapper.queryChannels(deviceId, null, query, hasSubChannel, online);
|
||||
}
|
||||
@@ -876,6 +884,11 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
return streamPushMapper.del(app, stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamPushItem getMedia(String app, String stream) {
|
||||
return streamPushMapper.selectOne(app, stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearMediaList() {
|
||||
streamPushMapper.clear();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.genersoft.iot.vmp.utils;
|
||||
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
@@ -18,35 +17,63 @@ import java.util.Locale;
|
||||
*/
|
||||
public class DateUtil {
|
||||
|
||||
private static final String yyyy_MM_dd_T_HH_mm_ss_SSSXXX = "yyyy-MM-dd'T'HH:mm:ss";
|
||||
public static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";
|
||||
/**
|
||||
* 兼容不规范的iso8601时间格式
|
||||
*/
|
||||
private static final String ISO8601_COMPATIBLE_PATTERN = "yyyy-M-d'T'H:m:s";
|
||||
|
||||
public static final SimpleDateFormat formatISO8601 = new SimpleDateFormat(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault());
|
||||
public static final SimpleDateFormat format = new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss, Locale.getDefault());
|
||||
/**
|
||||
* 用以输出标准的iso8601时间格式
|
||||
*/
|
||||
private static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
|
||||
|
||||
public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault()).withZone(ZoneId.systemDefault());
|
||||
public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()).withZone(ZoneId.systemDefault());
|
||||
/**
|
||||
* wvp内部统一时间格式
|
||||
*/
|
||||
public static final String PATTERN = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
public static final String zoneStr = "Asia/Shanghai";
|
||||
|
||||
public static final DateTimeFormatter formatterCompatibleISO8601 = DateTimeFormatter.ofPattern(ISO8601_COMPATIBLE_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
|
||||
public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(ISO8601_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
|
||||
public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
|
||||
|
||||
public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) {
|
||||
|
||||
return formatterISO8601.format(formatter.parse(formatTime));
|
||||
}
|
||||
|
||||
public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) {
|
||||
return formatter.format(formatterISO8601.parse(formatTime));
|
||||
return formatter.format(formatterCompatibleISO8601.parse(formatTime));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* yyyy_MM_dd_HH_mm_ss 转时间戳
|
||||
* @param formatTime
|
||||
* @return
|
||||
*/
|
||||
public static long yyyy_MM_dd_HH_mm_ssToTimestamp(String formatTime) {
|
||||
TemporalAccessor temporalAccessor = formatter.parse(formatTime);
|
||||
Instant instant = Instant.from(temporalAccessor);
|
||||
return instant.getEpochSecond();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间
|
||||
* @return
|
||||
*/
|
||||
public static String getNow() {
|
||||
LocalDateTime nowDateTime = LocalDateTime.now();
|
||||
return formatter.format(nowDateTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式校验
|
||||
* @param timeStr 时间字符串
|
||||
* @param dateTimeFormatter 待校验的格式
|
||||
* @return
|
||||
*/
|
||||
public static boolean verification(String timeStr, DateTimeFormatter dateTimeFormatter) {
|
||||
try {
|
||||
LocalDate.parse(timeStr, dateTimeFormatter);
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -68,8 +69,8 @@ public class AlarmController {
|
||||
@ApiImplicitParam(name="alarmMethod", value = "查询内容" ,dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name="alarmMethod", value = "查询内容" ,dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name="alarmType", value = "查询内容" ,dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name="startTime", value = "查询内容" ,dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name="endTime", value = "查询内容" ,dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name="startTime", value = "开始时间" ,dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name="endTime", value = "结束时间" ,dataTypeClass = String.class),
|
||||
})
|
||||
public ResponseEntity<PageInfo<DeviceAlarm>> getAll(
|
||||
@RequestParam int page,
|
||||
@@ -98,14 +99,7 @@ public class AlarmController {
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
if (startTime != null) {
|
||||
DateUtil.format.parse(startTime);
|
||||
}
|
||||
if (endTime != null) {
|
||||
DateUtil.format.parse(endTime);
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){
|
||||
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@@ -144,11 +138,7 @@ public class AlarmController {
|
||||
if (StringUtils.isEmpty(time)) {
|
||||
time = null;
|
||||
}
|
||||
try {
|
||||
if (time != null) {
|
||||
DateUtil.format.parse(time);
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
if (!DateUtil.verification(time, DateUtil.formatter) ){
|
||||
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
List<String> deviceIdList = null;
|
||||
@@ -189,7 +179,7 @@ public class AlarmController {
|
||||
deviceAlarm.setAlarmDescription("test");
|
||||
deviceAlarm.setAlarmMethod("1");
|
||||
deviceAlarm.setAlarmPriority("1");
|
||||
deviceAlarm.setAlarmTime(DateUtil.formatISO8601.format(System.currentTimeMillis()));
|
||||
deviceAlarm.setAlarmTime(DateUtil.formatterISO8601.format(LocalDateTime.now()));
|
||||
deviceAlarm.setAlarmType("1");
|
||||
deviceAlarm.setLongitude(115.33333);
|
||||
deviceAlarm.setLatitude(39.33333);
|
||||
|
||||
@@ -21,16 +21,22 @@ import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.context.request.async.DeferredResult;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.sip.DialogState;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
|
||||
@Api(tags = "国标设备查询", value = "国标设备查询")
|
||||
@@ -200,6 +206,11 @@ public class DeviceQuery {
|
||||
Set<String> allKeys = dynamicTask.getAllKeys();
|
||||
for (String key : allKeys) {
|
||||
if (key.startsWith(deviceId)) {
|
||||
Runnable runnable = dynamicTask.get(key);
|
||||
if (runnable instanceof ISubscribeTask) {
|
||||
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
|
||||
subscribeTask.stop();
|
||||
}
|
||||
dynamicTask.stop(key);
|
||||
}
|
||||
}
|
||||
@@ -306,12 +317,7 @@ public class DeviceQuery {
|
||||
public ResponseEntity<WVPResult<String>> updateDevice(Device device){
|
||||
|
||||
if (device != null && device.getDeviceId() != null) {
|
||||
|
||||
|
||||
// TODO 报警订阅相关的信息
|
||||
|
||||
deviceService.updateDevice(device);
|
||||
// cmder.deviceInfoQuery(device);
|
||||
}
|
||||
WVPResult<String> result = new WVPResult<>();
|
||||
result.setCode(0);
|
||||
@@ -336,6 +342,11 @@ public class DeviceQuery {
|
||||
Device device = storager.queryVideoDevice(deviceId);
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId;
|
||||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L);
|
||||
if(device == null) {
|
||||
result.setResult(new ResponseEntity(String.format("设备%s不存在", deviceId),HttpStatus.OK));
|
||||
return result;
|
||||
}
|
||||
cmder.deviceStatusQuery(device, event -> {
|
||||
RequestMessage msg = new RequestMessage();
|
||||
msg.setId(uuid);
|
||||
@@ -343,7 +354,6 @@ public class DeviceQuery {
|
||||
msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg));
|
||||
resultHolder.invokeResult(msg);
|
||||
});
|
||||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L);
|
||||
result.onTimeout(()->{
|
||||
logger.warn(String.format("获取设备状态超时"));
|
||||
// 释放rtpserver
|
||||
@@ -456,4 +466,17 @@ public class DeviceQuery {
|
||||
wvpResult.setData(dialogStateMap);
|
||||
return wvpResult;
|
||||
}
|
||||
|
||||
@GetMapping("/snap/{deviceId}/{channelId}")
|
||||
@ApiOperation(value = "请求截图", notes = "请求截图")
|
||||
public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId) {
|
||||
|
||||
try {
|
||||
final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + ".jpg").toPath());
|
||||
resp.setContentType(MediaType.IMAGE_PNG_VALUE);
|
||||
IOUtils.copy(in, resp.getOutputStream());
|
||||
} catch (IOException e) {
|
||||
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class GBRecordController {
|
||||
if (!DateUtil.verification(startTime, DateUtil.formatter)){
|
||||
WVPResult<RecordInfo> wvpResult = new WVPResult<>();
|
||||
wvpResult.setCode(-1);
|
||||
wvpResult.setMsg("startTime error, format is " + DateUtil.yyyy_MM_dd_HH_mm_ss);
|
||||
wvpResult.setMsg("startTime error, format is " + DateUtil.PATTERN);
|
||||
|
||||
ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK);
|
||||
result.setResult(resultResponseEntity);
|
||||
@@ -81,7 +81,7 @@ public class GBRecordController {
|
||||
if (!DateUtil.verification(endTime, DateUtil.formatter)){
|
||||
WVPResult<RecordInfo> wvpResult = new WVPResult<>();
|
||||
wvpResult.setCode(-1);
|
||||
wvpResult.setMsg("endTime error, format is " + DateUtil.yyyy_MM_dd_HH_mm_ss);
|
||||
wvpResult.setMsg("endTime error, format is " + DateUtil.PATTERN);
|
||||
ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK);
|
||||
result.setResult(resultResponseEntity);
|
||||
return result;
|
||||
|
||||
@@ -76,14 +76,7 @@ public class LogController {
|
||||
logger.warn("自动记录日志功能已关闭,查询结果可能不完整。");
|
||||
}
|
||||
|
||||
try {
|
||||
if (startTime != null) {
|
||||
DateUtil.format.parse(startTime);
|
||||
}
|
||||
if (endTime != null) {
|
||||
DateUtil.format.parse(endTime);
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){
|
||||
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
|
||||
@@ -146,8 +146,8 @@ public class ApiDeviceController {
|
||||
// 2-基于口令的双向认证,
|
||||
// 3-基于数字证书的双向认证
|
||||
deviceJOSNChannel.put("Status", deviceChannel.getStatus());
|
||||
deviceJOSNChannel.put("Longitude", deviceChannel.getLongitude());
|
||||
deviceJOSNChannel.put("Latitude", deviceChannel.getLatitude());
|
||||
deviceJOSNChannel.put("Longitude", deviceChannel.getLongitudeWgs84());
|
||||
deviceJOSNChannel.put("Latitude", deviceChannel.getLatitudeWgs84());
|
||||
deviceJOSNChannel.put("PTZType ", deviceChannel.getPTZType()); // 云台类型, 0 - 未知, 1 - 球机, 2 - 半球,
|
||||
// 3 - 固定枪机, 4 - 遥控枪机
|
||||
deviceJOSNChannel.put("CustomPTZType", "");
|
||||
|
||||
Reference in New Issue
Block a user