Merge remote-tracking branch 'origin/master'

2.7.3-20250312
648540858 2025-03-09 06:28:22 +08:00
commit b6a6144bf2
22 changed files with 871 additions and 211 deletions

View File

@ -1,8 +1,5 @@
package com.genersoft.iot.vmp.common.enums; package com.genersoft.iot.vmp.common.enums;
import lombok.Getter;
/** /**
* *
*/ */

View File

@ -0,0 +1,184 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
*
*/
@Data
public class FrontEndCode {
public static String encode(IFrontEndControlCode frontEndControlCode){
return frontEndControlCode.encode();
}
public static IFrontEndControlCode decode(@NotNull String cmdStr) {
if (cmdStr.length() != 16) {
return null;
}
String cmdCodeStr = cmdStr.substring(6, 8);
int cmdCode = Integer.parseInt(cmdCodeStr, 16);
if (cmdCode < 39) {
// PTZ指令
FrontEndControlCodeForPTZ codeForPTZ = new FrontEndControlCodeForPTZ();
int zoomOut = cmdCode >> 5 & 1;
if (zoomOut == 1) {
codeForPTZ.setZoom(0);
}
int zoomIn = cmdCode >> 4 & 1;
if (zoomIn == 1) {
codeForPTZ.setZoom(1);
}
int tiltUp = cmdCode >> 3 & 1;
if (tiltUp == 1) {
codeForPTZ.setTilt(0);
}
int tiltDown = cmdCode >> 2 & 1;
if (tiltDown == 1) {
codeForPTZ.setTilt(1);
}
int panLeft = cmdCode >> 1 & 1;
if (panLeft == 1) {
codeForPTZ.setPan(0);
}
int panRight = cmdCode & 1;
if (panRight == 1) {
codeForPTZ.setPan(1);
}
String param1Str = cmdStr.substring(8, 10);
codeForPTZ.setPanSpeed(Integer.parseInt(param1Str, 16));
String param2Str = cmdStr.substring(10, 12);
codeForPTZ.setTiltSpeed(Integer.parseInt(param2Str, 16));
String param3Str = cmdStr.substring(12, 13);
codeForPTZ.setZoomSpeed(Integer.parseInt(param3Str, 16));
return codeForPTZ;
}else if (cmdCode < 74) {
// FI指令
FrontEndControlCodeForFI codeForFI = new FrontEndControlCodeForFI();
int irisOut = cmdCode >> 3 & 1;
if (irisOut == 1) {
codeForFI.setIris(0);
}
int irisIn = cmdCode >> 2 & 1;
if (irisIn == 1) {
codeForFI.setIris(1);
}
int focusNear = cmdCode >> 1 & 1;
if (focusNear == 1) {
codeForFI.setFocus(0);
}
int focusFar = cmdCode & 1;
if (focusFar == 1) {
codeForFI.setFocus(1);
}
String param1Str = cmdStr.substring(8, 10);
codeForFI.setFocusSpeed(Integer.parseInt(param1Str, 16));
String param2Str = cmdStr.substring(10, 12);
codeForFI.setIrisSpeed(Integer.parseInt(param2Str, 16));
return codeForFI;
}else if (cmdCode < 131) {
// 预置位指令
FrontEndControlCodeForPreset codeForPreset = new FrontEndControlCodeForPreset();
switch (cmdCode) {
case 0x81: // 设置预置位
codeForPreset.setCode(1);
break;
case 0x82: // 调用预置位
codeForPreset.setCode(2);
break;
case 0x83: // 删除预置位
codeForPreset.setCode(3);
break;
default:
return null;
}
// 预置位编号
String param2Str = cmdStr.substring(10, 12);
codeForPreset.setPresetId(Integer.parseInt(param2Str, 16));
return codeForPreset;
}else if (cmdCode < 136) {
// 巡航指令
FrontEndControlCodeForTour codeForTour = new FrontEndControlCodeForTour();
String param3Str = cmdStr.substring(12, 13);
switch (cmdCode) {
case 0x84: // 加入巡航点
codeForTour.setCode(1);
break;
case 0x85: // 删除一个巡航点
codeForTour.setCode(2);
break;
case 0x86: // 设置巡航速度
codeForTour.setCode(3);
codeForTour.setTourSpeed(Integer.parseInt(param3Str, 16));
break;
case 0x87: // 设置巡航停留时间
codeForTour.setCode(4);
codeForTour.setTourTime(Integer.parseInt(param3Str, 16));
break;
case 0x88: // 开始巡航
codeForTour.setCode(5);
break;
default:
return null;
}
String param1Str = cmdStr.substring(8, 10);
codeForTour.setTourId(Integer.parseInt(param1Str, 16));
String param2Str = cmdStr.substring(10, 12);
codeForTour.setPresetId(Integer.parseInt(param2Str, 16));
return codeForTour;
}else if (cmdCode < 138) {
// 扫描指令
FrontEndControlCodeForScan controlCodeForScan = new FrontEndControlCodeForScan();
String param2Str = cmdStr.substring(10, 11);
int param2Code = Integer.parseInt(param2Str, 16);
switch (cmdCode) {
case 0x89:
switch (param2Code) {
case 0x00: // 开始自动扫描
controlCodeForScan.setCode(1);
break;
case 0x01: // 设置自动扫描左边界
controlCodeForScan.setCode(2);
break;
case 0x02: // 设置自动扫描右边界
controlCodeForScan.setCode(3);
break;
}
break;
case 0x8A: // 删除一个巡航点
controlCodeForScan.setCode(4);
String param3Str = cmdStr.substring(12, 13);
controlCodeForScan.setScanSpeed(Integer.parseInt(param3Str, 16));
break;
default:
return null;
}
String param1Str = cmdStr.substring(8, 10);
controlCodeForScan.setScanId(Integer.parseInt(param1Str, 16));
return controlCodeForScan;
}else if (cmdCode < 141) {
// 辅助开关
FrontEndControlCodeForAuxiliary codeForAuxiliary = new FrontEndControlCodeForAuxiliary();
switch (cmdCode) {
case 0x8C: // 开
codeForAuxiliary.setCode(1);
break;
case 0x8D: // 关
codeForAuxiliary.setCode(2);
break;
default:
return null;
}
// 预置位编号
String param2Str = cmdStr.substring(10, 12);
codeForAuxiliary.setAuxiliaryId(Integer.parseInt(param2Str, 16));
return codeForAuxiliary;
}else {
return null;
}
}
}

