Merge branch 'master' into 重构/1078

This commit is contained in:
648540858
2024-12-05 11:05:44 +08:00
71 changed files with 2115 additions and 1337 deletions

View File

@@ -1,14 +1,10 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Data;
import org.jetbrains.annotations.NotNull;
import java.time.Instant;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* @author lin
@@ -19,7 +15,7 @@ public class CatalogData {
* 命令序列号
*/
private int sn;
private int total;
private Integer total;
private Instant time;
private Device device;
private String errorMsg;

View File

@@ -0,0 +1,12 @@
package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Data;
@Data
public class Preset {
private String presetId;
private String presetName;
}

View File

@@ -1,28 +0,0 @@
package com.genersoft.iot.vmp.gb28181.bean;
/**
* @author chenjialing
*/
public class PresetQuerySipReq {
private String presetId;
private String presetName;
public String getPresetId() {
return presetId;
}
public void setPresetId(String presetId) {
this.presetId = presetId;
}
public String getPresetName() {
return presetName;
}
public void setPresetName(String presetName) {
this.presetName = presetName;
}
}

View File

@@ -175,15 +175,18 @@ public class SendRtpInfo {
return sendRtpItem;
}
public static SendRtpInfo getInstance(Integer localPort, MediaServer mediaServer, String ip, int port, String ssrc,
String deviceId, String platformId, Integer channelId, boolean isTcp, boolean rtcp,
public static SendRtpInfo getInstance(Integer localPort, MediaServer mediaServer, String ip, Integer port, String ssrc,
String deviceId, String platformId, Integer channelId, Boolean isTcp, Boolean rtcp,
String serverId) {
if (localPort == 0) {
return null;
}
SendRtpInfo sendRtpItem = new SendRtpInfo();
sendRtpItem.setIp(ip);
sendRtpItem.setPort(port);
if(port != null) {
sendRtpItem.setPort(port);
}
sendRtpItem.setSsrc(ssrc);
if (deviceId != null) {
sendRtpItem.setTargetId(deviceId);

View File

@@ -1,51 +1,31 @@
package com.genersoft.iot.vmp.gb28181.bean;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.Instant;
/**
* 摄像机同步状态
* @author lin
*/
@Data
@Schema(description = "摄像机同步状态")
public class SyncStatus {
@Schema(description = "总数")
private int total;
private Integer total;
@Schema(description = "当前更新多少")
private int current;
private Integer current;
@Schema(description = "错误描述")
private String errorMsg;
@Schema(description = "是否同步中")
private boolean syncIng;
private Boolean syncIng;
public int getTotal() {
return total;
}
@Schema(description = "时间")
private Instant time;
public void setTotal(int total) {
this.total = total;
}
public int getCurrent() {
return current;
}
public void setCurrent(int current) {
this.current = current;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public boolean isSyncIng() {
return syncIng;
}
public void setSyncIng(boolean syncIng) {
this.syncIng = syncIng;
}
}

View File

@@ -28,7 +28,10 @@ import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;
import javax.servlet.http.HttpServletRequest;
import javax.sip.message.Response;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
@@ -200,35 +203,44 @@ public class CommonChannelController {
@Operation(summary = "播放通道", security = @SecurityRequirement(name = JwtUtils.HEADER))
@GetMapping("/play")
public DeferredResult<WVPResult<StreamContent>> deleteChannelToGroupByGbDevice(Integer channelId){
public DeferredResult<WVPResult<StreamContent>> deleteChannelToGroupByGbDevice(HttpServletRequest request, Integer channelId){
Assert.notNull(channelId,"参数异常");
CommonGBChannel channel = channelService.getOne(channelId);
Assert.notNull(channel, "通道不存在");
DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
ErrorCallback<StreamInfo> callback = (code, msg, data) -> {
ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
if (code == InviteErrorCode.SUCCESS.getCode()) {
result.setResult(WVPResult.success(new StreamContent(data)));
WVPResult<StreamContent> wvpResult = WVPResult.success();
if (streamInfo != null) {
if (userSetting.getUseSourceIpAsStreamIp()) {
streamInfo=streamInfo.clone();//深拷贝
String host;
try {
URL url=new URL(request.getRequestURL().toString());
host=url.getHost();
} catch (MalformedURLException e) {
host=request.getLocalAddr();
}
streamInfo.channgeStreamIp(host);
}
if (!ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix())
&& !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) {
streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix());
}
wvpResult.setData(new StreamContent(streamInfo));
}else {
wvpResult.setCode(code);
wvpResult.setMsg(msg);
}
result.setResult(wvpResult);
}else {
result.setResult(WVPResult.fail(code, msg));
}
};
if (channel.getGbDeviceDbId() != null) {
// 国标通道
channelPlayService.playGbDeviceChannel(channel, callback);
} else if (channel.getStreamProxyId() != null) {
// 拉流代理
channelPlayService.playProxy(channel, callback);
} else if (channel.getStreamPushId() != null) {
// 推流
channelPlayService.playPush(channel, null, null, callback);
} else {
// 通道数据异常
log.error("[点播通用通道] 通道数据异常,无法识别通道来源: {}({})", channel.getGbName(), channel.getGbDeviceId());
throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error");
}
channelPlayService.play(channel, null, callback);
return result;
}
}

View File

@@ -144,9 +144,21 @@ public class DeviceQuery {
Device device = deviceService.getDeviceByDeviceId(deviceId);
boolean status = deviceService.isSyncRunning(deviceId);
// 已存在则返回进度
if (status) {
if (deviceService.isSyncRunning(deviceId)) {
SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId);
return WVPResult.success(channelSyncStatus);
WVPResult wvpResult = new WVPResult();
if (channelSyncStatus.getErrorMsg() != null) {
wvpResult.setCode(ErrorCode.ERROR100.getCode());
wvpResult.setMsg(channelSyncStatus.getErrorMsg());
}else if (channelSyncStatus.getTotal() == null || channelSyncStatus.getTotal() == 0){
wvpResult.setCode(ErrorCode.SUCCESS.getCode());
wvpResult.setMsg("等待通道信息...");
}else {
wvpResult.setCode(ErrorCode.SUCCESS.getCode());
wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
wvpResult.setData(channelSyncStatus);
}
return wvpResult;
}
deviceService.sync(device);
@@ -414,15 +426,18 @@ public class DeviceQuery {
SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId);
WVPResult<SyncStatus> wvpResult = new WVPResult<>();
if (channelSyncStatus == null) {
wvpResult.setCode(-1);
wvpResult.setMsg("同步尚未开始");
wvpResult.setCode(ErrorCode.ERROR100.getCode());
wvpResult.setMsg("同步不存在");
}else if (channelSyncStatus.getErrorMsg() != null) {
wvpResult.setCode(ErrorCode.ERROR100.getCode());
wvpResult.setMsg(channelSyncStatus.getErrorMsg());
}else if (channelSyncStatus.getTotal() == null || channelSyncStatus.getTotal() == 0){
wvpResult.setCode(ErrorCode.SUCCESS.getCode());
wvpResult.setMsg("等待通道信息...");
}else {
wvpResult.setCode(ErrorCode.SUCCESS.getCode());
wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
wvpResult.setData(channelSyncStatus);
if (channelSyncStatus.getErrorMsg() != null) {
wvpResult.setMsg(channelSyncStatus.getErrorMsg());
}
}
return wvpResult;
}

View File

@@ -18,16 +18,13 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@Tag(name = "媒体流相关")
@Controller
@RestController
@Slf4j
@RequestMapping(value = "/api/media")
public class MediaController {

View File

@@ -24,10 +24,10 @@ import javax.sip.SipException;
import java.text.ParseException;
import java.util.UUID;
@Tag(name = "云台控制")
@Tag(name = "前端设备控制")
@Slf4j
@RestController
@RequestMapping("/api/ptz")
@RequestMapping("/api/front-end")
public class PtzController {
@Autowired
@@ -39,30 +39,67 @@ public class PtzController {
@Autowired
private DeferredResultHolder resultHolder;
/***
* 云台控制
* @param deviceId 设备id
* @param channelId 通道id
* @param command 控制指令
* @param horizonSpeed 水平移动速度
* @param verticalSpeed 垂直移动速度
* @param zoomSpeed 缩放速度
*/
@Operation(summary = "通用前端控制命令(参考国标文档A.3.1指令格式)", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "cmdCode", description = "指令码(对应国标文档指令格式中的字节4)", required = true)
@Parameter(name = "parameter1", description = "数据一(对应国标文档指令格式中的字节5, 范围0-255)", required = true)
@Parameter(name = "parameter2", description = "数据二(对应国标文档指令格式中的字节6, 范围0-255)", required = true)
@Parameter(name = "combindCode2", description = "组合码二(对应国标文档指令格式中的字节7, 范围0-16)", required = true)
@GetMapping("/common/{deviceId}/{channelId}")
public void frontEndCommand(@PathVariable String deviceId,@PathVariable String channelId,Integer cmdCode, Integer parameter1, Integer parameter2, Integer combindCode2){
if (log.isDebugEnabled()) {
log.debug(String.format("设备云台控制 API调用deviceId%s channelId%s cmdCode%d parameter1%d parameter2%d",deviceId, channelId, cmdCode, parameter1, parameter2));
}
Device device = deviceService.getDeviceByDeviceId(deviceId);
if (parameter1 == null || parameter1 < 0 || parameter1 > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "parameter1 为 1-255的数字");
}
if (parameter2 == null || parameter2 < 0 || parameter2 > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "parameter1 为 1-255的数字");
}
if (combindCode2 == null || combindCode2 < 0 || combindCode2 > 16) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "parameter1 为 1-255的数字");
}
try {
cmder.frontEndCmd(device, channelId, cmdCode, parameter1, parameter2, combindCode2);
} catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[命令发送失败] 前端控制: {}", e.getMessage());
throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
}
}
@Operation(summary = "云台控制", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "command", description = "控制指令,允许值: left, right, up, down, upleft, upright, downleft, downright, zoomin, zoomout, stop", required = true)
@Parameter(name = "horizonSpeed", description = "水平速度", required = true)
@Parameter(name = "verticalSpeed", description = "垂直速度", required = true)
@Parameter(name = "zoomSpeed", description = "缩放速度", required = true)
@PostMapping("/control/{deviceId}/{channelId}")
public void ptz(@PathVariable String deviceId,@PathVariable String channelId, String command, int horizonSpeed, int verticalSpeed, int zoomSpeed){
@Parameter(name = "horizonSpeed", description = "水平速度(0-255)", required = true)
@Parameter(name = "verticalSpeed", description = "垂直速度(0-255)", required = true)
@Parameter(name = "zoomSpeed", description = "缩放速度(0-16)", required = true)
@GetMapping("/ptz/{deviceId}/{channelId}")
public void ptz(@PathVariable String deviceId,@PathVariable String channelId, String command, Integer horizonSpeed, Integer verticalSpeed, Integer zoomSpeed){
if (log.isDebugEnabled()) {
log.debug(String.format("设备云台控制 API调用deviceId%s channelId%s command%s horizonSpeed%d verticalSpeed%d zoomSpeed%d",deviceId, channelId, command, horizonSpeed, verticalSpeed, zoomSpeed));
}
Device device = deviceService.getDeviceByDeviceId(deviceId);
if (horizonSpeed == null) {
horizonSpeed = 100;
}else if (horizonSpeed < 0 || horizonSpeed > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "horizonSpeed 为 1-255的数字");
}
if (verticalSpeed == null) {
verticalSpeed = 100;
}else if (verticalSpeed < 0 || verticalSpeed > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "verticalSpeed 为 1-255的数字");
}
if (zoomSpeed == null) {
zoomSpeed = 16;
}else if (zoomSpeed < 0 || zoomSpeed > 16) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "zoomSpeed 为 1-255的数字");
}
int cmdCode = 0;
switch (command){
case "left":
@@ -103,44 +140,79 @@ public class PtzController {
default:
break;
}
try {
cmder.frontEndCmd(device, channelId, cmdCode, horizonSpeed, verticalSpeed, zoomSpeed);
} catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[命令发送失败] 云台控制: {}", e.getMessage());
throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
}
frontEndCommand(deviceId, channelId, cmdCode, horizonSpeed, verticalSpeed, zoomSpeed);
}
@Operation(summary = "通用前端控制命令", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Operation(summary = "光圈控制", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "cmdCode", description = "指令码", required = true)
@Parameter(name = "parameter1", description = "数据一", required = true)
@Parameter(name = "parameter2", description = "数据二", required = true)
@Parameter(name = "combindCode2", description = "组合码二", required = true)
@PostMapping("/front_end_command/{deviceId}/{channelId}")
public void frontEndCommand(@PathVariable String deviceId,@PathVariable String channelId,int cmdCode, int parameter1, int parameter2, int combindCode2){
@Parameter(name = "command", description = "控制指令,允许值: in, out, stop", required = true)
@Parameter(name = "speed", description = "光圈速度(0-255)", required = true)
@GetMapping("/fi/iris/{deviceId}/{channelId}")
public void iris(@PathVariable String deviceId,@PathVariable String channelId, String command, Integer speed){
if (log.isDebugEnabled()) {
log.debug(String.format("设备云台控制 API调用deviceId%s channelId%s cmdCode%d parameter1%d parameter2%d",deviceId, channelId, cmdCode, parameter1, parameter2));
log.debug("设备光圈控制 API调用deviceId{} channelId{} command{} speed{} ",deviceId, channelId, command, speed);
}
Device device = deviceService.getDeviceByDeviceId(deviceId);
try {
cmder.frontEndCmd(device, channelId, cmdCode, parameter1, parameter2, combindCode2);
} catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[命令发送失败] 前端控制: {}", e.getMessage());
throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
int cmdCode = 0x40;
switch (command){
case "in":
cmdCode = 0x44;
break;
case "out":
cmdCode = 0x48;
break;
case "stop":
speed = 0;
break;
default:
break;
}
frontEndCommand(deviceId, channelId, cmdCode, 0, speed, 0);
}
@Operation(summary = "聚焦控制", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "command", description = "控制指令,允许值: near, far, stop", required = true)
@Parameter(name = "speed", description = "聚焦速度(0-255)", required = true)
@GetMapping("/fi/focus/{deviceId}/{channelId}")
public void focus(@PathVariable String deviceId,@PathVariable String channelId, String command, Integer speed){
@Operation(summary = "预置位查询", security = @SecurityRequirement(name = JwtUtils.HEADER))
if (log.isDebugEnabled()) {
log.debug("设备聚焦控制 API调用deviceId{} channelId{} command{} speed{} ",deviceId, channelId, command, speed);
}
if (speed == null) {
speed = 100;
}else if (speed < 0 || speed > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "verticalSpeed 为 1-255的数字");
}
int cmdCode = 0x40;
switch (command){
case "near":
cmdCode = 0x42;
break;
case "far":
cmdCode = 0x41;
break;
case "stop":
speed = 0;
break;
default:
break;
}
frontEndCommand(deviceId, channelId, cmdCode, speed, 0, 0);
}
@Operation(summary = "查询预置位", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@GetMapping("/preset/query/{deviceId}/{channelId}")
public DeferredResult<String> presetQueryApi(@PathVariable String deviceId, @PathVariable String channelId) {
public DeferredResult<String> queryPreset(@PathVariable String deviceId, @PathVariable String channelId) {
if (log.isDebugEnabled()) {
log.debug("设备预置位查询API调用");
}
@@ -175,4 +247,248 @@ public class PtzController {
}
return result;
}
@Operation(summary = "预置位指令-设置预置位", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "presetId", description = "预置位编号(1-255)", required = true)
@GetMapping("/preset/add/{deviceId}/{channelId}")
public void addPreset(@PathVariable String deviceId, @PathVariable String channelId, Integer presetId) {
if (presetId == null || presetId < 1 || presetId > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "预置位编号必须为1-255之间的数字");
}
frontEndCommand(deviceId, channelId, 0x81, 1, presetId, 0);
}
@Operation(summary = "预置位指令-调用预置位", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "presetId", description = "预置位编号(1-255)", required = true)
@GetMapping("/preset/call/{deviceId}/{channelId}")
public void callPreset(@PathVariable String deviceId, @PathVariable String channelId, Integer presetId) {
if (presetId == null || presetId < 1 || presetId > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "预置位编号必须为1-255之间的数字");
}
frontEndCommand(deviceId, channelId, 0x82, 1, presetId, 0);
}
@Operation(summary = "预置位指令-删除预置位", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "presetId", description = "预置位编号(1-255)", required = true)
@GetMapping("/preset/delete/{deviceId}/{channelId}")
public void deletePreset(@PathVariable String deviceId, @PathVariable String channelId, Integer presetId) {
if (presetId == null || presetId < 1 || presetId > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "预置位编号必须为1-255之间的数字");
}
frontEndCommand(deviceId, channelId, 0x83, 1, presetId, 0);
}
@Operation(summary = "巡航指令-加入巡航点", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "cruiseId", description = "巡航组号(0-255)", required = true)
@Parameter(name = "presetId", description = "预置位编号(1-255)", required = true)
@GetMapping("/cruise/point/add/{deviceId}/{channelId}")
public void addCruisePoint(@PathVariable String deviceId, @PathVariable String channelId, Integer cruiseId, Integer presetId) {
if (presetId == null || cruiseId == null || presetId < 1 || presetId > 255 || cruiseId < 0 || cruiseId > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "编号必须为1-255之间的数字");
}
frontEndCommand(deviceId, channelId, 0x84, cruiseId, presetId, 0);
}
@Operation(summary = "巡航指令-删除一个巡航点", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "cruiseId", description = "巡航组号(1-255)", required = true)
@Parameter(name = "presetId", description = "预置位编号(0-255, 为0时删除整个巡航)", required = true)
@GetMapping("/cruise/point/delete/{deviceId}/{channelId}")
public void deleteCruisePoint(@PathVariable String deviceId, @PathVariable String channelId, Integer cruiseId, Integer presetId) {
if (presetId == null || presetId < 0 || presetId > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "预置位编号必须为0-255之间的数字, 为0时删除整个巡航");
}
if (cruiseId == null || cruiseId < 0 || cruiseId > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "巡航组号必须为0-255之间的数字");
}
frontEndCommand(deviceId, channelId, 0x85, cruiseId, presetId, 0);
}
@Operation(summary = "巡航指令-设置巡航速度", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "cruiseId", description = "巡航组号(0-255)", required = true)
@Parameter(name = "speed", description = "巡航速度(1-4095)", required = true)
@GetMapping("/cruise/speed/{deviceId}/{channelId}")
public void setCruiseSpeed(@PathVariable String deviceId, @PathVariable String channelId, Integer cruiseId, Integer speed) {
if (cruiseId == null || cruiseId < 0 || cruiseId > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "巡航组号必须为0-255之间的数字");
}
if (speed == null || speed < 1 || speed > 4095) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "巡航速度必须为1-4095之间的数字");
}
int parameter2 = speed & 0xFF;
int combindCode2 = speed >> 8;
frontEndCommand(deviceId, channelId, 0x86, cruiseId, parameter2, combindCode2);
}
@Operation(summary = "巡航指令-设置巡航停留时间", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "cruiseId", description = "巡航组号", required = true)
@Parameter(name = "time", description = "巡航停留时间(1-4095)", required = true)
@GetMapping("/cruise/time/{deviceId}/{channelId}")
public void setCruiseTime(@PathVariable String deviceId, @PathVariable String channelId, Integer cruiseId, Integer time) {
if (cruiseId == null || cruiseId < 0 || cruiseId > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "巡航组号必须为0-255之间的数字");
}
if (time == null || time < 1 || time > 4095) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "巡航停留时间必须为1-4095之间的数字");
}
int parameter2 = time & 0xFF;
int combindCode2 = time >> 8;
frontEndCommand(deviceId, channelId, 0x87, cruiseId, parameter2, combindCode2);
}
@Operation(summary = "巡航指令-开始巡航", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "cruiseId", description = "巡航组号)", required = true)
@GetMapping("/cruise/start/{deviceId}/{channelId}")
public void startCruise(@PathVariable String deviceId, @PathVariable String channelId, Integer cruiseId) {
if (cruiseId == null || cruiseId < 0 || cruiseId > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "巡航组号必须为0-255之间的数字");
}
frontEndCommand(deviceId, channelId, 0x88, cruiseId, 0, 0);
}
@Operation(summary = "巡航指令-停止巡航", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "cruiseId", description = "巡航组号", required = true)
@GetMapping("/cruise/stop/{deviceId}/{channelId}")
public void stopCruise(@PathVariable String deviceId, @PathVariable String channelId, Integer cruiseId) {
if (cruiseId == null || cruiseId < 0 || cruiseId > 255) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "巡航组号必须为0-255之间的数字");
}
frontEndCommand(deviceId, channelId, 0, 0, 0, 0);
}
@Operation(summary = "扫描指令-开始自动扫描", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "scanId", description = "扫描组号(0-255)", required = true)
@GetMapping("/scan/start/{deviceId}/{channelId}")
public void startScan(@PathVariable String deviceId, @PathVariable String channelId, Integer scanId) {
if (scanId == null || scanId < 0 || scanId > 255 ) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "扫描组号必须为0-255之间的数字");
}
frontEndCommand(deviceId, channelId, 0x89, scanId, 0, 0);
}
@Operation(summary = "扫描指令-停止自动扫描", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "scanId", description = "扫描组号(0-255)", required = true)
@GetMapping("/scan/stop/{deviceId}/{channelId}")
public void stopScan(@PathVariable String deviceId, @PathVariable String channelId, Integer scanId) {
if (scanId == null || scanId < 0 || scanId > 255 ) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "扫描组号必须为0-255之间的数字");
}
frontEndCommand(deviceId, channelId, 0, 0, 0, 0);
}
@Operation(summary = "扫描指令-设置自动扫描左边界", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "scanId", description = "扫描组号(0-255)", required = true)
@GetMapping("/scan/set/left/{deviceId}/{channelId}")
public void setScanLeft(@PathVariable String deviceId, @PathVariable String channelId, Integer scanId) {
if (scanId == null || scanId < 0 || scanId > 255 ) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "扫描组号必须为0-255之间的数字");
}
frontEndCommand(deviceId, channelId, 0x89, scanId, 1, 0);
}
@Operation(summary = "扫描指令-设置自动扫描右边界", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "scanId", description = "扫描组号(0-255)", required = true)
@GetMapping("/scan/set/right/{deviceId}/{channelId}")
public void setScanRight(@PathVariable String deviceId, @PathVariable String channelId, Integer scanId) {
if (scanId == null || scanId < 0 || scanId > 255 ) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "扫描组号必须为0-255之间的数字");
}
frontEndCommand(deviceId, channelId, 0x89, scanId, 2, 0);
}
@Operation(summary = "扫描指令-设置自动扫描速度", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "scanId", description = "扫描组号(0-255)", required = true)
@Parameter(name = "speed", description = "自动扫描速度(1-4095)", required = true)
@GetMapping("/scan/set/speed/{deviceId}/{channelId}")
public void setScanSpeed(@PathVariable String deviceId, @PathVariable String channelId, Integer scanId, Integer speed) {
if (scanId == null || scanId < 0 || scanId > 255 ) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "扫描组号必须为0-255之间的数字");
}
if (speed == null || speed < 1 || speed > 4095) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "自动扫描速度必须为1-4095之间的数字");
}
int parameter2 = speed & 0xFF;
int combindCode2 = speed >> 8;
frontEndCommand(deviceId, channelId, 0x8A, scanId, parameter2, combindCode2);
}
@Operation(summary = "辅助开关控制指令-雨刷控制", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "command", description = "控制指令,允许值: on, off", required = true)
@GetMapping("/wiper/{deviceId}/{channelId}")
public void wiper(@PathVariable String deviceId,@PathVariable String channelId, String command){
if (log.isDebugEnabled()) {
log.debug("辅助开关控制指令-雨刷控制 API调用deviceId{} channelId{} command{}",deviceId, channelId, command);
}
int cmdCode = 0;
switch (command){
case "on":
cmdCode = 0x8c;
break;
case "off":
cmdCode = 0x8d;
break;
default:
break;
}
frontEndCommand(deviceId, channelId, cmdCode, 1, 0, 0);
}
@Operation(summary = "辅助开关控制指令", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "command", description = "控制指令,允许值: on, off", required = true)
@Parameter(name = "switchId", description = "开关编号", required = true)
@GetMapping("/auxiliary/{deviceId}/{channelId}")
public void auxiliarySwitch(@PathVariable String deviceId,@PathVariable String channelId, String command, Integer switchId){
if (log.isDebugEnabled()) {
log.debug("辅助开关控制指令-雨刷控制 API调用deviceId{} channelId{} command{}, switchId: {}",deviceId, channelId, command, switchId);
}
int cmdCode = 0;
switch (command){
case "on":
cmdCode = 0x8c;
break;
case "off":
cmdCode = 0x8d;
break;
default:
break;
}
frontEndCommand(deviceId, channelId, cmdCode, switchId, 0, 0);
}
}

