Merge branch 'master' into dev/数据库统合

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceChannelService.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceChannelServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
#	src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyPlayServiceImpl.java
#	web_src/src/components/DeviceList.vue
This commit is contained in:
648540858
2025-03-10 21:36:52 +08:00
50 changed files with 8656 additions and 479 deletions

View File

@@ -1,8 +1,5 @@
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

@@ -36,7 +36,7 @@ public class PtzController {
private IDeviceService deviceService;
@Autowired
private IPTZService iptzService;
private IPTZService ptzService;
@Autowired
private DeferredResultHolder resultHolder;
@@ -69,7 +69,7 @@ public class PtzController {
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))

View File

@@ -1,12 +1,18 @@
package com.genersoft.iot.vmp.gb28181.service;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.common.enums.DeviceControlType;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
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.web.gb28181.dto.DeviceChannelExtend;
import com.github.pagehelper.PageInfo;
import org.dom4j.Element;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
@@ -125,6 +131,8 @@ public interface IDeviceChannelService {
List<Integer> queryChaneIdListByDeviceDbIds(List<Integer> deviceDbId);
void handlePtzCmd(@NotNull Integer dataDeviceId, @NotNull Integer gbId, Element rootElement, DeviceControlType type, ErrorCallback<String> callback);
void queryRecordInfo(Device device, DeviceChannel channel, String startTime, String endTime, ErrorCallback<RecordInfo> object);
void queryRecordInfo(CommonGBChannel channel, String startTime, String endTime, ErrorCallback<RecordInfo> object);

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

@@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.common.InviteInfo;
import com.genersoft.iot.vmp.common.InviteSessionType;
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.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.bean.*;
@@ -22,6 +23,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcPlayService;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
@@ -30,6 +32,7 @@ import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@@ -41,11 +44,18 @@ import org.springframework.util.ObjectUtils;
import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
import java.text.ParseException;
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.concurrent.ConcurrentHashMap;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
/**
* @author lin
*/
@@ -101,6 +111,10 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
}
}
@Autowired
private ISIPCommander cmder;
@Override
public int updateChannels(Device device, List<DeviceChannel> channels) {
List<DeviceChannel> addChannels = new ArrayList<>();
@@ -389,6 +403,39 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
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
public void updateChannelGPS(Device device, DeviceChannel deviceChannel, MobilePosition mobilePosition) {
if (userSetting.getSavePositionHistory()) {

View File

@@ -200,8 +200,8 @@ public class DeviceServiceImpl implements IDeviceService {
log.warn("[设备不存在] device{}", deviceId);
return;
}
log.info("[设备离线] device{} 当前心跳间隔: {} 上次心跳时间:{} 上次注册时间: {}", deviceId,
device.getHeartBeatInterval(), device.getKeepaliveTime(), device.getRegisterTime());
log.info("[设备离线] device{} 心跳间隔: {},心跳超时次数: {} 上次心跳时间:{} 上次注册时间: {}", deviceId,
device.getHeartBeatInterval(), device.getHeartBeatCount(), device.getKeepaliveTime(), device.getRegisterTime());
String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + deviceId;
dynamicTask.stop(registerExpireTaskKey);
if (device.isOnLine()) {

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 ) {
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) {
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);
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

@@ -218,9 +218,6 @@ public class SIPCommander implements ISIPCommander {
ptzXml.append("</Info>\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()));
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.service.IDeviceChannelService;
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.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
@@ -43,6 +44,9 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
@Autowired
private IGbChannelService channelService;
@Autowired
private IGbChannelControlService channelControlService;
@Autowired
private IDeviceService deviceService;
@@ -136,49 +140,91 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
* 处理云台指令
*/
private void handlePtzCmd(CommonGBChannel channel, Element rootElement, SIPRequest request, DeviceControlType type) {
if (channel.getDataType() != ChannelDataType.GB28181.value) {
// 只支持国标的云台控制
log.warn("[INFO 消息] 只支持国标的云台控制, 通道ID {}", channel.getGbId());
try {
responseAck(request, Response.FORBIDDEN, "");
} catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[命令发送失败] 错误信息: {}", e.getMessage());
}
return;
}
// 根据通道ID获取所属设备
Device device = deviceService.getDevice(channel.getDataDeviceId());
if (device == null) {
// 不存在则回复404
log.warn("[INFO 消息] 通道所属设备不存在, 通道ID {}", channel.getGbId());
try {
responseAck(request, Response.NOT_FOUND, "device not found");
} catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[命令发送失败] 错误信息: {}", e.getMessage());
}
return;
}
if (channel.getDataType() == ChannelDataType.GB28181.value) {
DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId());
if (deviceChannel == null) {
log.warn("[deviceControl] 未找到设备原始通道, 设备: {}{}),通道编号:{}", device.getName(),
device.getDeviceId(), channel.getGbId());
try {
responseAck(request, Response.NOT_FOUND, "channel not found");
} catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[命令发送失败] 错误信息: {}", e.getMessage());
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 {
responseAck(request, Response.FORBIDDEN, "");
} catch (InvalidArgumentException | SipException | ParseException exception) {
log.error("[命令发送失败] 云台指令: {}", exception.getMessage());
}
return;
}
switch (frontEndControlCode.getType()){
case PTZ:
channelControlService.ptz(channel, (FrontEndControlCodeForPTZ)frontEndControlCode, ((code, msg, data) -> {
try {
responseAck(request, code, msg);
} catch (InvalidArgumentException | SipException | ParseException exception) {
log.error("[命令发送失败] 云台指令: {}", exception.getMessage());
}
}));
break;
case FI:
channelControlService.fi(channel, (FrontEndControlCodeForPTZ)frontEndControlCode, ((code, msg, data) -> {
try {
responseAck(request, code, msg);
} catch (InvalidArgumentException | SipException | ParseException exception) {
log.error("[命令发送失败] 云台指令: {}", exception.getMessage());
}
}));
break;
case PRESET:
channelControlService.preset(channel, (FrontEndControlCodeForPTZ)frontEndControlCode, ((code, msg, data) -> {
try {
responseAck(request, code, msg);
} catch (InvalidArgumentException | SipException | ParseException exception) {
log.error("[命令发送失败] 云台指令: {}", exception.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());
}
}
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 -> onError(request, errorResult),
okResult -> onOk(request));
} catch (InvalidArgumentException | SipException | ParseException e) {
log.error("[命令发送失败] 云台/前端: {}", e.getMessage());
}
}

View File

@@ -140,8 +140,6 @@ public class MediaServiceImpl implements IMediaService {
result.setEnable_mp4(userSetting.getRecordSip());
}
result.setEnable_mp4(inviteInfo.getRecord());
// 单端口模式下修改流 ID
if (!mediaServer.isRtpEnable() && inviteInfo == null) {
String ssrc = String.format("%010d", Long.parseLong(stream, 16));

View File

@@ -2,9 +2,14 @@ package com.genersoft.iot.vmp.streamProxy.service.impl;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.UserSetting;
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.media.bean.MediaInfo;
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.service.IMediaServerService;
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcPlayService;
@@ -24,6 +29,7 @@ import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import javax.sip.message.Response;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -39,6 +45,12 @@ public class StreamProxyPlayServiceImpl implements IStreamProxyPlayService {
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private HookSubscribe subscribe;
@Autowired
private DynamicTask dynamicTask;
@Autowired
private UserSetting userSetting;
@@ -103,7 +115,26 @@ public class StreamProxyPlayServiceImpl implements IStreamProxyPlayService {
streamProxy.setEnableMp4(record);
}
return startProxy(streamProxy);
StreamInfo streamInfo = startProxy(streamProxy);
if (callback != null) {
// 设置流超时的定时任务
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;
}
@Override