View File

@ -0,0 +1,34 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Getter;
import lombok.Setter;
public class FrontEndControlCodeForAuxiliary implements IFrontEndControlCode {
private final FrontEndControlType type = FrontEndControlType.AUXILIARY;
@Override
public FrontEndControlType getType() {
return type;
}
/**
* 1 2 3 4
*/
@Getter
@Setter
private Integer code;
/**
*
*/
@Getter
@Setter
private Integer auxiliaryId;
@Override
public String encode() {
return "";
}
}

View File

@ -0,0 +1,48 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Getter;
import lombok.Setter;
public class FrontEndControlCodeForFI implements IFrontEndControlCode {
private final FrontEndControlType type = FrontEndControlType.FI;
@Override
public FrontEndControlType getType() {
return type;
}
/**
* 0 1
*/
@Getter
@Setter
private Integer iris;
/**
* 0 1
*/
@Getter
@Setter
private Integer focus;
/**
*
*/
@Getter
@Setter
private Integer focusSpeed;
/**
*
*/
@Getter
@Setter
private Integer irisSpeed;
@Override
public String encode() {
return "";
}
}

View File

@ -0,0 +1,62 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Getter;
import lombok.Setter;
public class FrontEndControlCodeForPTZ implements IFrontEndControlCode {
private final FrontEndControlType type = FrontEndControlType.PTZ;
@Override
public FrontEndControlType getType() {
return type;
}
/**
* 0 1
*/
@Getter
@Setter
private Integer zoom;
/**
* 0 1
*/
@Getter
@Setter
private Integer tilt;
/**
* 0 1
*/
@Getter
@Setter
private Integer pan;
/**
*
*/
@Getter
@Setter
private Integer panSpeed;
/**
*
*/
@Getter
@Setter
private Integer tiltSpeed;
/**
*
*/
@Getter
@Setter
private Integer zoomSpeed;
@Override
public String encode() {
return "";
}
}