View File

@@ -121,6 +121,7 @@ public interface CommonGBChannelMapper {
", gb_block = #{gbBlock}" +
", gb_address = #{gbAddress}" +
", gb_parental = #{gbParental}" +
", gb_parent_id = #{gbParentId}" +
", gb_safety_way = #{gbSafetyWay}" +
", gb_register_way = #{gbRegisterWay}" +
", gb_cert_num = #{gbCertNum}" +

View File

@@ -5,13 +5,11 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelReduce;
import com.genersoft.iot.vmp.gb28181.dao.provider.DeviceChannelProvider;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.streamPush.bean.StreamPush;
import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Map;
/**
* 用于存储设备通道信息
@@ -551,7 +549,7 @@ public interface DeviceChannelMapper {
"</script>")
void updateStreamGPS(List<GPSMsgInfo> gpsMsgInfoList);
@Update("UPDATE wvp_device_channel SET status=#{status} WHERE device_id=#{deviceId} AND channel_id=#{channelId}")
@Update("UPDATE wvp_device_channel SET status=#{status} WHERE device_db_id=#{deviceDbId} AND device_id=#{deviceId}")
void updateStatus(DeviceChannel channel);

View File

@@ -330,7 +330,11 @@ public interface DeviceMapper {
" FROM wvp_device de" +
" where 1 = 1 "+
" <if test='status != null'> AND de.on_line=${status}</if>"+
" <if test='query != null'> AND (coalesce(custom_name, name) LIKE '%${query}%' OR device_id LIKE '%${query}%' OR ip LIKE '%${query}%')</if> " +
" <if test='query != null'> AND (" +
" coalesce(custom_name, name) LIKE concat('%',#{query},'%') escape '/' " +
" OR device_id LIKE concat('%',#{query},'%') escape '/' " +
" OR ip LIKE concat('%',#{query},'%') escape '/')" +
"</if> " +
" order by create_time desc "+
" </script>")
List<Device> getDeviceList(@Param("query") String query, @Param("status") Boolean status);

View File

@@ -71,7 +71,7 @@ public interface PlatformMapper {
" ) as channel_count" +
" FROM wvp_platform pp where 1=1 " +
" <if test='query != null'> " +
" AND (pp.name LIKE concat('%',#{query},'%') OR pp.server_gb_id LIKE concat('%',#{query},'%') )</if> " +
" AND (pp.name LIKE concat('%',#{query},'%') escape '/' OR pp.server_gb_id LIKE concat('%',#{query},'%') escape '/' )</if> " +
" order by pp.id desc"+
" </script>")
List<Platform> queryList(@Param("query") String query);

View File

@@ -26,7 +26,7 @@ public interface RegionMapper {
@Select(value = {" <script>" +
"SELECT * from wvp_common_region WHERE 1=1 " +
" <if test='query != null'> AND (device_id LIKE concat('%',#{query},'%') OR name LIKE concat('%',#{query},'%'))</if> " +
" <if test='query != null'> AND (device_id LIKE concat('%',#{query},'%') escape '/' OR name LIKE concat('%',#{query},'%') escape '/')</if> " +
" <if test='parentId != null'> AND parent_device_id = #{parentId}</if> " +
"ORDER BY id " +
" </script>"})
@@ -79,7 +79,7 @@ public interface RegionMapper {
" where " +
" <if test='parentId != null'> parent_id = #{parentId} </if> " +
" <if test='parentId == null'> parent_id is null </if> " +
" <if test='query != null'> AND (device_id LIKE concat('%',#{query},'%') OR name LIKE concat('%',#{query},'%'))</if> " +
" <if test='query != null'> AND (device_id LIKE concat('%',#{query},'%') escape '/' OR name LIKE concat('%',#{query},'%') escape '/')</if> " +
" </script>")
List<RegionTree> queryForTree(@Param("query") String query, @Param("parentId") Integer parentId);

View File

@@ -124,8 +124,8 @@ public class ChannelProvider {
sqlBuild.append(BASE_SQL);
sqlBuild.append(" where channel_type = 0 ");
if (params.get("query") != null) {
sqlBuild.append(" AND (coalesce(gb_device_id, device_id) LIKE concat('%',#{query},'%')" +
" OR coalesce(gb_name, name) LIKE concat('%',#{query},'%') )")
sqlBuild.append(" AND (coalesce(gb_device_id, device_id) LIKE concat('%',#{query},'%') escape '/'" +
" OR coalesce(gb_name, name) LIKE concat('%',#{query},'%') escape '/' )")
;
}
if (params.get("online") != null && (Boolean)params.get("online")) {
@@ -158,8 +158,8 @@ public class ChannelProvider {
sqlBuild.append(BASE_SQL);
sqlBuild.append(" where channel_type = 0 ");
if (params.get("query") != null) {
sqlBuild.append(" AND (coalesce(gb_device_id, device_id) LIKE concat('%',#{query},'%')" +
" OR coalesce(gb_name, name) LIKE concat('%',#{query},'%') )")
sqlBuild.append(" AND (coalesce(gb_device_id, device_id) LIKE concat('%',#{query},'%') escape '/'" +
" OR coalesce(gb_name, name) LIKE concat('%',#{query},'%') escape '/' )")
;
}
if (params.get("online") != null && (Boolean)params.get("online")) {

View File

@@ -71,8 +71,8 @@ public class DeviceChannelProvider {
"OR (LENGTH(coalesce(dc.gb_device_id, dc.device_id))=LENGTH(#{civilCode}) + 2) AND coalesce(dc.gb_device_id, dc.device_id) LIKE concat(#{civilCode},'%'))");
}
if (params.get("query") != null && !ObjectUtils.isEmpty(params.get("query"))) {
sqlBuild.append(" AND (coalesce(dc.gb_device_id, dc.device_id) LIKE concat('%',#{query},'%')" +
" OR coalesce(dc.gb_name, dc.name) LIKE concat('%',#{query},'%'))")
sqlBuild.append(" AND (coalesce(dc.gb_device_id, dc.device_id) LIKE concat('%',#{query},'%') escape '/'" +
" OR coalesce(dc.gb_name, dc.name) LIKE concat('%',#{query},'%') escape '/')")
;
}
if (params.get("online") != null && (Boolean)params.get("online")) {

View File

@@ -10,6 +10,8 @@ public interface IGbChannelPlayService {
void start(CommonGBChannel channel, InviteInfo inviteInfo, Platform platform, ErrorCallback<StreamInfo> callback);
void play(CommonGBChannel channel, Platform platform, ErrorCallback<StreamInfo> callback);
void playGbDeviceChannel(CommonGBChannel channel, ErrorCallback<StreamInfo> callback);
void playProxy(CommonGBChannel channel, ErrorCallback<StreamInfo> callback);

View File

@@ -0,0 +1,21 @@
package com.genersoft.iot.vmp.gb28181.service;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.Preset;
import java.util.List;
public interface IPTZService {
List<Preset> queryPresetList(String deviceId, String channelDeviceId);
void addPreset(Preset preset);
void deletePreset(Integer qq);
void ptz(Device device, String channelId, int cmdCode, int horizonSpeed, int verticalSpeed, int zoomSpeed);
void frontEndCommand(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combindCode2);
}

View File

@@ -609,6 +609,11 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
parentId = channelId;
}
}
if (query != null) {
query = query.replaceAll("/", "//")
.replaceAll("%", "/%")
.replaceAll("_", "/_");
}
List<DeviceChannel> all = channelMapper.queryChannels(deviceDbId, civilCode, businessGroupId, parentId, query, channelType, online,null);
return new PageInfo<>(all);
}
@@ -624,7 +629,11 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
if (device == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到设备:" + deviceId);
}
// 获取到所有正在播放的流
if (query != null) {
query = query.replaceAll("/", "//")
.replaceAll("%", "/%")
.replaceAll("_", "/_");
}
PageHelper.startPage(page, count);
List<DeviceChannel> all = channelMapper.queryChannels(device.getId(), null,null, null, query, hasSubChannel, online,null);
return new PageInfo<>(all);

View File

@@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.gb28181.service.impl;
import com.alibaba.fastjson2.JSON;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
@@ -19,7 +20,6 @@ import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask;
import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
@@ -34,7 +34,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
@@ -51,9 +50,6 @@ import java.util.concurrent.TimeUnit;
@DS("master")
public class DeviceServiceImpl implements IDeviceService {
@Autowired
private SIPCommander cmder;
@Autowired
private DynamicTask dynamicTask;
@@ -327,7 +323,8 @@ public class DeviceServiceImpl implements IDeviceService {
@Override
public void sync(Device device) {
if (catalogResponseMessageHandler.isSyncRunning(device.getDeviceId())) {
log.info("开启同步时发现同步已经存在");
SyncStatus syncStatus = catalogResponseMessageHandler.getChannelSyncProgress(device.getDeviceId());
log.info("[同步通道] 同步已存在, 设备: {}, 同步信息: {}", device.getDeviceId(), JSON.toJSON(syncStatus));
return;
}
int sn = (int)((Math.random()*9+1)*100000);
@@ -335,6 +332,7 @@ public class DeviceServiceImpl implements IDeviceService {
try {
sipCommander.catalogQuery(device, sn, event -> {
String errorMsg = String.format("同步通道失败,错误码: %s, %s", event.statusCode, event.msg);
log.info("[同步通道]失败,编号: {}, 错误码: {}, {}", device.getDeviceId(), event.statusCode, event.msg);
catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), sn, errorMsg);
});
} catch (SipException | InvalidArgumentException | ParseException e) {
@@ -524,6 +522,11 @@ public class DeviceServiceImpl implements IDeviceService {
@Override
public PageInfo<Device> getAll(int page, int count, String query, Boolean status) {
PageHelper.startPage(page, count);
if (query != null) {
query = query.replaceAll("/", "//")
.replaceAll("%", "/%")
.replaceAll("_", "/_");
}
List<Device> all = deviceMapper.getDeviceList(query, status);
return new PageInfo<>(all);
}

View File

@@ -39,20 +39,7 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
}
log.info("[点播通用通道] 类型:{} 通道: {}({})", inviteInfo.getSessionName(), channel.getGbName(), channel.getGbDeviceId());
if ("Play".equalsIgnoreCase(inviteInfo.getSessionName())) {
if (channel.getGbDeviceDbId() != null) {
// 国标通道
playGbDeviceChannel(channel, callback);
} else if (channel.getStreamProxyId() != null) {
// 拉流代理
playProxy(channel, callback);
} else if (channel.getStreamPushId() != null) {
// 推流
playPush(channel, platform.getServerGBId(), platform.getName(), callback);
} else {
// 通道数据异常
log.error("[点播通用通道] 通道数据异常,无法识别通道来源: {}({})", channel.getGbName(), channel.getGbDeviceId());
throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error");
}
play(channel, platform, callback);
}else if ("Playback".equals(inviteInfo.getSessionName())) {
if (channel.getGbDeviceDbId() != null) {
// 国标通道
@@ -101,6 +88,29 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
}
}
@Override
public void play(CommonGBChannel channel, Platform platform, ErrorCallback<StreamInfo> callback) {
if (channel.getGbDeviceDbId() != null) {
// 国标通道
playGbDeviceChannel(channel, callback);
} else if (channel.getStreamProxyId() != null) {
// 拉流代理
playProxy(channel, callback);
} else if (channel.getStreamPushId() != null) {
if (platform != null) {
// 推流
playPush(channel, platform.getServerGBId(), platform.getName(), callback);
}else {
// 推流
playPush(channel, null, null, callback);
}
} else {
// 通道数据异常
log.error("[点播通用通道] 通道数据异常,无法识别通道来源: {}({})", channel.getGbName(), channel.getGbDeviceId());
throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error");
}
}
@Override
public void playGbDeviceChannel(CommonGBChannel channel, ErrorCallback<StreamInfo> callback){
// 国标通道

View File

@@ -390,6 +390,11 @@ public class GbChannelServiceImpl implements IGbChannelService {
@Override
public PageInfo<CommonGBChannel> queryListByCivilCode(int page, int count, String query, Boolean online, Integer channelType, String civilCode) {
PageHelper.startPage(page, count);
if (query != null) {
query = query.replaceAll("/", "//")
.replaceAll("%", "/%")
.replaceAll("_", "/_");
}
List<CommonGBChannel> all = commonGBChannelMapper.queryListByCivilCode(query, online, channelType, civilCode);
return new PageInfo<>(all);
}
@@ -397,6 +402,11 @@ public class GbChannelServiceImpl implements IGbChannelService {
@Override
public PageInfo<CommonGBChannel> queryListByParentId(int page, int count, String query, Boolean online, Integer channelType, String groupDeviceId) {
PageHelper.startPage(page, count);
if (query != null) {
query = query.replaceAll("/", "//")
.replaceAll("%", "/%")
.replaceAll("_", "/_");
}
List<CommonGBChannel> all = commonGBChannelMapper.queryListByParentId(query, online, channelType, groupDeviceId);
return new PageInfo<>(all);
}

View File

@@ -0,0 +1,62 @@
package com.genersoft.iot.vmp.gb28181.service.impl;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.Preset;
import com.genersoft.iot.vmp.gb28181.service.IPTZService;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
import java.text.ParseException;
import java.util.Collections;
import java.util.List;
@Slf4j
@Service
public class PTZServiceImpl implements IPTZService {
@Autowired
private SIPCommander cmder;
@Override
public void ptz(Device device, String channelId, int cmdCode, int horizonSpeed, int verticalSpeed, int zoomSpeed) {
try {
cmder.frontEndCmd(device, channelId, cmdCode, horizonSpeed, verticalSpeed, zoomSpeed);
} catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[命令发送失败] 云台控制: {}", e.getMessage());
throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
}
}
@Override
public void frontEndCommand(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combindCode2) {
try {
cmder.frontEndCmd(device, channelId, cmdCode, parameter1, parameter2, combindCode2);
} catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[命令发送失败] 前端控制: {}", e.getMessage());
throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
}
}
@Override
public List<Preset> queryPresetList(String deviceId, String channelDeviceId) {
return Collections.emptyList();
}
@Override
public void addPreset(Preset preset) {
}
@Override
public void deletePreset(Integer qq) {
}
}

View File

@@ -159,6 +159,11 @@ public class PlatformServiceImpl implements IPlatformService {
@Override
public PageInfo<Platform> queryPlatformList(int page, int count, String query) {
PageHelper.startPage(page, count);
if (query != null) {
query = query.replaceAll("/", "//")
.replaceAll("%", "/%")
.replaceAll("_", "/_");
}
List<Platform> all = platformMapper.queryList(query);
return new PageInfo<>(all);
}

View File

@@ -488,8 +488,7 @@ public class PlayServiceImpl implements IPlayService {
log.info("[语音对讲]开始 获取发流端口失败 deviceId: {}, channelId: {},", device.getDeviceId(), channel.getDeviceId());
return;
}
sendRtpInfo.setOnlyAudio(true);
sendRtpInfo.setPt(8);
sendRtpInfo.setStatus(1);
@@ -518,7 +517,14 @@ public class PlayServiceImpl implements IPlayService {
}, userSetting.getPlayTimeout());
try {
mediaServerService.startSendRtpPassive(mediaServerItem, sendRtpInfo, userSetting.getPlayTimeout() * 1000);
Integer localPort = mediaServerService.startSendRtpPassive(mediaServerItem, sendRtpInfo, userSetting.getPlayTimeout() * 1000);
if (localPort == null || localPort <= 0) {
timeoutCallback.run();
mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpInfo.getSsrc());
sessionManager.removeByStream(sendRtpInfo.getStream());
return;
}
sendRtpInfo.setPort(localPort);
}catch (ControllerException e) {
mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpInfo.getSsrc());
log.info("[语音对讲]失败 deviceId: {}, channelId: {}", device.getDeviceId(), channel.getDeviceId());

View File

@@ -97,6 +97,11 @@ public class RegionServiceImpl implements IRegionService {
@Override
public PageInfo<Region> query(String query, int page, int count) {
PageHelper.startPage(page, count);
if (query != null) {
query = query.replaceAll("/", "//")
.replaceAll("%", "/%")
.replaceAll("_", "/_");
}
List<Region> regionList = regionMapper.query(query, null);
return new PageInfo<>(regionList);
}
@@ -140,6 +145,11 @@ public class RegionServiceImpl implements IRegionService {
@Override
public List<RegionTree> queryForTree(String query, Integer parent, Boolean hasChannel) {
if (query != null) {
query = query.replaceAll("/", "//")
.replaceAll("%", "/%")
.replaceAll("_", "/_");
}
List<RegionTree> regionList = regionMapper.queryForTree(query, parent);
if (parent != null && hasChannel != null && hasChannel) {
Region parentRegion = regionMapper.queryOne(parent);

View File

@@ -170,11 +170,16 @@ public class CatalogDataManager implements CommandLineRunner {
syncStatus.setCurrent(catalogData.getRedisKeysForChannel().size());
syncStatus.setTotal(catalogData.getTotal());
syncStatus.setErrorMsg(catalogData.getErrorMsg());
if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end)) {
syncStatus.setTime(catalogData.getTime());
if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.ready) || catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end)) {
syncStatus.setSyncIng(false);
}else {
syncStatus.setSyncIng(true);
}
if (catalogData.getErrorMsg() != null) {
// 失败的同步信息,返回一次后直接移除
dataMap.remove(key);
}
return syncStatus;
}
}
@@ -237,7 +242,8 @@ public class CatalogDataManager implements CommandLineRunner {
catalogData.setErrorMsg(errorMsg);
}
}
if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end) && catalogData.getTime().isBefore(instantBefore30S)) { // 超过三十秒如果标记为end则删除
if ((catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end) || catalogData.getStatus().equals(CatalogData.CatalogDataStatus.ready))
&& catalogData.getTime().isBefore(instantBefore30S)) { // 超过三十秒如果标记为end则删除
dataMap.remove(dataKey);
Set<String> redisKeysForChannel = catalogData.getRedisKeysForChannel();
if (redisKeysForChannel != null && !redisKeysForChannel.isEmpty()) {

View File

@@ -8,7 +8,6 @@ import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
@@ -91,17 +90,14 @@ public class SSRCFactory {
* 获取后四位数SN,随机数
*/
private String getSN(String mediaServerId) {
String sn = null;
String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId;
Long size = redisTemplate.opsForSet().size(redisKey);
if (size == null || size == 0) {
throw new RuntimeException("ssrc已经用完");
} else {
// 在集合中移除并返回一个随机成员。
sn = (String) redisTemplate.opsForSet().pop(redisKey);
redisTemplate.opsForSet().remove(redisKey, sn);
return redisTemplate.opsForSet().pop(redisKey);
}
return sn;
}
/**

View File

@@ -142,15 +142,10 @@ public class SIPCommander implements ISIPCommander {
builder.append(strTmp, 0, 2);
strTmp = String.format("%02X", parameter2);
builder.append(strTmp, 0, 2);
//优化zoom变倍速率
if ((combineCode2 > 0) && (combineCode2 <16))
{
combineCode2 = 16;
}
strTmp = String.format("%X", combineCode2);
builder.append(strTmp, 0, 1).append("0");
strTmp = String.format("%02X", combineCode2 << 4);
builder.append(strTmp, 0, 2);
//计算校验码
int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 & 0XF0)) % 0X100;
int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 << 4)) % 0X100;
strTmp = String.format("%02X", checkCode);
builder.append(strTmp, 0, 2);
return builder.toString();
@@ -986,8 +981,6 @@ public class SIPCommander implements ISIPCommander {
catalogXml.append(" <DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
catalogXml.append("</Query>\r\n");
Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);

View File

@@ -303,7 +303,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
Media media = mediaDescription.getMedia();
Vector mediaFormats = media.getMediaFormats(false);
if (mediaFormats.contains("96")) {
if (mediaFormats.contains("96") || mediaFormats.contains("8")) {
port = media.getMediaPort();
//String mediaType = media.getMediaType();
String protocol = media.getProtocol();

View File

@@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.respon
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.Platform;
import com.genersoft.iot.vmp.gb28181.bean.PresetQuerySipReq;
import com.genersoft.iot.vmp.gb28181.bean.Preset;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
@@ -79,11 +79,11 @@ public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent
return;
}
int sumNum = Integer.parseInt(presetListNumElement.attributeValue("Num"));
List<PresetQuerySipReq> presetQuerySipReqList = new ArrayList<>();
List<Preset> presetQuerySipReqList = new ArrayList<>();
if (sumNum > 0) {
for (Iterator<Element> presetIterator = presetListNumElement.elementIterator(); presetIterator.hasNext(); ) {
Element itemListElement = presetIterator.next();
PresetQuerySipReq presetQuerySipReq = new PresetQuerySipReq();
Preset presetQuerySipReq = new Preset();
for (Iterator<Element> itemListIterator = itemListElement.elementIterator(); itemListIterator.hasNext(); ) {
// 遍历item
Element itemOne = itemListIterator.next();

View File

@@ -33,6 +33,10 @@ public class MediaInfo {
private Integer width;
@Schema(description = "视频高度")
private Integer height;
@Schema(description = "FPS")
private Integer fps;
@Schema(description = "丢包率")
private Integer loss;
@Schema(description = "音频编码类型")
private String audioCodec;
@Schema(description = "音频通道数")
@@ -58,6 +62,7 @@ public class MediaInfo {
@Schema(description = "服务ID")
private String serverId;
public static MediaInfo getInstance(JSONObject jsonObject, MediaServer mediaServer, String serverId) {
MediaInfo mediaInfo = new MediaInfo();
mediaInfo.setMediaServer(mediaServer);
@@ -111,7 +116,14 @@ public class MediaInfo {
Integer codecType = trackJson.getInteger("codec_type");
Integer sampleRate = trackJson.getInteger("sample_rate");
Integer height = trackJson.getInteger("height");
Integer width = trackJson.getInteger("height");
Integer width = trackJson.getInteger("width");
Integer fps = trackJson.getInteger("fps");
Integer loss = trackJson.getInteger("loss");
Integer frames = trackJson.getInteger("frames");
Long keyFrames = trackJson.getLongValue("key_frames");
Integer gop_interval_ms = trackJson.getInteger("gop_interval_ms");
Long gop_size = trackJson.getLongValue("gop_size");
Long duration = trackJson.getLongValue("duration");
if (channels != null) {
mediaInfo.setAudioChannels(channels);
@@ -125,6 +137,12 @@ public class MediaInfo {
if (width != null) {
mediaInfo.setWidth(width);
}
if (fps != null) {
mediaInfo.setFps(fps);
}
if (loss != null) {
mediaInfo.setLoss(loss);
}
if (duration > 0L) {
mediaInfo.setDuration(duration);
}

View File

@@ -58,7 +58,7 @@ public interface IMediaNodeServerService {
Map<String, String> getFFmpegCMDs(MediaServer mediaServer);
void startSendRtpPassive(MediaServer mediaServer, SendRtpInfo sendRtpItem, Integer timeout);
Integer startSendRtpPassive(MediaServer mediaServer, SendRtpInfo sendRtpItem, Integer timeout);
void startSendRtpStream(MediaServer mediaServer, SendRtpInfo sendRtpItem);

View File

@@ -142,7 +142,7 @@ public interface IMediaServerService {
Boolean isStreamReady(MediaServer mediaServer, String rtp, String streamId);
void startSendRtpPassive(MediaServer mediaServer, SendRtpInfo sendRtpItem, Integer timeout);
Integer startSendRtpPassive(MediaServer mediaServer, SendRtpInfo sendRtpItem, Integer timeout);
void startSendRtp(MediaServer mediaServer, SendRtpInfo sendRtpItem);

View File

@@ -14,7 +14,6 @@ import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerDeleteEvent;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOfflineEvent;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOnlineEvent;
@@ -318,11 +317,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
redisTemplate.opsForHash().put(key, mediaServerInDataBase.getId(), mediaServerInDataBase);
if (mediaServerInDataBase.isStatus()) {
resetOnlineServerItem(mediaServerInDataBase);
}else {
// 发送事件
MediaServerChangeEvent event = new MediaServerChangeEvent(this);
event.setMediaServerItemList(mediaServerInDataBase);
applicationEventPublisher.publishEvent(event);
}
}
@@ -444,11 +438,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
mediaServerMapper.add(mediaServer);
if (mediaServer.isStatus()) {
mediaNodeServerService.online(mediaServer);
}else {
// 发送事件
MediaServerChangeEvent event = new MediaServerChangeEvent(this);
event.setMediaServerItemList(mediaServer);
applicationEventPublisher.publishEvent(event);
}
}
@@ -878,13 +867,13 @@ public class MediaServerServiceImpl implements IMediaServerService {
}
@Override
public void startSendRtpPassive(MediaServer mediaServer, SendRtpInfo sendRtpItem, Integer timeout) {
public Integer startSendRtpPassive(MediaServer mediaServer, SendRtpInfo sendRtpItem, Integer timeout) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
log.info("[startSendRtpPassive] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类");
}
mediaNodeServerService.startSendRtpPassive(mediaServer, sendRtpItem, timeout);
return mediaNodeServerService.startSendRtpPassive(mediaServer, sendRtpItem, timeout);
}
@Override

View File

@@ -118,15 +118,6 @@ public class ZLMHttpHookListener {
}
}
/**
* rtsp/rtmp流注册或注销时触发此事件此事件对回复不敏感。
*/
// @ResponseBody
// @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
// public HookResult onStreamChanged(@RequestBody JSONObject param) {
// System.out.println(11);
// return HookResult.SUCCESS();
// }
/**
* rtsp/rtmp流注册或注销时触发此事件此事件对回复不敏感。
*/
@@ -299,7 +290,7 @@ public class ZLMHttpHookListener {
@ResponseBody
@PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8")
public HookResult onRecordMp4(HttpServletRequest request, @RequestBody OnRecordMp4HookParam param) {
log.info("[ZLM HOOK] 录像完成事件:{}->{}", param.getMediaServerId(), param.getFile_path());
log.info("[ZLM HOOK] 录像完成:时长: {}, {}->{}",param.getTime_len(), param.getMediaServerId(), param.getFile_path());
try {
MediaServer mediaServerItem = mediaServerService.getOne(param.getMediaServerId());

View File

@@ -180,7 +180,7 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService {
if (mediaList.getInteger("code") == 0) {
JSONArray data = mediaList.getJSONArray("data");
if (data == null) {
return null;
return streamInfoList;
}
JSONObject mediaJSON = data.getJSONObject(0);
MediaInfo mediaInfo = MediaInfo.getInstance(mediaJSON, mediaServer, userSetting.getServerId());
@@ -329,7 +329,7 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService {
}
@Override
public void startSendRtpPassive(MediaServer mediaServer, SendRtpInfo sendRtpItem, Integer timeout) {
public Integer startSendRtpPassive(MediaServer mediaServer, SendRtpInfo sendRtpItem, Integer timeout) {
Map<String, Object> param = new HashMap<>(12);
param.put("vhost","__defaultVhost__");
param.put("app", sendRtpItem.getApp());
@@ -361,6 +361,7 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService {
log.info("调用ZLM-TCP被动推流接口, 结果: {}", jsonObject);
log.info("启动监听TCP被动推流成功[ {}/{} ]{}->{}:{}, " , sendRtpItem.getApp(), sendRtpItem.getStream(),
jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port"));
return jsonObject.getInteger("local_port");
}
@Override

View File

@@ -1,19 +1,13 @@
package com.genersoft.iot.vmp.media.zlm.dto.hook;
import lombok.Data;
/**
* zlm hook事件的参数
* @author lin
*/
@Data
public class HookParam {
private String mediaServerId;
public String getMediaServerId() {
return mediaServerId;
}
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
}
}

View File

@@ -1,84 +1,48 @@
package com.genersoft.iot.vmp.media.zlm.dto.hook;
import lombok.Getter;
import lombok.Setter;
/**
* zlm hook事件中的on_publish事件的参数
* @author lin
*/
public class OnPublishHookParam extends HookParam{
@Getter
@Setter
private String id;
@Getter
@Setter
private String app;
@Getter
@Setter
private String stream;
@Getter
@Setter
private String ip;
@Getter
@Setter
private String params;
@Getter
@Setter
private int port;
@Getter
@Setter
private String schema;
@Getter
@Setter
private String vhost;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
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 String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getSchema() {
return schema;
}
public void setSchema(String schema) {
this.schema = schema;
}
public String getVhost() {
return vhost;
}
public void setVhost(String vhost) {
this.vhost = vhost;
}
@Override
public String toString() {
return "OnPublishHookParam{" +

View File

@@ -71,6 +71,11 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
}
PageHelper.startPage(page, count);
if (query != null) {
query = query.replaceAll("/", "//")
.replaceAll("%", "/%")
.replaceAll("_", "/_");
}
List<CloudRecordItem> all = cloudRecordServiceMapper.getList(query, app, stream, startTimeStamp, endTimeStamp,
callId, mediaServerItems, null);
return new PageInfo<>(all);

View File

@@ -96,6 +96,12 @@ public class MediaServiceImpl implements IMediaService {
public ResultForOnPublish authenticatePublish(MediaServer mediaServer, String app, String stream, String params) {
// 推流鉴权的处理
if (!"rtp".equals(app)) {
if ("talk".equals(app) && stream.endsWith("_talk")) {
ResultForOnPublish result = new ResultForOnPublish();
result.setEnable_mp4(false);
result.setEnable_audio(true);
return result;
}
StreamProxy streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(app, stream);
if (streamProxyItem != null) {
ResultForOnPublish result = new ResultForOnPublish();

View File

@@ -41,7 +41,7 @@ public interface CloudRecordServiceMapper {
"select * " +
" from wvp_cloud_record " +
" where 0 = 0" +
" <if test='query != null'> AND (app LIKE concat('%',#{query},'%') OR stream LIKE concat('%',#{query},'%') )</if> " +
" <if test='query != null'> AND (app LIKE concat('%',#{query},'%') escape '/' OR stream LIKE concat('%',#{query},'%') escape '/' )</if> " +
" <if test= 'app != null '> and app=#{app}</if>" +
" <if test= 'stream != null '> and stream=#{stream}</if>" +
" <if test= 'startTimeStamp != null '> and end_time &gt;= #{startTimeStamp}</if>" +
@@ -53,7 +53,7 @@ public interface CloudRecordServiceMapper {
" <if test= 'ids != null ' > and id in " +
" <foreach collection='ids' item='item' open='(' separator=',' close=')' > #{item}</foreach>" +
" </if>" +
" order by start_time ASC" +
" order by start_time desc" +
" </script>")
List<CloudRecordItem> getList(@Param("query") String query, @Param("app") String app, @Param("stream") String stream,
@Param("startTimeStamp")Long startTimeStamp, @Param("endTimeStamp")Long endTimeStamp,

View File

@@ -30,7 +30,7 @@ import java.util.Map;
* 拉流代理接口
*/
@Tag(name = "拉流代理", description = "")
@Controller
@RestController
@Slf4j
@RequestMapping(value = "/api/proxy")
public class StreamProxyController {

View File

@@ -48,8 +48,8 @@ public interface StreamProxyMapper {
@SelectProvider(type = StreamProxyProvider.class, method = "selectOneByAppAndStream")
StreamProxy selectOneByAppAndStream(@Param("app") String app, @Param("stream") String stream);
@SelectProvider(type = StreamProxyProvider.class, method = "selectForEnableInMediaServer")
List<StreamProxy> selectForEnableInMediaServer(@Param("mediaServerId") String mediaServerId, @Param("enable") boolean enable);
@SelectProvider(type = StreamProxyProvider.class, method = "selectForPushingInMediaServer")
List<StreamProxy> selectForPushingInMediaServer(@Param("mediaServerId") String mediaServerId, @Param("enable") boolean enable);
@Select("select count(1) from wvp_stream_proxy")

View File

@@ -19,9 +19,8 @@ public class StreamProxyProvider {
return getBaseSelectSql() + " WHERE st.id = " + params.get("id");
}
public String selectForEnableInMediaServer(Map<String, Object> params ){
return getBaseSelectSql() + String.format(" WHERE st.enable=%s and st.media_server_id= '%s' order by st.create_time desc",
params.get("enable"), params.get("mediaServerId"));
public String selectForPushingInMediaServer(Map<String, Object> params ){
return getBaseSelectSql() + " WHERE st.pulling=1 and st.media_server_id=#{mediaServerId} order by st.create_time desc";
}
public String selectOneByAppAndStream(Map<String, Object> params ){
@@ -36,13 +35,13 @@ public class StreamProxyProvider {
if (params.get("query") != null) {
sqlBuild.append(" AND ")
.append(" (")
.append(" st.app LIKE ").append("'%").append(params.get("query")).append("%'")
.append(" st.app LIKE ").append("'%").append(params.get("query")).append("%' escape '/'")
.append(" OR")
.append(" st.stream LIKE ").append("'%").append(params.get("query")).append("%'")
.append(" st.stream LIKE ").append("'%").append(params.get("query")).append("%' escape '/'")
.append(" OR")
.append(" wdc.gb_device_id LIKE ").append("'%").append(params.get("query")).append("%'")
.append(" wdc.gb_device_id LIKE ").append("'%").append(params.get("query")).append("%' escape '/'")
.append(" OR")
.append(" wdc.gb_name LIKE ").append("'%").append(params.get("query")).append("%'")
.append(" wdc.gb_name LIKE ").append("'%").append(params.get("query")).append("%' escape '/'")
.append(" )")
;
}

View File

@@ -230,22 +230,17 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
gbChannelService.add(streamProxy.buildCommonGBChannel());
}
}
// 判断是否需要重启代理
if (!streamProxyInDb.getApp().equals(streamProxy.getApp())
|| !streamProxyInDb.getStream().equals(streamProxy.getStream())
|| (streamProxyInDb.getMediaServerId() != null && streamProxyInDb.getMediaServerId().equals(streamProxy.getMediaServerId()))
|| (streamProxyInDb.getMediaServerId() == null && streamProxy.getMediaServerId() != null)
) {
// 变化则重启代理
playService.stopProxy(streamProxyInDb);
playService.startProxy(streamProxy);
}
return true;
}
@Override
public PageInfo<StreamProxy> getAll(Integer page, Integer count, String query, Boolean pulling, String mediaServerId) {
PageHelper.startPage(page, count);
if (query != null) {
query = query.replaceAll("/", "//")
.replaceAll("%", "/%")
.replaceAll("_", "/_");
}
List<StreamProxy> all = streamProxyMapper.selectAll(query, pulling, mediaServerId);
return new PageInfo<>(all);
}
@@ -291,7 +286,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
// 这里主要是控制数据库/redis缓存/以及zlm中存在的代理流 三者状态一致。以数据库中数据为根本
redisCatchStorage.removeStream(mediaServer.getId(), "PULL");
List<StreamProxy> streamProxies = streamProxyMapper.selectForEnableInMediaServer(mediaServer.getId(), true);
List<StreamProxy> streamProxies = streamProxyMapper.selectForPushingInMediaServer(mediaServer.getId(), true);
if (streamProxies.isEmpty()) {
return;
}
@@ -348,18 +343,16 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
streamProxyMapper.deleteByList(streamProxiesForRemove);
}
if (!streamProxyMapForDb.isEmpty()) {
for (StreamProxy streamProxy : streamProxyMapForDb.values()) {
log.info("恢复流代理," + streamProxy.getApp() + "/" + streamProxy.getStream());
mediaServerService.startProxy(mediaServer, streamProxy);
streamProxyMapper.offline(streamProxy.getId());
}
}
}
@Override
public void zlmServerOffline(MediaServer mediaServer) {
List<StreamProxy> streamProxies = streamProxyMapper.selectForEnableInMediaServer(mediaServer.getId(), true);
List<StreamProxy> streamProxies = streamProxyMapper.selectForPushingInMediaServer(mediaServer.getId(), true);
// 清理redis相关的缓存
redisCatchStorage.removeStream(mediaServer.getId(), "PULL");
@@ -382,11 +375,14 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
streamProxiesForSendMessage.add(streamProxy);
}
}
// 移除开启了无人观看自动移除的流
streamProxyMapper.deleteByList(streamProxiesForRemove);
// 修改国标关联的国标通道的状态
gbChannelService.offline(channelListForOffline);
if (!streamProxiesForRemove.isEmpty()) {
// 移除开启了无人观看自动移除的流
streamProxyMapper.deleteByList(streamProxiesForRemove);
}
if (!streamProxiesForRemove.isEmpty()) {
// 修改国标关联的国标通道的状态
gbChannelService.offline(channelListForOffline);
}
if (!streamProxiesForSendMessage.isEmpty()) {
for (StreamProxy streamProxy : streamProxiesForSendMessage) {
JSONObject jsonObject = new JSONObject();

View File

@@ -84,7 +84,7 @@ public class StreamPush extends CommonGBChannel implements Comparable<StreamPush
- DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(streamPushItem.getCreateTime())).intValue();
}
public StreamPush getInstance(StreamInfo streamInfo) {
public static StreamPush getInstance(StreamInfo streamInfo) {
StreamPush streamPush = new StreamPush();
streamPush.setApp(streamInfo.getApp());
if (streamInfo.getMediaServer() != null) {

View File

@@ -43,7 +43,7 @@ import java.util.Map;
import java.util.UUID;
@Tag(name = "推流信息管理")
@Controller
@RestController
@Slf4j
@RequestMapping(value = "/api/push")
public class StreamPushController {

View File

@@ -48,8 +48,8 @@ public interface StreamPushMapper {
" on st.id = wdc.stream_push_id " +
" WHERE " +
" 1=1 " +
" <if test='query != null'> AND (st.app LIKE concat('%',#{query},'%') OR st.stream LIKE concat('%',#{query},'%') " +
" OR wdc.gb_device_id LIKE concat('%',#{query},'%') OR wdc.gb_name LIKE concat('%',#{query},'%'))</if> " +
" <if test='query != null'> AND (st.app LIKE concat('%',#{query},'%') escape '/' OR st.stream LIKE concat('%',#{query},'%') escape '/' " +
" OR wdc.gb_device_id LIKE concat('%',#{query},'%') escape '/' OR wdc.gb_name LIKE concat('%',#{query},'%') escape '/')</if> " +
" <if test='pushing == true' > AND st.pushing=1</if>" +
" <if test='pushing == false' > AND st.pushing=0 </if>" +
" <if test='mediaServerId != null' > AND st.media_server_id=#{mediaServerId} </if>" +

View File

@@ -175,6 +175,11 @@ public class StreamPushServiceImpl implements IStreamPushService {
@Override
public PageInfo<StreamPush> getPushList(Integer page, Integer count, String query, Boolean pushing, String mediaServerId) {
PageHelper.startPage(page, count);
if (query != null) {
query = query.replaceAll("/", "//")
.replaceAll("%", "/%")
.replaceAll("_", "/_");
}
List<StreamPush> all = streamPushMapper.selectAll(query, pushing, mediaServerId);
return new PageInfo<>(all);
}
@@ -530,7 +535,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
String key = streamInfo.getApp() + "_" + streamInfo.getStream();
StreamPush streamPushItem = result.get(key);
if (streamPushItem == null) {
streamPushItem = streamPushItem.getInstance(streamInfo);
streamPushItem = StreamPush.getInstance(streamInfo);
result.put(key, streamPushItem);
}
}

View File

@@ -11,7 +11,9 @@ import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.conf.security.JwtUtils;
import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService;
import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.MediaServerLoad;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -28,6 +30,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;
import oshi.SystemInfo;
@@ -73,18 +76,18 @@ public class ServerController {
@Autowired
private IStreamPushService pushService;
@Autowired
private IStreamProxyService proxyService;
@Value("${server.port}")
private int serverPort;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@GetMapping(value = "/media_server/list")
@ResponseBody
@@ -134,13 +137,17 @@ public class ServerController {
@Parameter(name = "mediaServerItem", description = "流媒体信息", required = true)
@PostMapping(value = "/media_server/save")
@ResponseBody
public void saveMediaServer(@RequestBody MediaServer mediaServerItem) {
MediaServer mediaServerItemInDatabase = mediaServerService.getOneFromDatabase(mediaServerItem.getId());
public void saveMediaServer(@RequestBody MediaServer mediaServer) {
MediaServer mediaServerItemInDatabase = mediaServerService.getOneFromDatabase(mediaServer.getId());
if (mediaServerItemInDatabase != null) {
mediaServerService.update(mediaServerItem);
mediaServerService.update(mediaServer);
} else {
mediaServerService.add(mediaServerItem);
mediaServerService.add(mediaServer);
// 发送事件
MediaServerChangeEvent event = new MediaServerChangeEvent(this);
event.setMediaServerItemList(mediaServer);
applicationEventPublisher.publishEvent(event);
}
}
@@ -156,6 +163,20 @@ public class ServerController {
mediaServerService.delete(mediaServer);
}
@Operation(summary = "获取流信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "app", description = "应用名", required = true)
@Parameter(name = "stream", description = "流ID", required = true)
@Parameter(name = "mediaServerId", description = "流媒体ID", required = true)
@GetMapping(value = "/media_server/media_info")
@ResponseBody
public MediaInfo getMediaInfo(String app, String stream, String mediaServerId) {
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
if (mediaServer == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "流媒体不存在");
}
return mediaServerService.getMediaInfo(mediaServer, app, stream);
}
@Operation(summary = "重启服务", security = @SecurityRequirement(name = JwtUtils.HEADER))
@GetMapping(value = "/restart")

View File

@@ -4,7 +4,7 @@ import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.PresetQuerySipReq;
import com.genersoft.iot.vmp.gb28181.bean.Preset;
import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService;
import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
@@ -215,13 +215,13 @@ public class ApiDeviceController {
}
deferredResultEx.setFilter(filterResult->{
List<PresetQuerySipReq> presetQuerySipReqList = (List<PresetQuerySipReq>)filterResult;
List<Preset> presetQuerySipReqList = (List<Preset>)filterResult;
HashMap<String, Object> resultMap = new HashMap<>();
resultMap.put("DeviceID", code);
resultMap.put("Result", "OK");
resultMap.put("SumNum", presetQuerySipReqList.size());
ArrayList<Map<String, Object>> presetItemList = new ArrayList<>(presetQuerySipReqList.size());
for (PresetQuerySipReq presetQuerySipReq : presetQuerySipReqList) {
for (Preset presetQuerySipReq : presetQuerySipReqList) {
Map<String, Object> item = new HashMap<>();
item.put("PresetID", presetQuerySipReq.getPresetId());
item.put("PresetName", presetQuerySipReq.getPresetName());