Merge branch 'master' into 重构/1078

This commit is contained in:
648540858 2024-11-15 21:45:40 +08:00
commit 1dfbb875b2
50 changed files with 1943 additions and 361 deletions

View File

@ -28,7 +28,8 @@ ZLM使用文档 [https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZL
# 付费社群
[![社群](doc/_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm)
> 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接退款,大家不需要有顾虑,来白嫖三天也不是不可以。
> 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接自行推出,星球会直接退款给大家。
> 星球还提供了基于主线master分支的打包, 会随时更新。
# gitee同步仓库
https://gitee.com/pan648540858/wvp-GB28181-pro.git
@ -109,9 +110,11 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
- [X] 支持MysqlPostgresql金仓等数据库
- [X] 支持Onvif(目前在onvif分支需要安装onvif服务服务请在知识星球获取)
# 非开源的内容
- [X] ONVIF设备的接入支持点播云台控制国标级联点播自动点播。在[知识星球](https://t.zsxq.com/10WAnH2MP)放了试用安装包以及使用教程,没有使用时间限制,需要源码可以星球私信我或者邮箱联系。
- [X] 支持国标28181-2022协议支持巡航轨迹查询PTZ精准控制存储卡格式化设备软件升级OSD配置h265+aac支持辅码流录像倒放等。具体的功能列表可在[知识星球](https://t.zsxq.com/18GXkpkqs)查看,需要源码和测试可以在星球私信联系或者发邮件给我
- [X] ONVIF设备的接入支持点播云台控制国标级联点播自动点播。试用安装包以及使用教程: [知识星球](https://t.zsxq.com/10WAnH2MP),没有使用时间限制,需要源码可以星球私信我或者邮箱联系。
- [X] 支持国标28181-2022协议支持巡航轨迹查询PTZ精准控制存储卡格式化设备软件升级OSD配置h265+aac支持辅码流录像倒放等。具体的功能列表可在[知识星球](https://t.zsxq.com/18GXkpkqs)查看,试用安装包: [知识星球](https://t.zsxq.com/UJ6V3),没有使用时间限制,需要源码可以星球私信我或者邮箱联系。
# 授权协议

20
buildPackage.sh Executable file
View File

@ -0,0 +1,20 @@
#!/bin/bash
# 获取当前日期并格式化为 YYYY-MM-DD 的形式
current_date=$(date +"%Y-%m-%d")
mkdir -p "$current_date"/数据库
cp -r ./数据库/2.7.3 "$current_date"/数据库
cp src/main/resources/配置详情.yml "$current_date"
cp src/main/resources/application-dev.yml "$current_date"/application.yml
cp ./target/wvp-pro-*.jar "$current_date"
zip -r "$current_date".zip "$current_date"
rm -rf "$current_date"
exit 0

View File

@ -11,7 +11,7 @@
<groupId>com.genersoft</groupId>
<artifactId>wvp-pro</artifactId>
<version>2.7.3</version>
<version>2.7.18</version>
<name>web video platform</name>
<description>国标28181视频平台</description>
<packaging>${project.packaging}</packaging>

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

@ -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,16 +203,39 @@ 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));
}

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

@ -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

@ -19,7 +19,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 +33,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 +49,6 @@ import java.util.concurrent.TimeUnit;
@DS("master")
public class DeviceServiceImpl implements IDeviceService {
@Autowired
private SIPCommander cmder;
@Autowired
private DynamicTask dynamicTask;
@ -524,6 +519,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

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

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

@ -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();

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);
@ -112,6 +117,13 @@ public class MediaInfo {
Integer sampleRate = trackJson.getInteger("sample_rate");
Integer height = trackJson.getInteger("height");
Integer width = trackJson.getInteger("height");
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

@ -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);
}
}

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

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

@ -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>" +

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

@ -36,13 +36,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

@ -246,6 +246,11 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
@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);
}

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

View File