View File

@ -0,0 +1,35 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Getter;
import lombok.Setter;
public class FrontEndControlCodeForPreset implements IFrontEndControlCode {
private final FrontEndControlType type = FrontEndControlType.PRESET;
@Override
public FrontEndControlType getType() {
return type;
}
/**
* 1 2 3
*/
@Getter
@Setter
private Integer code;
/**
*
*/
@Getter
@Setter
private Integer presetId;
@Override
public String encode() {
return "";
}
}

View File

@ -0,0 +1,41 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Getter;
import lombok.Setter;
public class FrontEndControlCodeForScan implements IFrontEndControlCode {
private final FrontEndControlType type = FrontEndControlType.SCAN;
@Override
public FrontEndControlType getType() {
return type;
}
/**
* 1 2 3 4
*/
@Getter
@Setter
private Integer code;
/**
*
*/
@Getter
@Setter
private Integer scanSpeed;
/**
*
*/
@Getter
@Setter
private Integer scanId;
@Override
public String encode() {
return "";
}
}

View File

@ -0,0 +1,55 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Getter;
import lombok.Setter;
public class FrontEndControlCodeForTour implements IFrontEndControlCode {
private final FrontEndControlType type = FrontEndControlType.TOUR;
@Override
public FrontEndControlType getType() {
return type;
}
/**
* 1 2 3 4 5
*/
@Getter
@Setter
private Integer code;
/**
*
*/
@Getter
@Setter
private Integer tourId;
/**
*
*/
@Getter
@Setter
private Integer tourTime;
/**
*
*/
@Getter
@Setter
private Integer tourSpeed;
/**
*
*/
@Getter
@Setter
private Integer presetId;
@Override
public String encode() {
return "";
}
}

View File

@ -0,0 +1,6 @@
package com.genersoft.iot.vmp.gb28181.bean;
public enum FrontEndControlType {
PTZ,FI,PRESET,TOUR,SCAN,AUXILIARY
}

View File

@ -0,0 +1,7 @@
package com.genersoft.iot.vmp.gb28181.bean;
public interface IFrontEndControlCode {
FrontEndControlType getType();
String encode();
}

View File

@ -42,7 +42,7 @@ public class PtzController {
private IDeviceService deviceService; private IDeviceService deviceService;
@Autowired @Autowired
private IPTZService iptzService; private IPTZService ptzService;
@Autowired @Autowired
private DeferredResultHolder resultHolder; private DeferredResultHolder resultHolder;
@ -75,7 +75,7 @@ public class PtzController {
Assert.notNull(device, "设备[" + deviceId + "]不存在"); Assert.notNull(device, "设备[" + deviceId + "]不存在");
iptzService.frontEndCommand(device, channelId, cmdCode, parameter1, parameter2, combindCode2); ptzService.frontEndCommand(device, channelId, cmdCode, parameter1, parameter2, combindCode2);
} }
@Operation(summary = "云台控制", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Operation(summary = "云台控制", security = @SecurityRequirement(name = JwtUtils.HEADER))

View File

@ -1,13 +1,17 @@
package com.genersoft.iot.vmp.gb28181.service; package com.genersoft.iot.vmp.gb28181.service;
import com.genersoft.iot.vmp.common.enums.DeviceControlType;
import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelReduce; import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelReduce;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo;
import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import org.dom4j.Element;
import javax.validation.constraints.NotNull;
import java.util.List; import java.util.List;
/** /**
@ -126,4 +130,5 @@ public interface IDeviceChannelService {
List<Integer> queryChaneIdListByDeviceDbIds(List<Integer> deviceDbId); List<Integer> queryChaneIdListByDeviceDbIds(List<Integer> deviceDbId);
void handlePtzCmd(@NotNull Integer dataDeviceId, @NotNull Integer gbId, Element rootElement, DeviceControlType type, ErrorCallback<String> callback);
} }

View File

@ -0,0 +1,16 @@
package com.genersoft.iot.vmp.gb28181.service;
import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel;
import com.genersoft.iot.vmp.gb28181.bean.FrontEndControlCodeForPTZ;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
public interface IGbChannelControlService {
void ptz(CommonGBChannel channel, FrontEndControlCodeForPTZ frontEndControlCode, ErrorCallback<String> callback);
void fi(CommonGBChannel channel, FrontEndControlCodeForPTZ frontEndControlCode, ErrorCallback<String> callback);
void preset(CommonGBChannel channel, FrontEndControlCodeForPTZ frontEndControlCode, ErrorCallback<String> callback);
void tour(CommonGBChannel channel, FrontEndControlCodeForPTZ frontEndControlCode, ErrorCallback<String> callback);
void scan(CommonGBChannel channel, FrontEndControlCodeForPTZ frontEndControlCode, ErrorCallback<String> callback);
void auxiliary(CommonGBChannel channel, FrontEndControlCodeForPTZ frontEndControlCode, ErrorCallback<String> callback);
}

View File

@ -5,6 +5,7 @@ import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.common.InviteInfo; import com.genersoft.iot.vmp.common.InviteInfo;
import com.genersoft.iot.vmp.common.InviteSessionType; import com.genersoft.iot.vmp.common.InviteSessionType;
import com.genersoft.iot.vmp.common.enums.ChannelDataType; import com.genersoft.iot.vmp.common.enums.ChannelDataType;
import com.genersoft.iot.vmp.common.enums.DeviceControlType;
import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.bean.*;
@ -18,7 +19,9 @@ import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService;
import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService;
import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService; import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
@ -27,6 +30,7 @@ import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dom4j.Element;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -34,8 +38,15 @@ import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
import javax.sip.message.Response;
import javax.validation.constraints.NotNull;
import java.text.ParseException;
import java.util.*; import java.util.*;
import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
/** /**
* @author lin * @author lin
*/ */
@ -72,6 +83,10 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
private IPlatformChannelService platformChannelService; private IPlatformChannelService platformChannelService;
@Autowired
private ISIPCommander cmder;
@Override @Override
public int updateChannels(Device device, List<DeviceChannel> channels) { public int updateChannels(Device device, List<DeviceChannel> channels) {
List<DeviceChannel> addChannels = new ArrayList<>(); List<DeviceChannel> addChannels = new ArrayList<>();
@ -362,6 +377,39 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
return channelMapper.queryChaneIdListByDeviceDbIds(deviceDbIds); return channelMapper.queryChaneIdListByDeviceDbIds(deviceDbIds);
} }
@Override
public void handlePtzCmd(@NotNull Integer dataDeviceId, @NotNull Integer gbId, Element rootElement, DeviceControlType type, ErrorCallback<String> callback) {
// 根据通道ID获取所属设备
Device device = deviceMapper.query(dataDeviceId);
if (device == null) {
// 不存在则回复404
log.warn("[INFO 消息] 通道所属设备不存在, 设备ID {}", dataDeviceId);
callback.run(Response.NOT_FOUND, "device not found", null);
return;
}
DeviceChannel deviceChannel = channelMapper.getOneForSource(gbId);
if (deviceChannel == null) {
log.warn("[deviceControl] 未找到设备原始通道, 设备: {}{}),通道编号:{}", device.getName(),
device.getDeviceId(), gbId);
callback.run(Response.NOT_FOUND, "channel not found", null);
return;
}
log.info("[deviceControl] 命令: {}, 设备: {}{} 通道{}{}", type, device.getName(), device.getDeviceId(),
deviceChannel.getName(), deviceChannel.getDeviceId());
String cmdString = getText(rootElement, type.getVal());
try {
cmder.fronEndCmd(device, deviceChannel.getDeviceId(), cmdString, errorResult->{
callback.run(errorResult.statusCode, errorResult.msg, null);
}, errorResult->{
callback.run(errorResult.statusCode, errorResult.msg, null);
});
} catch (InvalidArgumentException | SipException | ParseException e) {
log.error("[命令发送失败] 云台/前端: {}", e.getMessage());
}
}
@Override @Override
public void updateChannelGPS(Device device, DeviceChannel deviceChannel, MobilePosition mobilePosition) { public void updateChannelGPS(Device device, DeviceChannel deviceChannel, MobilePosition mobilePosition) {
if (userSetting.getSavePositionHistory()) { if (userSetting.getSavePositionHistory()) {

View File

@ -0,0 +1,43 @@
package com.genersoft.iot.vmp.gb28181.service.impl;
import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel;
import com.genersoft.iot.vmp.gb28181.bean.FrontEndControlCodeForPTZ;
import com.genersoft.iot.vmp.gb28181.service.IGbChannelControlService;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class GbChannelControlServiceImpl implements IGbChannelControlService {
@Override
public void ptz(CommonGBChannel channel, FrontEndControlCodeForPTZ frontEndControlCode, ErrorCallback<String> callback) {
log.info("[通用通道] 云台控制, 通道: {}", channel.getGbId());
}
@Override
public void preset(CommonGBChannel channel, FrontEndControlCodeForPTZ frontEndControlCode, ErrorCallback<String> callback) {
log.info("[通用通道] 预置位, 通道: {}", channel.getGbId());
}
@Override
public void fi(CommonGBChannel channel, FrontEndControlCodeForPTZ frontEndControlCode, ErrorCallback<String> callback) {
log.info("[通用通道] FI指令 通道: {}", channel.getGbId());
}
@Override
public void tour(CommonGBChannel channel, FrontEndControlCodeForPTZ frontEndControlCode, ErrorCallback<String> callback) {
}
@Override
public void scan(CommonGBChannel channel, FrontEndControlCodeForPTZ frontEndControlCode, ErrorCallback<String> callback) {
}
@Override
public void auxiliary(CommonGBChannel channel, FrontEndControlCodeForPTZ frontEndControlCode, ErrorCallback<String> callback) {
}
}

View File

@ -61,7 +61,7 @@ public class SipInviteSessionManager {
if (ssrcTransaction == null ) { if (ssrcTransaction == null ) {
return; return;
} }
redisTemplate.opsForHash().delete(VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId(), stream); redisTemplate.opsForHash().delete(VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId(), app + stream);
if (ssrcTransaction.getCallId() != null) { if (ssrcTransaction.getCallId() != null) {
redisTemplate.opsForHash().delete(VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId(), ssrcTransaction.getCallId()); redisTemplate.opsForHash().delete(VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId(), ssrcTransaction.getCallId());
} }
@ -74,7 +74,7 @@ public class SipInviteSessionManager {
} }
redisTemplate.opsForHash().delete(VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId(), callId); redisTemplate.opsForHash().delete(VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId(), callId);
if (ssrcTransaction.getStream() != null) { if (ssrcTransaction.getStream() != null) {
redisTemplate.opsForHash().delete(VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId(), ssrcTransaction.getStream()); redisTemplate.opsForHash().delete(VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId(), ssrcTransaction.getApp() + ssrcTransaction.getStream());
} }
} }

View File

@ -211,9 +211,6 @@ public class SIPCommander implements ISIPCommander {
ptzXml.append("</Info>\r\n"); ptzXml.append("</Info>\r\n");
ptzXml.append("</Control>\r\n"); ptzXml.append("</Control>\r\n");
SIPRequest request = (SIPRequest) headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); SIPRequest request = (SIPRequest) headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request); sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);

View File

@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService;
import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
import com.genersoft.iot.vmp.gb28181.service.IGbChannelControlService;
import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; import com.genersoft.iot.vmp.gb28181.service.IGbChannelService;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
@ -42,6 +43,9 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
@Autowired @Autowired
private IGbChannelService channelService; private IGbChannelService channelService;
@Autowired
private IGbChannelControlService channelControlService;
@Autowired @Autowired
private IDeviceService deviceService; private IDeviceService deviceService;
@ -135,49 +139,91 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
* *
*/ */
private void handlePtzCmd(CommonGBChannel channel, Element rootElement, SIPRequest request, DeviceControlType type) { private void handlePtzCmd(CommonGBChannel channel, Element rootElement, SIPRequest request, DeviceControlType type) {
if (channel.getDataType() != ChannelDataType.GB28181.value) { if (channel.getDataType() == ChannelDataType.GB28181.value) {
// 只支持国标的云台控制
log.warn("[INFO 消息] 只支持国标的云台控制, 通道ID {}", channel.getGbId()); deviceChannelService.handlePtzCmd(channel.getDataDeviceId(), channel.getGbId(), rootElement, type, ((code, msg, data) -> {
try {
responseAck(request, code, msg);
} catch (InvalidArgumentException | SipException | ParseException exception) {
log.error("[命令发送失败] 云台指令: {}", exception.getMessage());
}
}));
}else {
// 解析云台控制参数
String cmdString = getText(rootElement, type.getVal());
IFrontEndControlCode frontEndControlCode = FrontEndCode.decode(cmdString);
if (frontEndControlCode == null) {
log.info("[INFO 消息] 不支持的控制方式");
try { try {
responseAck(request, Response.FORBIDDEN, ""); responseAck(request, Response.FORBIDDEN, "");
} catch (SipException | InvalidArgumentException | ParseException e) { } catch (InvalidArgumentException | SipException | ParseException exception) {
log.error("[命令发送失败] 错误信息: {}", e.getMessage()); log.error("[命令发送失败] 云台指令: {}", exception.getMessage());
} }
return; return;
} }
// 根据通道ID获取所属设备 switch (frontEndControlCode.getType()){
Device device = deviceService.getDevice(channel.getDataDeviceId()); case PTZ:
if (device == null) { channelControlService.ptz(channel, (FrontEndControlCodeForPTZ)frontEndControlCode, ((code, msg, data) -> {
// 不存在则回复404
log.warn("[INFO 消息] 通道所属设备不存在, 通道ID {}", channel.getGbId());
try { try {
responseAck(request, Response.NOT_FOUND, "device not found"); responseAck(request, code, msg);
} catch (SipException | InvalidArgumentException | ParseException e) { } catch (InvalidArgumentException | SipException | ParseException exception) {
log.error("[命令发送失败] 错误信息: {}", e.getMessage()); log.error("[命令发送失败] 云台指令: {}", exception.getMessage());
} }
return; }));
} break;
case FI:
DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId()); channelControlService.fi(channel, (FrontEndControlCodeForPTZ)frontEndControlCode, ((code, msg, data) -> {
if (deviceChannel == null) {
log.warn("[deviceControl] 未找到设备原始通道, 设备: {}{}),通道编号:{}", device.getName(),
device.getDeviceId(), channel.getGbId());
try { try {
responseAck(request, Response.NOT_FOUND, "channel not found"); responseAck(request, code, msg);
} catch (SipException | InvalidArgumentException | ParseException e) { } catch (InvalidArgumentException | SipException | ParseException exception) {
log.error("[命令发送失败] 错误信息: {}", e.getMessage()); log.error("[命令发送失败] 云台指令: {}", exception.getMessage());
} }
return; }));
} break;
log.info("[deviceControl] 命令: {}, 设备: {}{} 通道{}{}", type, device.getName(), device.getDeviceId(), case PRESET:
deviceChannel.getName(), deviceChannel.getDeviceId()); channelControlService.preset(channel, (FrontEndControlCodeForPTZ)frontEndControlCode, ((code, msg, data) -> {
String cmdString = getText(rootElement, type.getVal());
try { try {
cmder.fronEndCmd(device, deviceChannel.getDeviceId(), cmdString, responseAck(request, code, msg);
errorResult -> onError(request, errorResult), } catch (InvalidArgumentException | SipException | ParseException exception) {
okResult -> onOk(request, okResult)); log.error("[命令发送失败] 云台指令: {}", exception.getMessage());
} catch (InvalidArgumentException | SipException | ParseException e) { }
log.error("[命令发送失败] 云台/前端: {}", e.getMessage()); }));
break;
case TOUR:
channelControlService.tour(channel, (FrontEndControlCodeForPTZ)frontEndControlCode, ((code, msg, data) -> {
try {
responseAck(request, code, msg);
} catch (InvalidArgumentException | SipException | ParseException exception) {
log.error("[命令发送失败] 云台指令: {}", exception.getMessage());
}
}));
break;
case SCAN:
channelControlService.scan(channel, (FrontEndControlCodeForPTZ)frontEndControlCode, ((code, msg, data) -> {
try {
responseAck(request, code, msg);
} catch (InvalidArgumentException | SipException | ParseException exception) {
log.error("[命令发送失败] 云台指令: {}", exception.getMessage());
}
}));
break;
case AUXILIARY:
channelControlService.auxiliary(channel, (FrontEndControlCodeForPTZ)frontEndControlCode, ((code, msg, data) -> {
try {
responseAck(request, code, msg);
} catch (InvalidArgumentException | SipException | ParseException exception) {
log.error("[命令发送失败] 云台指令: {}", exception.getMessage());
}
}));
break;
default:
log.info("[INFO 消息] 设备不支持的控制方式");
try {
responseAck(request, Response.FORBIDDEN, "");
} catch (InvalidArgumentException | SipException | ParseException exception) {
log.error("[命令发送失败] 云台指令: {}", exception.getMessage());
}
}
} }
} }