@ -0,0 +1,98 @@
<template>
<div id="mediaInfo" >
<el-button style="position: absolute; right: 1rem;" icon="el-icon-refresh-right" circle size="mini" @click="getMediaInfo"></el-button>
<el-descriptions size="mini" :column="3" title="概况">
<el-descriptions-item label="观看人数">{{ info.readerCount }}</el-descriptions-item>
<el-descriptions-item label="网络">{{ formatByteSpeed() }}</el-descriptions-item>
<el-descriptions-item label="持续时间">{{info.aliveSecond}}</el-descriptions-item>
</el-descriptions>
<div style="display: grid; grid-template-columns: 1fr 1fr">
<el-descriptions size="mini" v-if="info.videoCodec" :column="2" title="视频信息">
<el-descriptions-item label="编码">{{ info.videoCodec }}</el-descriptions-item>
<el-descriptions-item label="分辨率"
>{{ info.width }}x{{ info.height }}
</el-descriptions-item>
<el-descriptions-item label="FPS">{{ info.fps }}</el-descriptions-item>
<el-descriptions-item label="丢包率">{{ info.loss }}</el-descriptions-item>
</el-descriptions>
<el-descriptions size="mini" v-if="info.audioCodec" :column="2" title="音频信息">
<el-descriptions-item label="编码">
{{ info.audioCodec }}
</el-descriptions-item>
<el-descriptions-item label="采样率">{{ info.audioSampleRate }}</el-descriptions-item>
</el-descriptions>
</div>
</div>
</template>
<script>
export default {
name: "mediaInfo",
props: [ 'app', 'stream', 'mediaServerId'],
components: {},
created() {
this.getMediaInfo()
},
data() {
return {
info: {}
};
},
methods: {
getMediaInfo: function () {
this.$axios({
method: 'get',
url: `/api/server/media_server/media_info`,
params: {
app: this.app,
stream: this.stream,
mediaServerId: this.mediaServerId,
}
}).then((res)=> {
console.log(res.data.data);
if (res.data.code === 0) {
this.info = res.data.data
}
}).catch((error)=> {
console.log(error);
});
},
formatByteSpeed: function (){
let bytesSpeed = this.info.bytesSpeed
let num = 1024.0 //byte
if (bytesSpeed < num) return bytesSpeed + ' B/S'
if (bytesSpeed < Math.pow(num, 2)) return (bytesSpeed / num).toFixed(2) + ' KB/S' //kb
if (bytesSpeed < Math.pow(num, 3))
return (bytesSpeed / Math.pow(num, 2)).toFixed(2) + ' MB/S' //M
if (bytesSpeed < Math.pow(num, 4))
return (bytesSpeed / Math.pow(num, 3)).toFixed(2) + ' G/S' //G
return (bytesSpeed / Math.pow(num, 4)).toFixed(2) + ' T/S' //T
},
formatAliveSecond: function (){
let aliveSecond = this.info.aliveSecond
const h = parseInt(aliveSecond.value / 3600)
const minute = parseInt((aliveSecond.value / 60) % 60)
const second = Math.ceil(aliveSecond.value % 60)
const hours = h < 10 ? '0' + h : h
const formatSecond = second > 59 ? 59 : second
return `${hours > 0 ? `${hours}小时` : ''}${minute < 10 ? '0' + minute : minute}${
formatSecond < 10 ? '0' + formatSecond : formatSecond
}`
}
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -0,0 +1,426 @@
<template>
<div id="ptzCruising">
<div style="display: grid; grid-template-columns: 80px auto; line-height: 28px">
<span>巡航组号: </span>
<el-input
min="1"
max="255"
placeholder="巡航组号"
addonBefore="巡航组号"
addonAfter="(1-255)"
v-model="cruiseId"
size="mini"
>
</el-input>
</div>
<p>
<el-tag v-for="(item, index) in presetList"
key="item.presetId"
closable
@close="delPreset(item, index)"
style="margin-right: 1rem; cursor: pointer"
>
{{item.presetName?item.presetName:item.presetId}}
</el-tag>
</p>
<el-form size="mini" :inline="true" v-if="selectPresetVisible">
<el-form-item >
<el-select v-model="selectPreset" placeholder="请选择预置点">
<el-option
v-for="item in allPresetList"
:key="item.presetId"
:label="item.presetName"
:value="item">
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="addCruisePoint">保存</el-button>
<el-button type="primary" @click="cancelAddCruisePoint">取消</el-button>
</el-form-item>
</el-form>
<el-button size="mini" v-else @click="selectPresetVisible=true">添加巡航点</el-button>
<el-form size="mini" :inline="true" v-if="setSpeedVisible">
<el-form-item >
<el-input
min="1"
max="4095"
placeholder="巡航速度"
addonBefore="巡航速度"
addonAfter="(1-4095)"
v-if="setSpeedVisible"
v-model="cruiseSpeed"
size="mini"
>
</el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="setCruiseSpeed">保存</el-button>
<el-button @click="cancelSetCruiseSpeed">取消</el-button>
</el-form-item>
</el-form>
<el-button v-else size="mini" @click="setSpeedVisible = true">设置巡航速度</el-button>
<el-form size="mini" :inline="true" v-if="setTimeVisible">
<el-form-item >
<el-input
min="1"
max="4095"
placeholder="巡航停留时间(秒)"
addonBefore="巡航停留时间(秒)"
addonAfter="(1-4095)"
style="width: 100%;"
v-model="cruiseTime"
>
</el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="setCruiseTime">保存</el-button>
<el-button @click="cancelSetCruiseTime">取消</el-button>
</el-form-item>
</el-form>
<el-button v-else size="mini" @click="setTimeVisible = true">设置巡航时间</el-button>
<el-button size="mini" @click="startCruise">开始巡航</el-button>
<el-button size="mini" @click="stopCruise">停止巡航</el-button>
<el-button size="mini" type="danger" @click="deleteCruise">删除巡航</el-button>
</div>
</template>
<script>
export default {
name: "ptzCruising",
props: [ 'channelDeviceId', 'deviceId'],
components: {},
created() {
this.getPresetList()
},
data() {
return {
cruiseId: 1,
presetList: [],
allPresetList: [],
selectPreset: "",
inputVisible: false,
selectPresetVisible: false,
setSpeedVisible: false,
setTimeVisible: false,
cruiseSpeed: '',
cruiseTime: '',
};
},
methods: {
getPresetList: function () {
this.$axios({
method: 'get',
url: `/api/front-end/preset/query/${this.deviceId}/${this.channelDeviceId}`,
}).then((res)=> {
if (res.data.code === 0) {
this.allPresetList = res.data.data;
}
}).catch((error)=> {
console.log(error);
});
},
addCruisePoint: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/cruise/point/add/${this.deviceId}/${this.channelDeviceId}`,
params: {
cruiseId: this.cruiseId,
presetId: this.selectPreset.presetId
}
}).then((res)=> {
if (res.data.code === 0) {
this.presetList.push(this.selectPreset)
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
this.selectPreset = ""
this.selectPresetVisible = false;
loading.close()
})
},
cancelAddCruisePoint: function () {
this.selectPreset = ""
this.selectPresetVisible = false;
},
delPreset: function (preset, index){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/cruise/point/delete/${this.deviceId}/${this.channelDeviceId}`,
params: {
cruiseId: this.cruiseId,
presetId: preset.presetId
}
}).then((res)=> {
if (res.data.code === 0) {
this.presetList.splice(index, 1)
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
deleteCruise: function (preset, index){
this.$confirm("确定删除此巡航组", '提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/cruise/point/delete/${this.deviceId}/${this.channelDeviceId}`,
params: {
cruiseId: this.cruiseId,
presetId: 0
}
}).then((res)=> {
if (res.data.code === 0) {
this.presetList = []
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
})
},
setCruiseSpeed: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/cruise/speed/${this.deviceId}/${this.channelDeviceId}`,
params: {
cruiseId: this.cruiseId,
speed: this.cruiseSpeed
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
this.cruiseSpeed = ""
this.setSpeedVisible = false
loading.close()
})
},
cancelSetCruiseSpeed: function (){
this.cruiseSpeed = ""
this.setSpeedVisible = false
},
setCruiseTime: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/cruise/time/${this.deviceId}/${this.channelDeviceId}`,
params: {
cruiseId: this.cruiseId,
time: this.cruiseTime
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
this.setTimeVisible = false;
this.cruiseTime = "";
loading.close()
})
},
cancelSetCruiseTime: function (){
this.setTimeVisible = false;
this.cruiseTime = "";
},
startCruise: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/cruise/start/${this.deviceId}/${this.channelDeviceId}`,
params: {
cruiseId: this.cruiseId
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "发送成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
stopCruise: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/cruise/stop/${this.deviceId}/${this.channelDeviceId}`,
params: {
cruiseId: this.cruiseId
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "发送成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -0,0 +1,212 @@
<template>
<div id="ptzPreset" style="width: 100%">
<el-tag v-for="item in presetList"
key="item.presetId"
closable
@close="delPreset(item)"
@click="gotoPreset(item)"
size="mini"
style="margin-right: 1rem; cursor: pointer; margin-bottom: 0.6rem"
>
{{item.presetName?item.presetName:item.presetId}}
</el-tag>
<el-input
min="1"
max="255"
placeholder="预置位编号"
addonBefore="预置位编号"
addonAfter="(1-255)"
style="width: 300px; vertical-align: bottom;"
v-if="inputVisible"
v-model="ptzPresetId"
ref="saveTagInput"
size="small"
>
<template v-slot:append>
<el-button @click="addPreset()">保存</el-button>
<el-button @click="cancel()">取消</el-button>
</template>
</el-input>
<el-button v-else size="small" @click="showInput">+ 添加</el-button>
</div>
</template>
<script>
export default {
name: "ptzPreset",
props: [ 'channelDeviceId', 'deviceId'],
components: {},
created() {
this.getPresetList()
},
data() {
return {
presetList: [],
inputVisible: false,
ptzPresetId: '',
};
},
methods: {
getPresetList: function () {
this.$axios({
method: 'get',
url: `/api/front-end/preset/query/${this.deviceId}/${this.channelDeviceId}`,
}).then((res)=> {
if (res.data.code === 0) {
this.presetList = res.data.data;
//
this.$nextTick(() => {
this.$refs.channelListTable.doLayout();
})
}
}).catch((error)=> {
console.log(error);
});
},
showInput() {
this.inputVisible = true;
this.$nextTick(_ => {
this.$refs.saveTagInput.$refs.input.focus();
});
},
addPreset: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/preset/add/${this.deviceId}/${this.channelDeviceId}`,
params: {
presetId: this.ptzPresetId
}
}).then((res)=> {
if (res.data.code === 0) {
setTimeout(()=>{
loading.close()
this.inputVisible = false;
this.ptzPresetId = ""
this.getPresetList()
}, 1000)
}else {
loading.close()
this.inputVisible = false;
this.ptzPresetId = ""
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
loading.close()
this.inputVisible = false;
this.ptzPresetId = ""
this.$message({
showClose: true,
message: error,
type: 'error'
});
});
},
cancel: function () {
this.inputVisible = false;
this.ptzPresetId = ""
},
gotoPreset: function (preset){
console.log(preset)
this.$axios({
method: 'get',
url: `/api/front-end/preset/call/${this.deviceId}/${this.channelDeviceId}`,
params: {
presetId: preset.presetId
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: '调用成功',
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
});
},
delPreset: function (preset){
this.$confirm("确定删除此预置位", '提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/preset/delete/${this.deviceId}/${this.channelDeviceId}`,
params: {
presetId: preset.presetId
}
}).then((res)=> {
if (res.data.code === 0) {
setTimeout(()=>{
loading.close()
this.getPresetList()
}, 1000)
}else {
loading.close()
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
loading.close()
this.$message({
showClose: true,
message: error,
type: 'error'
});
});
}).catch(() => {
});
},
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -0,0 +1,273 @@
<template>
<div id="ptzScan">
<div style="display: grid; grid-template-columns: 80px auto; line-height: 28px">
<span>扫描组号: </span>
<el-input
min="1"
max="255"
placeholder="扫描组号"
addonBefore="扫描组号"
addonAfter="(1-255)"
v-model="scanId"
size="mini"
>
</el-input>
</div>
<el-button size="mini" @click="setScanLeft">设置左边界</el-button>
<el-button size="mini" @click="setScanRight">设置右边界</el-button>
<el-form size="mini" :inline="true" v-if="setSpeedVisible">
<el-form-item >
<el-input
min="1"
max="4095"
placeholder="巡航速度"
addonBefore="巡航速度"
addonAfter="(1-4095)"
v-if="setSpeedVisible"
v-model="speed"
size="mini"
>
</el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="setSpeed">保存</el-button>
<el-button @click="cancelSetSpeed">取消</el-button>
</el-form-item>
</el-form>
<el-button v-else size="mini" @click="setSpeedVisible = true">设置扫描速度</el-button>
<el-button size="mini" @click="startScan">开始自动扫描</el-button>
<el-button size="mini" @click="stopScan">停止自动扫描</el-button>
</div>
</template>
<script>
export default {
name: "ptzScan",
props: [ 'channelDeviceId', 'deviceId'],
components: {},
created() {
},
data() {
return {
scanId: 1,
setSpeedVisible: false,
speed: '',
};
},
methods: {
setSpeed: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/scan/set/speed/${this.deviceId}/${this.channelDeviceId}`,
params: {
scanId: this.scanId,
speed: this.speed
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
this.speed = ""
this.setSpeedVisible = false
loading.close()
})
},
cancelSetSpeed: function (){
this.speed = ""
this.setSpeedVisible = false
},
setScanLeft: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/scan/set/left/${this.deviceId}/${this.channelDeviceId}`,
params: {
scanId: this.scanId,
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
setScanRight: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/scan/set/right/${this.deviceId}/${this.channelDeviceId}`,
params: {
scanId: this.scanId,
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
this.setSpeedVisible = false;
this.speed = "";
loading.close()
})
},
startScan: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/scan/start/${this.deviceId}/${this.channelDeviceId}`,
params: {
scanId: this.scanId
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "发送成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
stopScan: function (){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/scan/stop/${this.deviceId}/${this.channelDeviceId}`,
params: {
scanId: this.scanId
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "发送成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -0,0 +1,90 @@
<template>
<div id="ptzScan">
<el-form size="mini" :inline="true" >
<el-form-item >
<el-input
min="1"
max="4095"
placeholder="开关编号"
addonBefore="开关编号"
addonAfter="(2-255)"
v-model="switchId"
size="mini"
>
</el-input>
</el-form-item>
<el-form-item>
<el-button size="mini" @click="open('on')">开启</el-button>
<el-button size="mini" @click="open('off')">关闭</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "ptzScan",
props: [ 'channelDeviceId', 'deviceId'],
components: {},
created() {
},
data() {
return {
switchId: 1,
};
},
methods: {
open: function (command){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/auxiliary/${this.deviceId}/${this.channelDeviceId}`,
params: {
command: command,
switchId: this.switchId,
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -0,0 +1,70 @@
<template>
<div id="ptzWiper">
<el-button size="mini" @click="open('on')">开启</el-button>
<el-button size="mini" @click="open('off')">关闭</el-button>
</div>
</template>
<script>
export default {
name: "ptzWiper",
props: [ 'channelDeviceId', 'deviceId'],
components: {},
created() {
},
data() {
return {};
},
methods: {
open: function (command){
const loading = this.$loading({
lock: true,
fullscreen: true,
text: '正在发送指令',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$axios({
method: 'get',
url: `/api/front-end/wiper/${this.deviceId}/${this.channelDeviceId}`,
params: {
command: command,
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message({
showClose: true,
message: "保存成功",
type: 'success'
});
}else {
this.$message({
showClose: true,
message: res.data.msg,
type: 'error'
});
}
}).catch((error)=> {
this.$message({
showClose: true,
message: error,
type: 'error'
});
}).finally(()=>{
loading.close()
})
},
},
};
</script>
<style>
.channel-form {
display: grid;
background-color: #FFFFFF;
padding: 1rem 2rem 0 2rem;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
</style>

View File

@ -26,6 +26,7 @@
</div>
<div id="shared" style="text-align: right; margin-top: 1rem;">
<el-tabs v-model="tabActiveName" @tab-click="tabHandleClick">
<el-tab-pane label="实时视频" name="media">
<div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
@ -154,148 +155,84 @@
<!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}-->
<!--遥控界面-->
<el-tab-pane label="云台控制" name="control" v-if="showPtz">
<div style="display: flex; justify-content: left;">
<div class="control-wrapper">
<div class="control-btn control-top" @mousedown="ptzCamera('up')" @mouseup="ptzCamera('stop')">
<i class="el-icon-caret-top"></i>
<div class="control-inner-btn control-inner"></div>
<div style="display: grid; grid-template-columns: 240px auto; height: 180px; overflow: auto">
<div style="display: grid; grid-template-columns: 6.25rem auto;">
<div class="control-wrapper">
<div class="control-btn control-top" @mousedown="ptzCamera('up')" @mouseup="ptzCamera('stop')">
<i class="el-icon-caret-top"></i>
<div class="control-inner-btn control-inner"></div>
</div>
<div class="control-btn control-left" @mousedown="ptzCamera('left')" @mouseup="ptzCamera('stop')">
<i class="el-icon-caret-left"></i>
<div class="control-inner-btn control-inner"></div>
</div>
<div class="control-btn control-bottom" @mousedown="ptzCamera('down')" @mouseup="ptzCamera('stop')">
<i class="el-icon-caret-bottom"></i>
<div class="control-inner-btn control-inner"></div>
</div>
<div class="control-btn control-right" @mousedown="ptzCamera('right')" @mouseup="ptzCamera('stop')">
<i class="el-icon-caret-right"></i>
<div class="control-inner-btn control-inner"></div>
</div>
<div class="control-round">
<div class="control-round-inner"><i class="fa fa-pause-circle"></i></div>
</div>
<div class="contro-speed" style="position: absolute; left: 4px; top: 7rem; width: 6.25rem;">
<el-slider v-model="controSpeed" :max="100"></el-slider>
</div>
</div>
<div class="control-btn control-left" @mousedown="ptzCamera('left')" @mouseup="ptzCamera('stop')">
<i class="el-icon-caret-left"></i>
<div class="control-inner-btn control-inner"></div>
</div>
<div class="control-btn control-bottom" @mousedown="ptzCamera('down')" @mouseup="ptzCamera('stop')">
<i class="el-icon-caret-bottom"></i>
<div class="control-inner-btn control-inner"></div>
</div>
<div class="control-btn control-right" @mousedown="ptzCamera('right')" @mouseup="ptzCamera('stop')">
<i class="el-icon-caret-right"></i>
<div class="control-inner-btn control-inner"></div>
</div>
<div class="control-round">
<div class="control-round-inner"><i class="fa fa-pause-circle"></i></div>
</div>
<div style="position: absolute; left: 7.25rem; top: 1.25rem" @mousedown="ptzCamera('zoomin')"
@mouseup="ptzCamera('stop')"><i class="el-icon-zoom-in control-zoom-btn"
style="font-size: 1.875rem;"></i></div>
<div style="position: absolute; left: 7.25rem; top: 3.25rem; font-size: 1.875rem;"
@mousedown="ptzCamera('zoomout')" @mouseup="ptzCamera('stop')"><i
class="el-icon-zoom-out control-zoom-btn"></i></div>
<div class="contro-speed" style="position: absolute; left: 4px; top: 7rem; width: 9rem;">
<el-slider v-model="controSpeed" :max="255"></el-slider>
<div>
<div class="ptz-btn-box">
<div style="" @mousedown="ptzCamera('zoomin')" @mouseup="ptzCamera('stop')" title="变倍+">
<i class="el-icon-zoom-in control-zoom-btn" style="font-size: 1.5rem;"></i>
</div>
<div style="" @mousedown="ptzCamera('zoomout')" @mouseup="ptzCamera('stop')" title="变倍-">
<i class="el-icon-zoom-out control-zoom-btn" style="font-size: 1.5rem;"></i>
</div>
</div>
<div class="ptz-btn-box">
<div @mousedown="focusCamera('near')" @mouseup="focusCamera('stop')" title="聚焦+">
<i class="iconfont icon-bianjiao-fangda control-zoom-btn" style="font-size: 1.5rem;"></i>
</div>
<div @mousedown="focusCamera('far')" @mouseup="focusCamera('stop')" title="聚焦-">
<i class="iconfont icon-bianjiao-suoxiao control-zoom-btn" style="font-size: 1.5rem;"></i>
</div>
</div>
<div class="ptz-btn-box">
<div @mousedown="irisCamera('in')" @mouseup="irisCamera('stop')" title="光圈+">
<i class="iconfont icon-guangquan control-zoom-btn" style="font-size: 1.5rem;"></i>
</div>
<div @mousedown="pirisCamera('out')" @mouseup="irisCamera('stop')" title="光圈-">
<i class="iconfont icon-guangquan- control-zoom-btn" style="font-size: 1.5rem;"></i>
</div>
</div>
</div>
</div>
<div style="text-align: left" >
<el-select
v-model="ptzMethod"
style="width: 100%"
size="mini"
placeholder="请选择云台功能"
>
<el-option label="预置点" value="preset"></el-option>
<el-option label="巡航组" value="cruise"></el-option>
<el-option label="自动扫描" value="scan"></el-option>
<el-option label="雨刷" value="wiper"></el-option>
<el-option label="辅助开关" value="switch"></el-option>
</el-select>
<div class="control-panel">
<el-button-group>
<el-tag style="position :absolute; left: 0rem; top: 0rem; width: 5rem; text-align: center"
size="medium">预置位编号
</el-tag>
<el-input-number style="position: absolute; left: 5rem; top: 0rem; width: 6rem" size="mini"
v-model="presetPos" controls-position="right" :precision="0" :step="1" :min="1"
:max="255"></el-input-number>
<el-button style="position: absolute; left: 11rem; top: 0rem; width: 5rem" size="mini"
icon="el-icon-add-location" @click="presetPosition(129, presetPos)">设置
</el-button>
<el-button style="position: absolute; left: 27rem; top: 0rem; width: 5rem" size="mini" type="primary"
icon="el-icon-place" @click="presetPosition(130, presetPos)">调用
</el-button>
<el-button style="position: absolute; left: 16rem; top: 0rem; width: 5rem" size="mini"
icon="el-icon-delete-location" @click="presetPosition(131, presetPos)">删除
</el-button>
<el-tag style="position :absolute; left: 0rem; top: 2.5rem; width: 5rem; text-align: center"
size="medium">巡航速度
</el-tag>
<el-input-number style="position: absolute; left: 5rem; top: 2.5rem; width: 6rem" size="mini"
v-model="cruisingSpeed" controls-position="right" :precision="0" :min="1"
:max="4095"></el-input-number>
<el-button style="position: absolute; left: 11rem; top: 2.5rem; width: 5rem" size="mini"
icon="el-icon-loading" @click="setSpeedOrTime(134, cruisingGroup, cruisingSpeed)">设置
</el-button>
<el-tag style="position :absolute; left: 16rem; top: 2.5rem; width: 5rem; text-align: center"
size="medium">停留时间
</el-tag>
<el-input-number style="position: absolute; left: 21rem; top: 2.5rem; width: 6rem" size="mini"
v-model="cruisingTime" controls-position="right" :precision="0" :min="1"
:max="4095"></el-input-number>
<el-button style="position: absolute; left: 27rem; top: 2.5rem; width: 5rem" size="mini"
icon="el-icon-timer" @click="setSpeedOrTime(135, cruisingGroup, cruisingTime)">设置
</el-button>
<el-tag style="position :absolute; left: 0rem; top: 4.5rem; width: 5rem; text-align: center"
size="medium">巡航组编号
</el-tag>
<el-input-number style="position: absolute; left: 5rem; top: 4.5rem; width: 6rem" size="mini"
v-model="cruisingGroup" controls-position="right" :precision="0" :min="0"
:max="255"></el-input-number>
<el-button style="position: absolute; left: 11rem; top: 4.5rem; width: 5rem" size="mini"
icon="el-icon-add-location" @click="setCommand(132, cruisingGroup, presetPos)">添加点
</el-button>
<el-button style="position: absolute; left: 16rem; top: 4.5rem; width: 5rem" size="mini"
icon="el-icon-delete-location" @click="setCommand(133, cruisingGroup, presetPos)">删除点
</el-button>
<el-button style="position: absolute; left: 21rem; top: 4.5rem; width: 5rem" size="mini"
icon="el-icon-delete" @click="setCommand(133, cruisingGroup, 0)">删除组
</el-button>
<el-button style="position: absolute; left: 27rem; top: 5rem; width: 5rem" size="mini" type="primary"
icon="el-icon-video-camera-solid" @click="setCommand(136, cruisingGroup, 0)">巡航
</el-button>
<el-tag style="position :absolute; left: 0rem; top: 7rem; width: 5rem; text-align: center"
size="medium">扫描速度
</el-tag>
<el-input-number style="position: absolute; left: 5rem; top: 7rem; width: 6rem" size="mini"
v-model="scanSpeed" controls-position="right" :precision="0" :min="1"
:max="4095"></el-input-number>
<el-button style="position: absolute; left: 11rem; top: 7rem; width: 5rem" size="mini"
icon="el-icon-loading" @click="setSpeedOrTime(138, scanGroup, scanSpeed)">设置
</el-button>
<el-tag style="position :absolute; left: 0rem; top: 9rem; width: 5rem; text-align: center"
size="medium">扫描组编号
</el-tag>
<el-input-number style="position: absolute; left: 5rem; top: 9rem; width: 6rem" size="mini"
v-model="scanGroup" controls-position="right" :precision="0" :step="1" :min="0"
:max="255"></el-input-number>
<el-button style="position: absolute; left: 11rem; top: 9rem; width: 5rem" size="mini"
icon="el-icon-d-arrow-left" @click="setCommand(137, scanGroup, 1)">左边界
</el-button>
<el-button style="position: absolute; left: 16rem; top: 9rem; width: 5rem" size="mini"
icon="el-icon-d-arrow-right" @click="setCommand(137, scanGroup, 2)">右边界
</el-button>
<el-button style="position: absolute; left: 27rem; top: 7rem; width: 5rem" size="mini" type="primary"
icon="el-icon-video-camera-solid" @click="setCommand(137, scanGroup, 0)">扫描
</el-button>
<el-button style="position: absolute; left: 27rem; top: 9rem; width: 5rem" size="mini" type="danger"
icon="el-icon-switch-button" @click="ptzCamera('stop')">停止
</el-button>
</el-button-group>
<ptzPreset :channelDeviceId="channelId" :deviceId="deviceId" v-if="ptzMethod === 'preset'" style="margin-top: 1rem"></ptzPreset>
<ptzCruising :channelDeviceId="channelId" :deviceId="deviceId" v-if="ptzMethod === 'cruise'" style="margin-top: 1rem"></ptzCruising>
<ptzScan :channelDeviceId="channelId" :deviceId="deviceId" v-if="ptzMethod === 'scan'" style="margin-top: 1rem"></ptzScan>
<ptzWiper :channelDeviceId="channelId" :deviceId="deviceId" v-if="ptzMethod === 'wiper'" style="margin-top: 1rem"></ptzWiper>
<ptzSwitch :channelDeviceId="channelId" :deviceId="deviceId" v-if="ptzMethod === 'switch'" style="margin-top: 1rem"></ptzSwitch>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="编码信息" name="codec" v-loading="tracksLoading">
<p>
无法播放或者没有声音?&nbsp&nbsp&nbsp试一试&nbsp
<el-button size="mini" type="primary" v-if="!coverPlaying" @click="coverPlay">转码播放</el-button>
<el-button size="mini" type="danger" v-if="coverPlaying" @click="convertStopClick">停止转码</el-button>
</p>
<div class="trank">
<p v-if="tracksNotLoaded" style="text-align: center;padding-top: 3rem;">暂无数据</p>
<div v-for="(item, index) in tracks" style="width: 50%; float: left" loading>
<span> {{ index }}</span>
<div class="trankInfo" v-if="item.codec_type == 0">
<p>格式: {{ item.codec_id_name }}</p>
<p>类型: 视频</p>
<p>分辨率: {{ item.width }} x {{ item.height }}</p>
<p>帧率: {{ item.fps }}</p>
</div>
<div class="trankInfo" v-if="item.codec_type == 1">
<p>格式: {{ item.codec_id_name }}</p>
<p>类型: 音频</p>
<p>采样位数: {{ item.sample_bit }}</p>
<p>采样率: {{ item.sample_rate }}</p>
</div>
</div>
</div>
<mediaInfo :app="app" :stream="streamId" :mediaServerId="mediaServerId"></mediaInfo>
</el-tab-pane>
<el-tab-pane label="语音对讲" name="broadcast">
<div style="padding: 0 10px">
@ -327,11 +264,18 @@ import rtcPlayer from '../dialog/rtcPlayer.vue'
import LivePlayer from '@liveqing/liveplayer'
import crypto from 'crypto'
import jessibucaPlayer from '../common/jessibuca.vue'
import PtzPreset from "../common/ptzPreset.vue";
import PtzCruising from "../common/ptzCruising.vue";
import ptzScan from "../common/ptzScan.vue";
import ptzWiper from "../common/ptzWiper.vue";
import ptzSwitch from "../common/ptzSwitch.vue";
import mediaInfo from "../common/mediaInfo.vue";
export default {
name: 'devicePlayer',
props: {},
components: {
PtzPreset,PtzCruising,ptzScan,ptzWiper,ptzSwitch,mediaInfo,
LivePlayer, jessibucaPlayer, rtcPlayer,
},
computed: {
@ -363,9 +307,10 @@ export default {
},
showVideoDialog: false,
streamId: '',
ptzMethod: 'preset',
ptzPresetId: '',
app: '',
mediaServerId: '',
convertKey: '',
deviceId: '',
channelId: '',
tabActiveName: 'media',
@ -384,7 +329,6 @@ export default {
scanSpeed: 100,
scanGroup: 0,
tracks: [],
coverPlaying: false,
tracksLoading: false,
showPtz: true,
showRrecord: true,
@ -484,63 +428,6 @@ export default {
}
return this.videoUrl;
},
coverPlay: function () {
var that = this;
this.coverPlaying = true;
this.$refs[this.activePlayer].pause()
that.$axios({
method: 'post',
url: '/api/play/convert/' + that.streamId
}).then(function (res) {
if (res.data.code === 0) {
that.convertKey = res.data.key;
setTimeout(() => {
that.isLoging = false;
that.playFromStreamInfo(false, res.data.data);
}, 2000)
} else {
that.isLoging = false;
that.coverPlaying = false;
that.$message({
showClose: true,
message: '转码失败',
type: 'error'
});
}
}).catch(function (e) {
console.log(e)
that.coverPlaying = false;
that.$message({
showClose: true,
message: '播放错误',
type: 'error'
});
});
},
convertStopClick: function () {
this.convertStop(() => {
this.$refs[this.activePlayer].play(this.videoUrl)
});
},
convertStop: function (callback) {
var that = this;
that.$refs.videoPlayer.pause()
this.$axios({
method: 'post',
url: '/api/play/convertStop/' + this.convertKey
}).then(function (res) {
if (res.data.code == 0) {
console.log(res.data.msg)
} else {
console.error(res.data.msg)
}
if (callback) callback();
}).catch(function (e) {
});
that.coverPlaying = false;
that.convertKey = "";
// if (callback )callback();
},
playFromStreamInfo: function (realHasAudio, streamInfo) {
@ -562,10 +449,6 @@ export default {
this.videoUrl = '';
this.coverPlaying = false;
this.showVideoDialog = false;
if (this.convertKey != '') {
this.convertStop();
}
this.convertKey = ''
this.stopBroadcast()
},
@ -595,8 +478,22 @@ export default {
console.log('云台控制:' + command);
let that = this;
this.$axios({
method: 'post',
url: '/api/ptz/control/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&horizonSpeed=' + this.controSpeed + '&verticalSpeed=' + this.controSpeed + '&zoomSpeed=' + this.controSpeed
method: 'get',
url: '/api/front-end/ptz/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&horizonSpeed=' + parseInt(this.controSpeed * 255/100) + '&verticalSpeed=' + parseInt(this.controSpeed * 255/100) + '&zoomSpeed=' + parseInt(this.controSpeed * 16/100)
}).then(function (res) {
});
},
irisCamera: function (command) {
this.$axios({
method: 'get',
url: '/api/front-end/fi/iris/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&speed=' + parseInt(this.controSpeed * 255/100)
}).then(function (res) {
});
},
focusCamera: function (command) {
this.$axios({
method: 'get',
url: '/api/front-end/fi/focus/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&speed=' + parseInt(this.controSpeed * 255/100)
}).then(function (res) {
});
},
@ -1001,4 +898,14 @@ export default {
width: 80%;
padding: 0 10%;
}
.el-dialog__body{
padding: 10px 20px;
}
.ptz-btn-box {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
height: 3rem;
line-height: 4rem;
}
</style>

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 1291092 */
src: url('iconfont.woff2?t=1726109971995') format('woff2'),
url('iconfont.woff?t=1726109971995') format('woff'),
url('iconfont.ttf?t=1726109971995') format('truetype');
src: url('iconfont.woff2?t=1731484250872') format('woff2'),
url('iconfont.woff?t=1731484250872') format('woff'),
url('iconfont.ttf?t=1731484250872') format('truetype');
}
.iconfont {
@ -13,6 +13,22 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-bianjiao-suoxiao:before {
content: "\e8c8";
}
.icon-bianjiao-fangda:before {
content: "\e8c9";
}
.icon-guangquan-:before {
content: "\e7e9";
}
.icon-guangquan:before {
content: "\e7ea";
}
.icon-a-mti-1fenpingshi:before {
content: "\e7e5";
}

Binary file not shown.