View File

@ -2,9 +2,14 @@ package com.genersoft.iot.vmp.streamProxy.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.event.hook.Hook;
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
import com.genersoft.iot.vmp.media.event.hook.HookType;
import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent; import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
import com.genersoft.iot.vmp.media.service.IMediaServerService; import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.ErrorCallback; import com.genersoft.iot.vmp.service.bean.ErrorCallback;
@ -23,6 +28,7 @@ import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import javax.sip.message.Response; import javax.sip.message.Response;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
@ -39,6 +45,15 @@ public class StreamProxyPlayServiceImpl implements IStreamProxyPlayService {
@Autowired @Autowired
private IMediaServerService mediaServerService; private IMediaServerService mediaServerService;
@Autowired
private HookSubscribe subscribe;
@Autowired
private DynamicTask dynamicTask;
@Autowired
private UserSetting userSetting;
private ConcurrentHashMap<Integer, ErrorCallback<StreamInfo>> callbackMap = new ConcurrentHashMap<>(); private ConcurrentHashMap<Integer, ErrorCallback<StreamInfo>> callbackMap = new ConcurrentHashMap<>();
private ConcurrentHashMap<Integer, StreamInfo> streamInfoMap = new ConcurrentHashMap<>(); private ConcurrentHashMap<Integer, StreamInfo> streamInfoMap = new ConcurrentHashMap<>();
@ -96,9 +111,25 @@ public class StreamProxyPlayServiceImpl implements IStreamProxyPlayService {
if (record != null) { if (record != null) {
streamProxy.setEnableMp4(record); streamProxy.setEnableMp4(record);
} }
StreamInfo streamInfo = startProxy(streamProxy); StreamInfo streamInfo = startProxy(streamProxy);
if (streamInfo != null && callback != null) { if (callback != null) {
callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo); // 设置流超时的定时任务
String timeOutTaskKey = UUID.randomUUID().toString();
Hook rtpHook = Hook.getInstance(HookType.on_media_arrival, streamProxy.getApp(), streamProxy.getStream(), streamInfo.getMediaServer().getId());
dynamicTask.startDelay(timeOutTaskKey, () -> {
// 收流超时
subscribe.removeSubscribe(rtpHook);
callback.run(InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), streamInfo);
}, userSetting.getPlayTimeout());
// 开启流到来的监听
subscribe.addSubscribe(rtpHook, (hookData) -> {
dynamicTask.stop(timeOutTaskKey);
// hook响应
callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo);
subscribe.removeSubscribe(rtpHook);
});
} }
return streamInfo; return streamInfo;
} }

View File

@ -1,6 +1,7 @@
<template> <template>
<div id="channelList" style="width: 100%"> <div id="channelList" style="width: 100%">
<div v-if="!editId" class="page-header"> <div v-if="!editId">
<div class="page-header">
<div class="page-title"> <div class="page-title">
<el-page-header @back="showDevice" content="通道列表"></el-page-header> <el-page-header @back="showDevice" content="通道列表"></el-page-header>
</div> </div>
@ -40,7 +41,6 @@
<el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button> <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
</div> </div>
</div> </div>
<devicePlayer ref="devicePlayer"></devicePlayer>
<el-table size="medium" ref="channelListTable" :data="deviceChannelList" :height="$tableHeght" <el-table size="medium" ref="channelListTable" :data="deviceChannelList" :height="$tableHeght"
header-row-class-name="table-header"> header-row-class-name="table-header">
<el-table-column prop="name" label="名称" min-width="180"> <el-table-column prop="name" label="名称" min-width="180">
@ -159,6 +159,9 @@
layout="total, sizes, prev, pager, next" layout="total, sizes, prev, pager, next"
:total="total"> :total="total">
</el-pagination> </el-pagination>
</div>
<devicePlayer ref="devicePlayer"></devicePlayer>
<channel-edit v-if="editId" :id="editId" :closeEdit="closeEdit"></channel-edit> <channel-edit v-if="editId" :id="editId" :closeEdit="closeEdit"></channel-edit>
</div> </div>

View File

@ -309,6 +309,7 @@ create table wvp_stream_proxy
update_time character varying(50), update_time character varying(50),
stream_key character varying(255), stream_key character varying(255),
enable_disable_none_reader bool default false, enable_disable_none_reader bool default false,
relates_media_server_id character varying(50),
constraint uk_stream_proxy_app_stream unique (app, stream) constraint uk_stream_proxy_app_stream unique (app, stream)
); );

View File

@ -325,6 +325,7 @@ create table wvp_stream_proxy
update_time character varying(50), update_time character varying(50),
stream_key character varying(255), stream_key character varying(255),
enable_disable_none_reader bool default false, enable_disable_none_reader bool default false,
relates_media_server_id character varying(50),
constraint uk_stream_proxy_app_stream unique (app, stream) constraint uk_stream_proxy_app_stream unique (app, stream)
); );
@ -452,7 +453,7 @@ create table wvp_record_plan
create table wvp_record_plan_item create table wvp_record_plan_item
( (
id serial primary key, id serial primary key,
start int, "start" int,
stop int, stop int,
week_day int, week_day int,
plan_id int, plan_id int,