Merge remote-tracking branch 'github/wvp-28181-2.0' into wvp-28181-2.0

# Conflicts:
#	pom.xml
#	src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
This commit is contained in:
zxb 2024-01-23 15:56:57 +08:00
commit 01622884a9
104 changed files with 4657 additions and 27386 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

30
pom.xml
View File

@ -11,7 +11,7 @@
<groupId>com.genersoft</groupId>
<artifactId>wvp-pro</artifactId>
<version>2.6.9</version>
<version>2.7.0</version>
<name>web video platform</name>
<description>国标28181视频平台</description>
<packaging>${project.packaging}</packaging>
@ -50,15 +50,15 @@
<maven.build.timestamp.format>MMddHHmm</maven.build.timestamp.format>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<!-- 依赖版本 -->
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
<asciidoctor.input.directory>${project.basedir}/docs/asciidoc</asciidoctor.input.directory>
<generated.asciidoc.directory>${project.build.directory}/asciidoc</generated.asciidoc.directory>
<asciidoctor.html.output.directory>${project.build.directory}/asciidoc/html</asciidoctor.html.output.directory>
<asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdf</asciidoctor.pdf.output.directory>
<!-- 依赖版本 -->
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
<asciidoctor.input.directory>${project.basedir}/docs/asciidoc</asciidoctor.input.directory>
<generated.asciidoc.directory>${project.build.directory}/asciidoc</generated.asciidoc.directory>
<asciidoctor.html.output.directory>${project.build.directory}/asciidoc/html</asciidoctor.html.output.directory>
<asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdf</asciidoctor.pdf.output.directory>
<org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
<springboot.version>2.7.2</springboot.version>
</properties>
</properties>
<profiles>
<profile>
@ -178,6 +178,20 @@
<artifactId>springdoc-openapi-security</artifactId>
<version>1.6.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.baomidou/dynamic-datasource-spring-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.6.1</version>
</dependency>
<!--在线文档 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.10</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>

View File

@ -1,8 +0,0 @@
alter table wvp_device_channel
change stream_id stream_id varying(255)
alter table wvp_platform
add auto_push_channel bool default false
alter table wvp_stream_proxy
add stream_key character varying(255)

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.common;
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
@ -76,6 +77,8 @@ public class StreamInfo implements Serializable, Cloneable{
private String endTime;
@Schema(description = "进度(录像下载使用)")
private double progress;
@Schema(description = "文件下载地址(录像下载使用)")
private DownloadFileInfo downLoadFilePath;
@Schema(description = "是否暂停(录像回放使用)")
private boolean pause;
@ -605,5 +608,11 @@ public class StreamInfo implements Serializable, Cloneable{
this.subStream = subStream;
}
public DownloadFileInfo getDownLoadFilePath() {
return downLoadFilePath;
}
public void setDownLoadFilePath(DownloadFileInfo downLoadFilePath) {
this.downLoadFilePath = downLoadFilePath;
}
}

View File

@ -53,7 +53,7 @@ public class VideoManagerConstants {
public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_MEDIA_TRANSACTION_";
public static final String MEDIA_STREAM_AUTHORITY = "MEDIA_STREAM_AUTHORITY_";
public static final String MEDIA_STREAM_AUTHORITY = "VMP_MEDIA_STREAM_AUTHORITY_";
public static final String SIP_CSEQ_PREFIX = "VMP_SIP_CSEQ_";
@ -70,6 +70,7 @@ public class VideoManagerConstants {
public static final String SYSTEM_INFO_DISK_PREFIX = "VMP_SYSTEM_INFO_DISK_";
public static final String REGISTER_EXPIRE_TASK_KEY_PREFIX = "VMP_device_register_expire_";
public static final String PUSH_STREAM_LIST = "VMP_PUSH_STREAM_LIST_";

View File

@ -0,0 +1,83 @@
package com.genersoft.iot.vmp.conf;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
import com.genersoft.iot.vmp.vmanager.cloudRecord.CloudRecordController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
/**
* 录像文件定时删除
*/
@Component
public class CloudRecordTimer {
private final static Logger logger = LoggerFactory.getLogger(CloudRecordTimer.class);
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private CloudRecordServiceMapper cloudRecordServiceMapper;
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
/**
* 定时查询待删除的录像文件
*/
// @Scheduled(fixedRate = 10000) //每五秒执行一次方便测试
@Scheduled(cron = "0 0 0 * * ?") //每天的0点执行
public void execute(){
logger.info("[录像文件定时清理] 开始清理过期录像文件");
// 获取配置了assist的流媒体节点
List<MediaServerItem> mediaServerItemList = mediaServerService.getAllOnline();
if (mediaServerItemList.isEmpty()) {
return;
}
long result = 0;
for (MediaServerItem mediaServerItem : mediaServerItemList) {
Calendar lastCalendar = Calendar.getInstance();
if (mediaServerItem.getRecordDay() > 0) {
lastCalendar.setTime(new Date());
// 获取保存的最后截至日[因为每个节点都有一个日期也就是支持每个节点设置不同的保存日期
lastCalendar.add(Calendar.DAY_OF_MONTH, -mediaServerItem.getRecordDay());
Long lastDate = lastCalendar.getTimeInMillis();
// 获取到截至日期之前的录像文件列表文件列表满足未被收藏和保持的这两个字段目前共能一致
// 为我自己业务系统相关的代码大家使用的时候直接使用收藏collect这一个类型即可
List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.queryRecordListForDelete(lastDate, mediaServerItem.getId());
if (cloudRecordItemList.isEmpty()) {
continue;
}
// TODO 后续可以删除空了的过期日期文件夹
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
String date = new File(cloudRecordItem.getFilePath()).getParentFile().getName();
JSONObject jsonObject = zlmresTfulUtils.deleteRecordDirectory(mediaServerItem, cloudRecordItem.getApp(),
cloudRecordItem.getStream(), date, cloudRecordItem.getFileName());
if (jsonObject.getInteger("code") != 0) {
logger.warn("[录像文件定时清理] 删除磁盘文件错误: {}:{}", cloudRecordItem.getFilePath(), jsonObject);
}
}
result += cloudRecordServiceMapper.deleteList(cloudRecordItemList);
}
}
logger.info("[录像文件定时清理] 共清理{}个过期录像文件", result);
}
}

View File

@ -86,6 +86,12 @@ public class MediaConfig{
@Value("${media.record-assist-port:0}")
private Integer recordAssistPort = 0;
@Value("${media.record-day:7}")
private Integer recordDay;
@Value("${media.record-path:}")
private String recordPath;
public String getId() {
return id;
}
@ -218,13 +224,32 @@ public class MediaConfig{
mediaServerItem.setRecordAssistIp(recordAssistIp);
mediaServerItem.setRecordAssistPort(recordAssistPort);
mediaServerItem.setHookAliveInterval(30.00f);
mediaServerItem.setRecordDay(recordDay);
if (recordPath != null) {
mediaServerItem.setRecordPath(recordPath);
}
mediaServerItem.setCreateTime(DateUtil.getNow());
mediaServerItem.setUpdateTime(DateUtil.getNow());
return mediaServerItem;
}
public Integer getRecordDay() {
return recordDay;
}
public void setRecordDay(Integer recordDay) {
this.recordDay = recordDay;
}
public String getRecordPath() {
return recordPath;
}
public void setRecordPath(String recordPath) {
this.recordPath = recordPath;
}
public String getRtpSendPortRange() {
return rtpSendPortRange;
}

View File

@ -39,4 +39,6 @@ public class SystemInfoTimerTask {
}
}
}

View File

@ -28,7 +28,7 @@ public class UserSetting {
private Integer playTimeout = 18000;
private int platformPlayTimeout = 60000;
private int platformPlayTimeout = 20000;
private Boolean interfaceAuthentication = Boolean.TRUE;
@ -61,8 +61,6 @@ public class UserSetting {
private String serverId = "000000";
private String recordPath = null;
private String thirdPartyGBIdReg = "[\\s\\S]*";
private String civilCodeFile = "classpath:civilCode.csv";
@ -257,14 +255,6 @@ public class UserSetting {
this.refuseChannelStatusChannelFormNotify = refuseChannelStatusChannelFormNotify;
}
public String getRecordPath() {
return recordPath;
}
public void setRecordPath(String recordPath) {
this.recordPath = recordPath;
}
public int getMaxNotifyCountQueue() {
return maxNotifyCountQueue;
}

View File

@ -43,7 +43,7 @@ public class RecordEndEventListener implements ApplicationListener<RecordEndEven
event.getRecordInfo().getDeviceId(),
event.getRecordInfo().getChannelId(), count,sumNum);
logger.debug("handlerMap.size => {}", handlerMap.size());
if (handlerMap.size() > 0) {
if (!handlerMap.isEmpty()) {
String key = deviceId + channelId + sn;
logger.debug("handlerMap.keys => {}", handlerMap.keySet());
RecordEndEventHandler handler = handlerMap.get(deviceId + channelId + sn);
@ -63,6 +63,9 @@ public class RecordEndEventListener implements ApplicationListener<RecordEndEven
scheduleMap.put(key,schedule);
}
}
}else {
logger.info("录像查询完成事件触发, 但是订阅为空取消发送deviceId{}, channelId: {}",
event.getRecordInfo().getDeviceId(), event.getRecordInfo().getChannelId());
}
}

View File

@ -148,13 +148,13 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
if (event.getDeviceChannels() != null) {
deviceChannelList.addAll(event.getDeviceChannels());
}
if (event.getGbStreams() != null && event.getGbStreams().size() > 0){
if (event.getGbStreams() != null && !event.getGbStreams().isEmpty()){
for (GbStream gbStream : event.getGbStreams()) {
deviceChannelList.add(
gbStreamService.getDeviceChannelListByStreamWithStatus(gbStream, gbStream.getCatalogId(), parentPlatform));
}
}
if (deviceChannelList.size() > 0) {
if (!deviceChannelList.isEmpty()) {
logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size());
try {
sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), parentPlatform, deviceChannelList, subscribe, null);
@ -163,10 +163,10 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage());
}
}
}else if (parentPlatformMap.keySet().size() > 0) {
}else if (!parentPlatformMap.keySet().isEmpty()) {
for (String gbId : parentPlatformMap.keySet()) {
List<ParentPlatform> parentPlatforms = parentPlatformMap.get(gbId);
if (parentPlatforms != null && parentPlatforms.size() > 0) {
if (parentPlatforms != null && !parentPlatforms.isEmpty()) {
for (ParentPlatform platform : parentPlatforms) {
SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId());
if (subscribeInfo == null) {

View File

@ -75,6 +75,33 @@ public class VideoStreamSessionManager {
return (SsrcTransaction)redisTemplate.opsForValue().get(scanResult.get(0));
}
public SsrcTransaction getSsrcTransactionByCallId(String callId){
if (ObjectUtils.isEmpty(callId)) {
return null;
}
String key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_*_*_" + callId+ "_*";
List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
if (!scanResult.isEmpty()) {
return (SsrcTransaction)redisTemplate.opsForValue().get(scanResult.get(0));
}else {
key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_*_*_play_*";
scanResult = RedisUtil.scan(redisTemplate, key);
if (scanResult.isEmpty()) {
return null;
}
for (Object keyObj : scanResult) {
SsrcTransaction ssrcTransaction = (SsrcTransaction)redisTemplate.opsForValue().get(keyObj);
if (ssrcTransaction.getSipTransactionInfo() != null &&
ssrcTransaction.getSipTransactionInfo().getCallId().equals(callId)) {
return ssrcTransaction;
}
}
return null;
}
}
public List<SsrcTransaction> getSsrcTransactionForAll(String deviceId, String channelId, String callId, String stream){
if (ObjectUtils.isEmpty(deviceId)) {
deviceId ="*";

View File

@ -129,4 +129,6 @@ public class SipRunner implements CommandLineRunner {
}
}
}
}

View File

@ -66,17 +66,17 @@ public class SIPSender {
// 添加错误订阅
if (errorEvent != null) {
sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
errorEvent.response(eventResult);
sipSubscribe.removeErrorSubscribe(eventResult.callId);
sipSubscribe.removeOkSubscribe(eventResult.callId);
errorEvent.response(eventResult);
}));
}
// 添加订阅
if (okEvent != null) {
sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
okEvent.response(eventResult);
sipSubscribe.removeOkSubscribe(eventResult.callId);
sipSubscribe.removeErrorSubscribe(eventResult.callId);
okEvent.response(eventResult);
});
}
if ("TCP".equals(transport)) {

View File

@ -557,7 +557,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
@Override
public void sendNotifyForCatalogAddOrUpdate(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException {
if (parentPlatform == null || deviceChannels == null || deviceChannels.size() == 0 || subscribeInfo == null) {
if (parentPlatform == null || deviceChannels == null || deviceChannels.isEmpty() || subscribeInfo == null) {
return;
}
if (index == null) {
@ -575,6 +575,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
Integer finalIndex = index;
String catalogXmlContent = getCatalogXmlContentForCatalogAddOrUpdate(parentPlatform, channels,
deviceChannels.size(), type, subscribeInfo);
logger.info("[发送NOTIFY通知]类型: {},发送数量: {}", type, channels.size());
sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> {
logger.error("发送NOTIFY通知消息失败。错误{} {}", eventResult.statusCode, eventResult.msg);
}, (eventResult -> {
@ -598,7 +599,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
SIPRequest notifyRequest = headerProviderPlatformProvider.createNotifyRequest(parentPlatform, catalogXmlContent, subscribeInfo);
sipSender.transmitRequest(parentPlatform.getDeviceIp(), notifyRequest);
sipSender.transmitRequest(parentPlatform.getDeviceIp(), notifyRequest, errorEvent, okEvent);
}
private String getCatalogXmlContentForCatalogAddOrUpdate(ParentPlatform parentPlatform, List<DeviceChannel> channels, int sumNum, String type, SubscribeInfo subscribeInfo) {
@ -610,9 +611,9 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
.append("<CmdType>Catalog</CmdType>\r\n")
.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n")
.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
.append("<SumNum>1</SumNum>\r\n")
.append("<SumNum>"+ sumNum +"</SumNum>\r\n")
.append("<DeviceList Num=\"" + channels.size() + "\">\r\n");
if (channels.size() > 0) {
if (!channels.isEmpty()) {
for (DeviceChannel channel : channels) {
if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
channel.setParentId(parentPlatform.getDeviceGBId());
@ -679,6 +680,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
}else {
channels = deviceChannels.subList(index, deviceChannels.size());
}
logger.info("[发送NOTIFY通知]类型: {},发送数量: {}", type, channels.size());
Integer finalIndex = index;
String catalogXmlContent = getCatalogXmlContentForCatalogOther(parentPlatform, channels, type);
sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> {
@ -725,13 +727,14 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
if ( parentPlatform ==null) {
return ;
}
logger.info("[国标级联] 发送录像数据通道: {}", recordInfo.getChannelId());
String characterSet = parentPlatform.getCharacterSet();
StringBuffer recordXml = new StringBuffer(600);
recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
.append("<Response>\r\n")
.append("<CmdType>RecordInfo</CmdType>\r\n")
.append("<SN>" +recordInfo.getSn() + "</SN>\r\n")
.append("<DeviceID>" + recordInfo.getChannelId() + "</DeviceID>\r\n")
.append("<DeviceID>" + deviceChannel.getChannelId() + "</DeviceID>\r\n")
.append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
if (recordInfo.getRecordList() == null ) {
recordXml.append("<RecordList Num=\"0\">\r\n");
@ -741,7 +744,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
for (RecordItem recordItem : recordInfo.getRecordList()) {
recordXml.append("<Item>\r\n");
if (deviceChannel != null) {
recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n")
recordXml.append("<DeviceID>" + deviceChannel.getChannelId() + "</DeviceID>\r\n")
.append("<Name>" + recordItem.getName() + "</Name>\r\n")
.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n")
.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n")
@ -761,12 +764,14 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
recordXml.append("</RecordList>\r\n")
.append("</Response>\r\n");
logger.info("[国标级联] 发送录像数据通道:{}, 内容: {}", recordInfo.getChannelId(), recordXml);
// callid
CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, recordXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
sipSender.transmitRequest(parentPlatform.getDeviceIp(), request);
sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, eventResult -> {
logger.info("[国标级联] 发送录像数据通道:{}, 发送成功", recordInfo.getChannelId());
});
}

View File

@ -31,6 +31,7 @@ import javax.sip.header.CallIdHeader;
import javax.sip.message.Response;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@ -149,7 +150,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
}else {
// 可能是设备发送的停止
SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
SsrcTransaction ssrcTransaction = streamSession.getSsrcTransactionByCallId(callIdHeader.getCallId());
if (ssrcTransaction == null) {
return;
}

View File

@ -145,7 +145,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
String requesterId = SipUtils.getUserIdFromFromHeader(request);
CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
if (requesterId == null || channelId == null) {
logger.info("无法从FromHeader的Address中获取到平台id返回400");
logger.info("无法从请求中获取到平台id返回400");
// 参数不全 发400请求错误
try {
responseAck(request, Response.BAD_REQUEST);
@ -155,6 +155,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
return;
}
logger.info("[INVITE] requesterId: {}, callId: {}, 来自:{}{}",
requesterId, callIdHeader.getCallId(), request.getRemoteAddress(), request.getRemotePort());
// 查询请求是否来自上级平台\设备
ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);
@ -415,7 +417,16 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
// 非严格模式端口不统一, 增加兼容性修改为一个不为0的端口
localPort = new Random().nextInt(65535) + 1;
}
content.append("m=video " + localPort + " RTP/AVP 96\r\n");
if (sendRtpItem.isTcp()) {
content.append("m=video " + localPort + " TCP/RTP/AVP 96\r\n");
if (!sendRtpItem.isTcpActive()) {
content.append("a=setup:active\r\n");
} else {
content.append("a=setup:passive\r\n");
}
}else {
content.append("m=video " + localPort + " RTP/AVP 96\r\n");
}
content.append("a=sendonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
content.append("y=" + sendRtpItem.getSsrc() + "\r\n");
@ -540,7 +551,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
}
});
} else {
sendRtpItem.setPlayType(InviteStreamType.PLAY);
String streamId = String.format("%s_%s", device.getDeviceId(), channelId);
sendRtpItem.setStreamId(streamId);
redisCatchStorage.updateSendRTPSever(sendRtpItem);
SSRCInfo ssrcInfo = playService.play(mediaServerItem, device.getDeviceId(), channelId, ssrc, ((code, msg, data) -> {
if (code == InviteErrorCode.SUCCESS.getCode()) {
hookEvent.run(code, msg, data);
@ -552,9 +566,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
errorEvent.run(code, msg, data);
}
}));
sendRtpItem.setPlayType(InviteStreamType.PLAY);
String streamId = String.format("%s_%s", device.getDeviceId(), channelId);
sendRtpItem.setStreamId(streamId);
sendRtpItem.setSsrc(ssrcInfo.getSsrc());
redisCatchStorage.updateSendRTPSever(sendRtpItem);
@ -736,9 +747,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
zlmHttpHookSubscribe.removeSubscribe(hookSubscribe);
dynamicTask.stop(callIdHeader.getCallId());
}
} else if ("push".equals(gbStream.getStreamType())) {
if (!platform.isStartOfflinePush()) {
// 平台设置中关闭了拉起离线的推流则直接回复
@ -761,13 +769,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream());
try {
redisPushStreamResponseListener.removeEvent(gbStream.getApp(), gbStream.getStream());
mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream());
responseAck(request, Response.REQUEST_TIMEOUT); // 超时
} catch (SipException e) {
logger.error("未处理的异常 ", e);
} catch (InvalidArgumentException e) {
logger.error("未处理的异常 ", e);
} catch (ParseException e) {
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("未处理的异常 ", e);
}
}, userSetting.getPlatformPlayTimeout());
@ -778,6 +783,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
// 添加在本机上线的通知
mediaListManager.addChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream(), (app, stream, serverId) -> {
dynamicTask.stop(callIdHeader.getCallId());
redisPushStreamResponseListener.removeEvent(gbStream.getApp(), gbStream.getStream());
if (serverId.equals(userSetting.getServerId())) {
SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId,
app, stream, channelId, mediaTransmissionTCP, platform.isRtcp());

View File

@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
import com.genersoft.iot.vmp.service.IDeviceChannelService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.utils.DateUtil;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.slf4j.Logger;
@ -185,6 +186,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
// 判断此通道是否存在
DeviceChannel deviceChannel = deviceChannelService.getOne(deviceId, channel.getChannelId());
if (deviceChannel != null) {
logger.info("[增加通道] 已存在,不发送通知只更新,设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
channel.setId(deviceChannel.getId());
updateChannelMap.put(channel.getChannelId(), channel);
if (updateChannelMap.keySet().size() > 300) {
@ -222,6 +224,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
DeviceChannel deviceChannelForUpdate = deviceChannelService.getOne(deviceId, channel.getChannelId());
if (deviceChannelForUpdate != null) {
channel.setId(deviceChannelForUpdate.getId());
channel.setUpdateTime(DateUtil.getNow());
updateChannelMap.put(channel.getChannelId(), channel);
if (updateChannelMap.keySet().size() > 300) {
executeSaveForUpdate();
@ -244,11 +247,11 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
// 转发变化信息
eventPublisher.catalogEventPublish(null, channel, event);
if (updateChannelMap.keySet().size() > 0
|| addChannelMap.keySet().size() > 0
|| updateChannelOnlineList.size() > 0
|| updateChannelOfflineList.size() > 0
|| deleteChannelList.size() > 0) {
if (!updateChannelMap.keySet().isEmpty()
|| !addChannelMap.keySet().isEmpty()
|| !updateChannelOnlineList.isEmpty()
|| !updateChannelOfflineList.isEmpty()
|| !deleteChannelList.isEmpty()) {
if (!dynamicTask.contains(talkKey)) {
dynamicTask.startDelay(talkKey, this::executeSave, 1000);
@ -262,16 +265,36 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
}
private void executeSave(){
executeSaveForAdd();
executeSaveForUpdate();
executeSaveForDelete();
executeSaveForOnline();
executeSaveForOffline();
try {
executeSaveForAdd();
} catch (Exception e) {
logger.error("[存储收到的增加通道] 异常: ", e );
}
try {
executeSaveForUpdate();
} catch (Exception e) {
logger.error("[存储收到的更新通道] 异常: ", e );
}
try {
executeSaveForDelete();
} catch (Exception e) {
logger.error("[存储收到的删除通道] 异常: ", e );
}
try {
executeSaveForOnline();
} catch (Exception e) {
logger.error("[存储收到的通道上线] 异常: ", e );
}
try {
executeSaveForOffline();
} catch (Exception e) {
logger.error("[存储收到的通道离线] 异常: ", e );
}
dynamicTask.stop(talkKey);
}
private void executeSaveForUpdate(){
if (updateChannelMap.values().size() > 0) {
if (!updateChannelMap.values().isEmpty()) {
ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(updateChannelMap.values());
updateChannelMap.clear();
deviceChannelService.batchUpdateChannel(deviceChannels);
@ -280,7 +303,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
}
private void executeSaveForAdd(){
if (addChannelMap.values().size() > 0) {
if (!addChannelMap.values().isEmpty()) {
ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(addChannelMap.values());
addChannelMap.clear();
deviceChannelService.batchAddChannel(deviceChannels);
@ -288,21 +311,21 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
}
private void executeSaveForDelete(){
if (deleteChannelList.size() > 0) {
if (!deleteChannelList.isEmpty()) {
deviceChannelService.deleteChannels(deleteChannelList);
deleteChannelList.clear();
}
}
private void executeSaveForOnline(){
if (updateChannelOnlineList.size() > 0) {
if (!updateChannelOnlineList.isEmpty()) {
deviceChannelService.channelsOnline(updateChannelOnlineList);
updateChannelOnlineList.clear();
}
}
private void executeSaveForOffline(){
if (updateChannelOfflineList.size() > 0) {
if (!updateChannelOfflineList.isEmpty()) {
deviceChannelService.channelsOffline(updateChannelOfflineList);
updateChannelOfflineList.clear();
}

View File

@ -103,23 +103,27 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
String title = registerFlag ? "[注册请求]": "[注销请求]";
logger.info(title + "设备:{}, 开始处理: {}", deviceId, requestAddress);
if (device != null &&
device.getSipTransactionInfo() != null &&
request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) {
device.getSipTransactionInfo() != null &&
request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) {
logger.info(title + "设备:{}, 注册续订: {}",device.getDeviceId(), device.getDeviceId());
device.setExpires(request.getExpires().getExpires());
device.setIp(remoteAddressInfo.getIp());
device.setPort(remoteAddressInfo.getPort());
device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
device.setLocalIp(request.getLocalAddress().getHostAddress());
Response registerOkResponse = getRegisterOkResponse(request);
// 判断TCP还是UDP
ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
String transport = reqViaHeader.getTransport();
device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), registerOkResponse);
device.setRegisterTime(DateUtil.getNow());
SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)registerOkResponse);
deviceService.online(device, sipTransactionInfo);
if (registerFlag) {
device.setExpires(request.getExpires().getExpires());
device.setIp(remoteAddressInfo.getIp());
device.setPort(remoteAddressInfo.getPort());
device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
device.setLocalIp(request.getLocalAddress().getHostAddress());
Response registerOkResponse = getRegisterOkResponse(request);
// 判断TCP还是UDP
ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
String transport = reqViaHeader.getTransport();
device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), registerOkResponse);
device.setRegisterTime(DateUtil.getNow());
SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)registerOkResponse);
deviceService.online(device, sipTransactionInfo);
}else {
deviceService.offline(deviceId, "主动注销");
}
return;
}
String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword();

View File

@ -76,7 +76,7 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress());
if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) {
logger.info("[心跳] 设备{}地址变化, 远程地址为: {}:{}", device.getDeviceId(), remoteAddressInfo.getIp(), remoteAddressInfo.getPort());
logger.info("[收到心跳] 设备{}地址变化, 远程地址为: {}:{}", device.getDeviceId(), remoteAddressInfo.getIp(), remoteAddressInfo.getPort());
device.setPort(remoteAddressInfo.getPort());
device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
device.setIp(remoteAddressInfo.getIp());

View File

@ -2,19 +2,27 @@ package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import okhttp3.*;
import okhttp3.logging.HttpLoggingInterceptor;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import java.io.IOException;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Component
public class AssistRESTfulUtils {
@ -22,21 +30,41 @@ public class AssistRESTfulUtils {
private final static Logger logger = LoggerFactory.getLogger(AssistRESTfulUtils.class);
private OkHttpClient client;
public interface RequestCallback{
void run(JSONObject response);
}
private OkHttpClient getClient(){
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
if (logger.isDebugEnabled()) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> {
logger.debug("http请求参数" + message);
});
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
// OkHttp進行添加攔截器loggingInterceptor
httpClientBuilder.addInterceptor(logging);
return getClient(null);
}
private OkHttpClient getClient(Integer readTimeOut){
if (client == null) {
if (readTimeOut == null) {
readTimeOut = 10;
}
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
// 设置连接超时时间
httpClientBuilder.connectTimeout(8, TimeUnit.SECONDS);
// 设置读取超时时间
httpClientBuilder.readTimeout(readTimeOut,TimeUnit.SECONDS);
// 设置连接池
httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES));
if (logger.isDebugEnabled()) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> {
logger.debug("http请求参数" + message);
});
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
// OkHttp進行添加攔截器loggingInterceptor
httpClientBuilder.addInterceptor(logging);
}
client = httpClientBuilder.build();
}
return httpClientBuilder.build();
return client;
}
@ -50,11 +78,11 @@ public class AssistRESTfulUtils {
logger.warn("未启用Assist服务");
return null;
}
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(String.format("http://%s:%s/%s", mediaServerItem.getIp(), mediaServerItem.getRecordAssistPort(), api));
StringBuilder stringBuffer = new StringBuilder();
stringBuffer.append(api);
JSONObject responseJSON = null;
if (param != null && param.keySet().size() > 0) {
if (param != null && !param.keySet().isEmpty()) {
stringBuffer.append("?");
int index = 1;
for (String key : param.keySet()){
@ -69,6 +97,7 @@ public class AssistRESTfulUtils {
}
String url = stringBuffer.toString();
logger.info("[访问assist] {}", url);
Request request = new Request.Builder()
.get()
.url(url)
@ -124,13 +153,92 @@ public class AssistRESTfulUtils {
return responseJSON;
}
public JSONObject sendPost(MediaServerItem mediaServerItem, String url,
JSONObject param, ZLMRESTfulUtils.RequestCallback callback,
Integer readTimeOut) {
OkHttpClient client = getClient(readTimeOut);
public JSONObject fileDuration(MediaServerItem mediaServerItem, String app, String stream, RequestCallback callback){
Map<String, Object> param = new HashMap<>();
param.put("app",app);
param.put("stream",stream);
param.put("recordIng",true);
return sendGet(mediaServerItem, "api/record/file/duration",param, callback);
if (mediaServerItem == null) {
return null;
}
JSONObject responseJSON = new JSONObject();
//-2自定义流媒体 调用错误码
responseJSON.put("code",-2);
responseJSON.put("msg","ASSIST调用失败");
RequestBody requestBodyJson = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), param.toString());
Request request = new Request.Builder()
.post(requestBodyJson)
.url(url)
.addHeader("Content-Type", "application/json")
.build();
if (callback == null) {
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
ResponseBody responseBody = response.body();
if (responseBody != null) {
String responseStr = responseBody.string();
responseJSON = JSON.parseObject(responseStr);
}
}else {
response.close();
Objects.requireNonNull(response.body()).close();
}
}catch (IOException e) {
logger.error(String.format("[ %s ]ASSIST请求失败: %s", url, e.getMessage()));
if(e instanceof SocketTimeoutException){
//读取超时超时异常
logger.error(String.format("读取ASSIST数据失败: %s, %s", url, e.getMessage()));
}
if(e instanceof ConnectException){
//判断连接异常我这里是报Failed to connect to 10.7.5.144
logger.error(String.format("连接ASSIST失败: %s, %s", url, e.getMessage()));
}
}catch (Exception e){
logger.error(String.format("访问ASSIST失败: %s, %s", url, e.getMessage()));
}
}else {
client.newCall(request).enqueue(new Callback(){
@Override
public void onResponse(@NotNull Call call, @NotNull Response response){
if (response.isSuccessful()) {
try {
String responseStr = Objects.requireNonNull(response.body()).string();
callback.run(JSON.parseObject(responseStr));
} catch (IOException e) {
logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage()));
}
}else {
response.close();
Objects.requireNonNull(response.body()).close();
}
}
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage()));
if(e instanceof SocketTimeoutException){
//读取超时超时异常
logger.error(String.format("读取ZLM数据失败: %s, %s", call.request().toString(), e.getMessage()));
}
if(e instanceof ConnectException){
//判断连接异常我这里是报Failed to connect to 10.7.5.144
logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage()));
}
}
});
}
return responseJSON;
}
public JSONObject getInfo(MediaServerItem mediaServerItem, RequestCallback callback){
@ -138,33 +246,43 @@ public class AssistRESTfulUtils {
return sendGet(mediaServerItem, "api/record/info",param, callback);
}
public JSONObject addStreamCallInfo(MediaServerItem mediaServerItem, String app, String stream, String callId, RequestCallback callback){
Map<String, Object> param = new HashMap<>();
param.put("app",app);
param.put("stream",stream);
param.put("callId",callId);
return sendGet(mediaServerItem, "api/record/addStreamCallInfo",param, callback);
public JSONObject addTask(MediaServerItem mediaServerItem, String app, String stream, String startTime,
String endTime, String callId, List<String> filePathList, String remoteHost) {
JSONObject videoTaskInfoJSON = new JSONObject();
videoTaskInfoJSON.put("app", app);
videoTaskInfoJSON.put("stream", stream);
videoTaskInfoJSON.put("startTime", startTime);
videoTaskInfoJSON.put("endTime", endTime);
videoTaskInfoJSON.put("callId", callId);
videoTaskInfoJSON.put("filePathList", filePathList);
if (!ObjectUtils.isEmpty(remoteHost)) {
videoTaskInfoJSON.put("remoteHost", remoteHost);
}
String urlStr = String.format("%s/api/record/file/download/task/add", remoteHost);;
return sendPost(mediaServerItem, urlStr, videoTaskInfoJSON, null, 30);
}
public JSONObject getDateList(MediaServerItem mediaServerItem, String app, String stream, int year, int month) {
public JSONObject queryTaskList(MediaServerItem mediaServerItem, String app, String stream, String callId,
String taskId, Boolean isEnd, String scheme) {
Map<String, Object> param = new HashMap<>();
param.put("app", app);
param.put("stream", stream);
param.put("year", year);
param.put("month", month);
return sendGet(mediaServerItem, "api/record/date/list", param, null);
if (!ObjectUtils.isEmpty(app)) {
param.put("app", app);
}
if (!ObjectUtils.isEmpty(stream)) {
param.put("stream", stream);
}
if (!ObjectUtils.isEmpty(callId)) {
param.put("callId", callId);
}
if (!ObjectUtils.isEmpty(taskId)) {
param.put("taskId", taskId);
}
if (!ObjectUtils.isEmpty(isEnd)) {
param.put("isEnd", isEnd);
}
String urlStr = String.format("%s://%s:%s/api/record/file/download/task/list",
scheme, mediaServerItem.getIp(), mediaServerItem.getRecordAssistPort());;
return sendGet(mediaServerItem, urlStr, param, null);
}
public JSONObject getFileList(MediaServerItem mediaServerItem, int page, int count, String app, String stream,
String startTime, String endTime) {
Map<String, Object> param = new HashMap<>();
param.put("app", app);
param.put("stream", stream);
param.put("page", page);
param.put("count", count);
param.put("startTime", startTime);
param.put("endTime", endTime);
return sendGet(mediaServerItem, "api/record/file/listWithDate", param, null);
}
}

View File

@ -110,6 +110,9 @@ public class ZLMHttpHookListener {
@Autowired
private IUserService userService;
@Autowired
private ICloudRecordService cloudRecordService;
@Autowired
private VideoStreamSessionManager sessionManager;
@ -231,12 +234,6 @@ public class ZLMHttpHookListener {
streamAuthorityInfo.setSign(sign);
// 鉴权通过
redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
// 通知assist新的callId
if (mediaInfo != null && mediaInfo.getRecordAssistPort() > 0) {
taskExecutor.execute(() -> {
assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null);
});
}
}
} else {
zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId());
@ -262,7 +259,6 @@ public class ZLMHttpHookListener {
} else {
result.setEnable_mp4(userSetting.isRecordPushLive());
}
// 国标流
if ("rtp".equals(param.getApp()) ) {
@ -279,9 +275,17 @@ public class ZLMHttpHookListener {
}
// 设置音频信息及录制信息
List<SsrcTransaction> ssrcTransactionForAll = (inviteInfo == null ? null :
sessionManager.getSsrcTransactionForAll(inviteInfo.getDeviceId(), inviteInfo.getChannelId(), null, null));
List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
// 为录制国标模拟一个鉴权信息, 方便后续写入录像文件时使用
StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
streamAuthorityInfo.setApp(param.getApp());
streamAuthorityInfo.setStream(ssrcTransactionForAll.get(0).getStream());
streamAuthorityInfo.setCallId(ssrcTransactionForAll.get(0).getSipTransactionInfo().getCallId());
redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), ssrcTransactionForAll.get(0).getStream(), streamAuthorityInfo);
String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
String channelId = ssrcTransactionForAll.get(0).getChannelId();
DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
@ -290,26 +294,16 @@ public class ZLMHttpHookListener {
}
// 如果是录像下载就设置视频间隔十秒
if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {
result.setMp4_max_second(10);
result.setEnable_mp4(true);
}
}
}
if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
logger.info("推流时发现尚未设置录像路径从assist服务中读取");
JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null);
if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) {
JSONObject dataJson = info.getJSONObject("data");
if (dataJson != null) {
String recordPath = dataJson.getString("record");
userSetting.setRecordPath(recordPath);
result.setMp4_save_path(recordPath);
// 修改zlm中的录像路径
if (mediaInfo.isAutoConfig()) {
taskExecutor.execute(() -> {
mediaServerService.setZLMConfig(mediaInfo, false);
});
// 获取录像的总时长然后设置为这个视频的时长
InviteInfo inviteInfoForDownload = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, param.getStream());
if (inviteInfoForDownload != null && inviteInfoForDownload.getStreamInfo() != null) {
String startTime = inviteInfoForDownload.getStreamInfo().getStartTime();
String endTime = inviteInfoForDownload.getStreamInfo().getEndTime();
long difference = DateUtil.getDifference(startTime, endTime) / 1000;
result.setMp4_max_second((int) difference);
result.setEnable_mp4(true);
// 设置为2保证得到的mp4的时长是正常的
result.setModify_stamp(2);
}
}
}
@ -357,13 +351,11 @@ public class ZLMHttpHookListener {
List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
// TODO 重构此处逻辑
boolean isPush = false;
if (param.isRegist()) {
// 处理流注册的鉴权信息
// 处理流注册的鉴权信息 流注销这里不再删除鉴权信息下次来了新的鉴权信息会对就的进行覆盖
if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
isPush = true;
StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
if (streamAuthorityInfo == null) {
streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
@ -373,8 +365,6 @@ public class ZLMHttpHookListener {
}
redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
}
} else {
redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());
}
if ("rtsp".equals(param.getSchema())) {
@ -415,6 +405,9 @@ public class ZLMHttpHookListener {
|| param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
param.setSeverId(userSetting.getServerId());
zlmMediaListManager.addPush(param);
// 冗余数据自己系统中自用
redisCatchStorage.addPushListItem(param.getApp(), param.getStream(), param);
}
} else {
// 兼容流注销时类型从redis记录获取
@ -423,6 +416,10 @@ public class ZLMHttpHookListener {
if (onStreamChangedHookParam != null) {
type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
redisCatchStorage.removeStream(mediaInfo.getId(), type, param.getApp(), param.getStream());
if ("PUSH".equalsIgnoreCase(type)) {
// 冗余数据自己系统中自用
redisCatchStorage.removePushListItem(param.getApp(), param.getStream(), param.getMediaServerId());
}
}
GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
if (gbStream != null) {
@ -771,7 +768,7 @@ public class ZLMHttpHookListener {
taskExecutor.execute(() -> {
JSONObject json = (JSONObject) JSON.toJSON(param);
List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
if (subscribes != null && subscribes.size() > 0) {
if (subscribes != null && !subscribes.isEmpty()) {
for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
subscribe.response(null, param);
}
@ -781,6 +778,28 @@ public class ZLMHttpHookListener {
return HookResult.SUCCESS();
}
/**
* 录像完成事件
*/
@ResponseBody
@PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8")
public HookResult onRecordMp4(HttpServletRequest request, @RequestBody OnRecordMp4HookParam param) {
logger.info("[ZLM HOOK] 录像完成事件:{}->{}", param.getMediaServerId(), param.getFile_path());
taskExecutor.execute(() -> {
List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_record_mp4);
if (subscribes != null && !subscribes.isEmpty()) {
for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
subscribe.response(null, param);
}
}
cloudRecordService.addRecord(param);
});
return HookResult.SUCCESS();
}
private Map<String, String> urlParamToMap(String params) {
HashMap<String, String> map = new HashMap<>();
if (ObjectUtils.isEmpty(params)) {

View File

@ -25,8 +25,6 @@ public class ZLMRESTfulUtils {
private OkHttpClient client;
public interface RequestCallback{
void run(JSONObject response);
}
@ -398,4 +396,14 @@ public class ZLMRESTfulUtils {
param.put("stream_id", streamId);
return sendPost(mediaServerItem, "updateRtpServerSSRC",param, null);
}
public JSONObject deleteRecordDirectory(MediaServerItem mediaServerItem, String app, String stream, String date, String fileName) {
Map<String, Object> param = new HashMap<>(1);
param.put("vhost", "__defaultVhost__");
param.put("app", app);
param.put("stream", stream);
param.put("period", date);
param.put("name", fileName);
return sendPost(mediaServerItem, "deleteRecordDirectory",param, null);
}
}

View File

@ -41,4 +41,15 @@ public class HookSubscribeFactory {
return hookSubscribe;
}
public static HookSubscribeForRecordMp4 on_record_mp4(String mediaServerId, String app, String stream) {
HookSubscribeForRecordMp4 hookSubscribe = new HookSubscribeForRecordMp4();
JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject();
subscribeKey.put("app", app);
subscribeKey.put("stream", stream);
subscribeKey.put("mediaServerId", mediaServerId);
hookSubscribe.setContent(subscribeKey);
return hookSubscribe;
}
}

View File

@ -0,0 +1,44 @@
package com.genersoft.iot.vmp.media.zlm.dto;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import java.time.Instant;
/**
* hook订阅-录像完成
* @author lin
*/
public class HookSubscribeForRecordMp4 implements IHookSubscribe{
private HookType hookType = HookType.on_record_mp4;
private JSONObject content;
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Instant expires;
@Override
public HookType getHookType() {
return hookType;
}
@Override
public JSONObject getContent() {
return content;
}
public void setContent(JSONObject content) {
this.content = content;
}
@Override
public Instant getExpires() {
return expires;
}
@Override
public void setExpires(Instant expires) {
this.expires = expires;
}
}

View File

@ -85,9 +85,11 @@ public class MediaServerItem{
@Schema(description = "是否是默认ZLM")
private boolean defaultServer;
@Schema(description = "当前使用到的端口")
private int currentPort;
@Schema(description = "录像存储时长")
private int recordDay;
@Schema(description = "录像存储路径")
private String recordPath;
public MediaServerItem() {
}
@ -274,14 +276,6 @@ public class MediaServerItem{
this.updateTime = updateTime;
}
public int getCurrentPort() {
return currentPort;
}
public void setCurrentPort(int currentPort) {
this.currentPort = currentPort;
}
public boolean isStatus() {
return status;
}
@ -313,4 +307,20 @@ public class MediaServerItem{
public void setSendRtpPortRange(String sendRtpPortRange) {
this.sendRtpPortRange = sendRtpPortRange;
}
public int getRecordDay() {
return recordDay;
}
public void setRecordDay(int recordDay) {
this.recordDay = recordDay;
}
public String getRecordPath() {
return recordPath;
}
public void setRecordPath(String recordPath) {
this.recordPath = recordPath;
}
}

View File

@ -7,6 +7,7 @@ public class HookResultForOnPublish extends HookResult{
private int mp4_max_second;
private String mp4_save_path;
private String stream_replace;
private Integer modify_stamp;
public HookResultForOnPublish() {
}
@ -60,14 +61,23 @@ public class HookResultForOnPublish extends HookResult{
this.stream_replace = stream_replace;
}
public Integer getModify_stamp() {
return modify_stamp;
}
public void setModify_stamp(Integer modify_stamp) {
this.modify_stamp = modify_stamp;
}
@Override
public String toString() {
return "HookResultForOnPublish{" +
"enable_audio=" + enable_audio +
", enable_mp4=" + enable_mp4 +
", mp4_max_second=" + mp4_max_second +
", stream_replace=" + stream_replace +
", mp4_save_path='" + mp4_save_path + '\'' +
", stream_replace='" + stream_replace + '\'' +
", modify_stamp='" + modify_stamp + '\'' +
'}';
}
}

View File

@ -0,0 +1,114 @@
package com.genersoft.iot.vmp.media.zlm.dto.hook;
/**
* zlm hook事件中的on_rtp_server_timeout事件的参数
* @author lin
*/
public class OnRecordMp4HookParam extends HookParam{
private String app;
private String stream;
private String file_name;
private String file_path;
private long file_size;
private String folder;
private String url;
private String vhost;
private long start_time;
private double time_len;
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public String getStream() {
return stream;
}
public void setStream(String stream) {
this.stream = stream;
}
public String getFile_name() {
return file_name;
}
public void setFile_name(String file_name) {
this.file_name = file_name;
}
public String getFile_path() {
return file_path;
}
public void setFile_path(String file_path) {
this.file_path = file_path;
}
public long getFile_size() {
return file_size;
}
public void setFile_size(long file_size) {
this.file_size = file_size;
}
public String getFolder() {
return folder;
}
public void setFolder(String folder) {
this.folder = folder;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getVhost() {
return vhost;
}
public void setVhost(String vhost) {
this.vhost = vhost;
}
public long getStart_time() {
return start_time;
}
public void setStart_time(long start_time) {
this.start_time = start_time;
}
public double getTime_len() {
return time_len;
}
public void setTime_len(double time_len) {
this.time_len = time_len;
}
@Override
public String toString() {
return "OnRecordMp4HookParam{" +
"app='" + app + '\'' +
", stream='" + stream + '\'' +
", file_name='" + file_name + '\'' +
", file_path='" + file_path + '\'' +
", file_size='" + file_size + '\'' +
", folder='" + folder + '\'' +
", url='" + url + '\'' +
", vhost='" + vhost + '\'' +
", start_time=" + start_time +
", time_len=" + time_len +
'}';
}
}

View File

@ -0,0 +1,59 @@
package com.genersoft.iot.vmp.service;
import com.alibaba.fastjson2.JSONArray;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
import com.github.pagehelper.PageInfo;
import java.util.List;
/**
* 云端录像管理
* @author lin
*/
public interface ICloudRecordService {
/**
* 分页回去云端录像列表
*/
PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems);
/**
* 根据hook消息增加一条记录
*/
void addRecord(OnRecordMp4HookParam param);
/**
* 获取所有的日期
*/
List<String> getDateList(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems);
/**
* 添加合并任务
*/
String addTask(String app, String stream, MediaServerItem mediaServerItem, String startTime,
String endTime, String callId, String remoteHost, boolean filterMediaServer);
/**
* 查询合并任务列表
*/
JSONArray queryTask(String app, String stream, String callId, String taskId, String mediaServerId, Boolean isEnd, String scheme);
/**
* 收藏视频收藏的视频过期不会删除
*/
int changeCollect(boolean result, String app, String stream, String mediaServerId, String startTime, String endTime, String callId);
/**
* 添加指定录像收藏
*/
int changeCollectById(Integer recordId, boolean result);
/**
* 获取播放地址
*/
DownloadFileInfo getPlayUrlPath(Integer recordId);
}

View File

@ -87,21 +87,12 @@ public interface IMediaServerService {
void updateMediaServerKeepalive(String mediaServerId, ServerKeepaliveData data);
boolean checkRtpServer(MediaServerItem mediaServerItem, String rtp, String stream);
/**
* 获取负载信息
* @return
*/
MediaServerLoad getLoad(MediaServerItem mediaServerItem);
/**
* 按时间查找录像文件
*/
List<RecordFile> getRecords(String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems);
List<MediaServerItem> getAllWithAssistPort();
/**
* 查找存在录像文件的时间
*/
List<String> getRecordDates(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems);
}

View File

@ -22,11 +22,6 @@ public interface IPlayService {
MediaServerItem getNewMediaServerItem(Device device);
/**
* 获取包含assist服务的节点
*/
MediaServerItem getNewMediaServerItemHasAssist(Device device);
void playBack(String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback);
void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback);
void zlmServerOffline(String mediaServerId);
@ -44,5 +39,4 @@ public interface IPlayService {
void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback);
}

View File

@ -114,4 +114,5 @@ public interface IStreamPushService {
* @return
*/
ResourceBaseInfo getOverview();
}

View File

@ -0,0 +1,205 @@
package com.genersoft.iot.vmp.service.bean;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
/**
* 云端录像数据
*/
public class CloudRecordItem {
/**
* 主键
*/
private int id;
/**
* 应用名
*/
private String app;
/**
*
*/
private String stream;
/**
* 健全ID
*/
private String callId;
/**
* 开始时间
*/
private long startTime;
/**
* 结束时间
*/
private long endTime;
/**
* ZLM Id
*/
private String mediaServerId;
/**
* 文件名称
*/
private String fileName;
/**
* 文件路径
*/
private String filePath;
/**
* 文件夹
*/
private String folder;
/**
* 收藏收藏的文件不移除
*/
private Boolean collect;
/**
* 保留收藏的文件不移除
*/
private Boolean reserve;
/**
* 文件大小
*/
private long fileSize;
/**
* 文件时长
*/
private long timeLen;
public static CloudRecordItem getInstance(OnRecordMp4HookParam param) {
CloudRecordItem cloudRecordItem = new CloudRecordItem();
cloudRecordItem.setApp(param.getApp());
cloudRecordItem.setStream(param.getStream());
cloudRecordItem.setStartTime(param.getStart_time()*1000);
cloudRecordItem.setFileName(param.getFile_name());
cloudRecordItem.setFolder(param.getFolder());
cloudRecordItem.setFileSize(param.getFile_size());
cloudRecordItem.setFilePath(param.getFile_path());
cloudRecordItem.setMediaServerId(param.getMediaServerId());
cloudRecordItem.setTimeLen((long) param.getTime_len() * 1000);
cloudRecordItem.setEndTime((param.getStart_time() + (long)param.getTime_len()) * 1000);
return cloudRecordItem;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public String getStream() {
return stream;
}
public void setStream(String stream) {
this.stream = stream;
}
public String getCallId() {
return callId;
}
public void setCallId(String callId) {
this.callId = callId;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public long getEndTime() {
return endTime;
}
public void setEndTime(long endTime) {
this.endTime = endTime;
}
public String getMediaServerId() {
return mediaServerId;
}
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public String getFolder() {
return folder;
}
public void setFolder(String folder) {
this.folder = folder;
}
public long getFileSize() {
return fileSize;
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
public long getTimeLen() {
return timeLen;
}
public void setTimeLen(long timeLen) {
this.timeLen = timeLen;
}
public Boolean getCollect() {
return collect;
}
public void setCollect(Boolean collect) {
this.collect = collect;
}
public Boolean getReserve() {
return reserve;
}
public void setReserve(Boolean reserve) {
this.reserve = reserve;
}
}

View File

@ -0,0 +1,41 @@
package com.genersoft.iot.vmp.service.bean;
public class DownloadFileInfo {
private String httpPath;
private String httpsPath;
private String httpDomainPath;
private String httpsDomainPath;
public String getHttpPath() {
return httpPath;
}
public void setHttpPath(String httpPath) {
this.httpPath = httpPath;
}
public String getHttpsPath() {
return httpsPath;
}
public void setHttpsPath(String httpsPath) {
this.httpsPath = httpsPath;
}
public String getHttpDomainPath() {
return httpDomainPath;
}
public void setHttpDomainPath(String httpDomainPath) {
this.httpDomainPath = httpDomainPath;
}
public String getHttpsDomainPath() {
return httpsDomainPath;
}
public void setHttpsDomainPath(String httpsDomainPath) {
this.httpsDomainPath = httpsDomainPath;
}
}

View File

@ -29,12 +29,12 @@ public class WvpRedisMsg {
* 消息的ID
*/
private String serial;
private Object content;
private String content;
private final static String requestTag = "req";
private final static String responseTag = "res";
public static WvpRedisMsg getRequestInstance(String fromId, String toId, String cmd, String serial, Object content) {
public static WvpRedisMsg getRequestInstance(String fromId, String toId, String cmd, String serial, String content) {
WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
wvpRedisMsg.setType(requestTag);
wvpRedisMsg.setFromId(fromId);
@ -51,7 +51,7 @@ public class WvpRedisMsg {
return wvpRedisMsg;
}
public static WvpRedisMsg getResponseInstance(String fromId, String toId, String cmd, String serial, Object content) {
public static WvpRedisMsg getResponseInstance(String fromId, String toId, String cmd, String serial, String content) {
WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
wvpRedisMsg.setType(responseTag);
wvpRedisMsg.setFromId(fromId);
@ -106,11 +106,11 @@ public class WvpRedisMsg {
this.cmd = cmd;
}
public Object getContent() {
public String getContent() {
return content;
}
public void setContent(Object content) {
public void setContent(String content) {
this.content = content;
}
}

View File

@ -0,0 +1,235 @@
package com.genersoft.iot.vmp.service.impl;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
import com.genersoft.iot.vmp.service.ICloudRecordService;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
import com.genersoft.iot.vmp.utils.CloudRecordUtils;
import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.*;
import java.util.*;
@Service
@DS("share")
public class CloudRecordServiceImpl implements ICloudRecordService {
private final static Logger logger = LoggerFactory.getLogger(CloudRecordServiceImpl.class);
@Autowired
private CloudRecordServiceMapper cloudRecordServiceMapper;
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private AssistRESTfulUtils assistRESTfulUtils;
@Autowired
private VideoStreamSessionManager streamSession;
@Override
public PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems) {
// 开始时间和结束时间在数据库中都是以秒为单位的
Long startTimeStamp = null;
Long endTimeStamp = null;
if (startTime != null ) {
if (!DateUtil.verification(startTime, DateUtil.formatter)) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "开始时间格式错误,正确格式为: " + DateUtil.formatter);
}
startTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
}
if (endTime != null ) {
if (!DateUtil.verification(endTime, DateUtil.formatter)) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "结束时间格式错误,正确格式为: " + DateUtil.formatter);
}
endTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
}
PageHelper.startPage(page, count);
List<CloudRecordItem> all = cloudRecordServiceMapper.getList(query, app, stream, startTimeStamp, endTimeStamp,
null, mediaServerItems);
return new PageInfo<>(all);
}
@Override
public List<String> getDateList(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems) {
LocalDate startDate = LocalDate.of(year, month, 1);
LocalDate endDate;
if (month == 12) {
endDate = LocalDate.of(year + 1, 1, 1);
}else {
endDate = LocalDate.of(year, month + 1, 1);
}
long startTimeStamp = startDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).getEpochSecond();
long endTimeStamp = endDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).getEpochSecond();
List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp,
endTimeStamp, null, mediaServerItems);
if (cloudRecordItemList.isEmpty()) {
return new ArrayList<>();
}
Set<String> resultSet = new HashSet<>();
cloudRecordItemList.stream().forEach(cloudRecordItem -> {
String date = DateUtil.timestampTo_yyyy_MM_dd(cloudRecordItem.getStartTime());
resultSet.add(date);
});
return new ArrayList<>(resultSet);
}
@Override
public void addRecord(OnRecordMp4HookParam param) {
CloudRecordItem cloudRecordItem = CloudRecordItem.getInstance(param);
StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
if (streamAuthorityInfo != null) {
cloudRecordItem.setCallId(streamAuthorityInfo.getCallId());
}
logger.info("[添加录像记录] {}/{} 文件大小:{}, 时长: {}秒", param.getApp(), param.getStream(), param.getFile_size(),param.getTime_len());
cloudRecordServiceMapper.add(cloudRecordItem);
}
@Override
public String addTask(String app, String stream, MediaServerItem mediaServerItem, String startTime, String endTime,
String callId, String remoteHost, boolean filterMediaServer) {
// 参数校验
assert app != null;
assert stream != null;
if (mediaServerItem.getRecordAssistPort() == 0) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "为配置Assist服务");
}
Long startTimeStamp = null;
Long endTimeStamp = null;
if (startTime != null) {
startTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
}
if (endTime != null) {
endTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
}
List<MediaServerItem> mediaServers = new ArrayList<>();
mediaServers.add(mediaServerItem);
// 检索相关的录像文件
List<String> filePathList = cloudRecordServiceMapper.queryRecordFilePathList(app, stream, startTimeStamp,
endTimeStamp, callId, filterMediaServer ? mediaServers : null);
if (filePathList == null || filePathList.isEmpty()) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未检索到视频文件");
}
JSONObject result = assistRESTfulUtils.addTask(mediaServerItem, app, stream, startTime, endTime, callId, filePathList, remoteHost);
if (result.getInteger("code") != 0) {
throw new ControllerException(result.getInteger("code"), result.getString("msg"));
}
return result.getString("data");
}
@Override
public JSONArray queryTask(String app, String stream, String callId, String taskId, String mediaServerId,
Boolean isEnd, String scheme) {
MediaServerItem mediaServerItem = null;
if (mediaServerId == null) {
mediaServerItem = mediaServerService.getDefaultMediaServer();
}else {
mediaServerItem = mediaServerService.getOne(mediaServerId);
}
if (mediaServerItem == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的流媒体");
}
JSONObject result = assistRESTfulUtils.queryTaskList(mediaServerItem, app, stream, callId, taskId, isEnd, scheme);
if (result == null || result.getInteger("code") != 0) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), result == null ? "查询任务列表失败" : result.getString("msg"));
}
return result.getJSONArray("data");
}
@Override
public int changeCollect(boolean result, String app, String stream, String mediaServerId, String startTime, String endTime, String callId) {
// 开始时间和结束时间在数据库中都是以秒为单位的
Long startTimeStamp = null;
Long endTimeStamp = null;
if (startTime != null ) {
if (!DateUtil.verification(startTime, DateUtil.formatter)) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "开始时间格式错误,正确格式为: " + DateUtil.formatter);
}
startTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
}
if (endTime != null ) {
if (!DateUtil.verification(endTime, DateUtil.formatter)) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "结束时间格式错误,正确格式为: " + DateUtil.formatter);
}
endTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
}
List<MediaServerItem> mediaServerItems;
if (!ObjectUtils.isEmpty(mediaServerId)) {
mediaServerItems = new ArrayList<>();
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
if (mediaServerItem == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到流媒体: " + mediaServerId);
}
mediaServerItems.add(mediaServerItem);
} else {
mediaServerItems = null;
}
List<CloudRecordItem> all = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp, endTimeStamp,
callId, mediaServerItems);
if (all.isEmpty()) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到待收藏的视频");
}
int limitCount = 50;
int resultCount = 0;
if (all.size() > limitCount) {
for (int i = 0; i < all.size(); i += limitCount) {
int toIndex = i + limitCount;
if (i + limitCount > all.size()) {
toIndex = all.size();
}
resultCount += cloudRecordServiceMapper.updateCollectList(result, all.subList(i, toIndex));
}
}else {
resultCount = cloudRecordServiceMapper.updateCollectList(result, all);
}
return resultCount;
}
@Override
public int changeCollectById(Integer recordId, boolean result) {
return cloudRecordServiceMapper.changeCollectById(result, recordId);
}
@Override
public DownloadFileInfo getPlayUrlPath(Integer recordId) {
CloudRecordItem recordItem = cloudRecordServiceMapper.queryOne(recordId);
if (recordItem == null) {
throw new ControllerException(ErrorCode.ERROR400.getCode(), "资源不存在");
}
String filePath = recordItem.getFilePath();
MediaServerItem mediaServerItem = mediaServerService.getOne(recordItem.getMediaServerId());
return CloudRecordUtils.getDownloadFilePath(mediaServerItem, filePath);
}
}

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.service.IDeviceAlarmService;
@ -12,6 +13,7 @@ import org.springframework.stereotype.Service;
import java.util.List;
@Service
@DS("master")
public class DeviceAlarmServiceImpl implements IDeviceAlarmService {
@Autowired

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.common.InviteInfo;
import com.genersoft.iot.vmp.common.InviteSessionType;
import com.genersoft.iot.vmp.gb28181.bean.Device;
@ -27,6 +28,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
* @author lin
*/
@Service
@DS("master")
public class DeviceChannelServiceImpl implements IDeviceChannelService {
private final static Logger logger = LoggerFactory.getLogger(DeviceChannelServiceImpl.class);

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.UserSetting;
@ -45,6 +46,7 @@ import java.util.concurrent.TimeUnit;
* 设备业务目录订阅
*/
@Service
@DS("master")
public class DeviceServiceImpl implements IDeviceService {
private final static Logger logger = LoggerFactory.getLogger(DeviceServiceImpl.class);
@ -152,6 +154,19 @@ public class DeviceServiceImpl implements IDeviceService {
sync(device);
// TODO 如果设备下的通道级联到了其他平台那么需要发送事件或者notify给上级平台
}
// 上线添加订阅
if (device.getSubscribeCycleForCatalog() > 0) {
// 查询在线设备那些开启了订阅为设备开启定时的目录订阅
addCatalogSubscribe(device);
}
if (device.getSubscribeCycleForMobilePosition() > 0) {
addMobilePositionSubscribe(device);
}
if (userSetting.getDeviceStatusNotify()) {
// 发送redis消息
redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, true);
}
}else {
if (deviceChannelMapper.queryAllChannels(device.getDeviceId()).isEmpty()) {
logger.info("[设备上线]: {}通道数为0,查询通道信息", device.getDeviceId());
@ -164,22 +179,10 @@ public class DeviceServiceImpl implements IDeviceService {
}
// 上线添加订阅
if (device.getSubscribeCycleForCatalog() > 0) {
// 查询在线设备那些开启了订阅为设备开启定时的目录订阅
addCatalogSubscribe(device);
}
if (device.getSubscribeCycleForMobilePosition() > 0) {
addMobilePositionSubscribe(device);
}
// 刷新过期任务
String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
// 如果第一次注册那么必须在60 * 3时间内收到一个心跳否则设备离线
dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId(), "首次注册后未能收到心跳"), device.getKeepaliveIntervalTime() * 1000 * 3);
if (userSetting.getDeviceStatusNotify()) {
// 发送redis消息
redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, true);
}
//
// try {
@ -203,6 +206,13 @@ public class DeviceServiceImpl implements IDeviceService {
}
String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + deviceId;
dynamicTask.stop(registerExpireTaskKey);
if (device.isOnLine()) {
if (userSetting.getDeviceStatusNotify()) {
// 发送redis消息
redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, false);
}
}
device.setOnLine(false);
redisCatchStorage.updateDevice(device);
deviceMapper.update(device);
@ -220,11 +230,6 @@ public class DeviceServiceImpl implements IDeviceService {
// 移除订阅
removeCatalogSubscribe(device);
removeMobilePositionSubscribe(device);
if (userSetting.getDeviceStatusNotify()) {
// 发送redis消息
redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, false);
}
}
@Override
@ -271,7 +276,7 @@ public class DeviceServiceImpl implements IDeviceService {
// 设置最小值为30
int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForMobilePosition(),30);
// 刷新订阅
dynamicTask.startCron(device.getDeviceId() + "mobile_position" , mobilePositionSubscribeTask, (subscribeCycleForCatalog) * 1000);
dynamicTask.startCron(device.getDeviceId() + "mobile_position" , mobilePositionSubscribeTask, subscribeCycleForCatalog * 1000);
return true;
}

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
@ -25,6 +26,7 @@ import java.util.ArrayList;
import java.util.List;
@Service
@DS("master")
public class GbStreamServiceImpl implements IGbStreamService {
private final static Logger logger = LoggerFactory.getLogger(GbStreamServiceImpl.class);
@ -77,8 +79,6 @@ public class GbStreamServiceImpl implements IGbStreamService {
}
try {
List<DeviceChannel> deviceChannelList = new ArrayList<>();
for (int i = 0; i < gbStreams.size(); i++) {
GbStream gbStream = gbStreams.get(i);
gbStream.setCatalogId(catalogId);
@ -251,18 +251,17 @@ public class GbStreamServiceImpl implements IGbStreamService {
return ;
}
if (ObjectUtils.isEmpty(catalogId)) {
catalogId = platform.getDeviceGBId();
catalogId = null;
}
if (platformGbStreamMapper.delByPlatformAndCatalogId(platformId, catalogId) > 0) {
List<GbStream> gbStreams = platformGbStreamMapper.queryChannelInParentPlatformAndCatalog(platformId, catalogId);
List<DeviceChannel> deviceChannelList = new ArrayList<>();
for (GbStream gbStream : gbStreams) {
DeviceChannel deviceChannel = new DeviceChannel();
deviceChannel.setChannelId(gbStream.getGbId());
deviceChannelList.add(deviceChannel);
}
eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL);
List<GbStream> gbStreams = platformGbStreamMapper.queryChannelInParentPlatformAndCatalog(platformId, catalogId);
List<DeviceChannel> deviceChannelList = new ArrayList<>();
for (GbStream gbStream : gbStreams) {
DeviceChannel deviceChannel = new DeviceChannel();
deviceChannel.setChannelId(gbStream.getGbId());
deviceChannelList.add(deviceChannel);
}
eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL);
platformGbStreamMapper.delByPlatformAndCatalogId(platformId, catalogId);
}
@Override

View File

@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.service.impl;
import com.alibaba.fastjson2.JSON;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.common.InviteInfo;
import com.genersoft.iot.vmp.common.InviteSessionStatus;
import com.genersoft.iot.vmp.common.InviteSessionType;
@ -20,6 +21,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@Service
@DS("master")
public class InviteStreamServiceImpl implements IInviteStreamService {
private final Logger logger = LoggerFactory.getLogger(InviteStreamServiceImpl.class);
@ -116,9 +118,12 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
":" + (stream != null ? stream : "*")
+ ":*";
List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
if (scanResult.size() != 1) {
if (scanResult.isEmpty()) {
return null;
}
if (scanResult.size() != 1) {
logger.warn("[获取InviteInfo] 发现 key: {}存在多条", key);
}
return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0));
}

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
import com.genersoft.iot.vmp.service.ILogService;
import com.genersoft.iot.vmp.storager.dao.LogMapper;
@ -12,6 +13,7 @@ import org.springframework.stereotype.Service;
import java.util.List;
@Service
@DS("master")
public class LogServiceImpl implements ILogService {
@Autowired

View File

@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.DynamicTask;
@ -55,6 +56,7 @@ import java.util.concurrent.ExecutionException;
* 媒体服务器节点管理
*/
@Service
@DS("master")
@RequiredArgsConstructor
public class MediaServerServiceImpl implements IMediaServerService {
@ -309,7 +311,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
@Override
public MediaServerItem getDefaultMediaServer() {
return mediaServerMapper.queryDefault();
}
@ -424,17 +425,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
if (serverItem.isAutoConfig()) {
// 查看assist服务的录像路径配置
if (serverItem.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
JSONObject info = assistRESTfulUtils.getInfo(serverItem, null);
if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) {
JSONObject dataJson = info.getJSONObject("data");
if (dataJson != null) {
String recordPath = dataJson.getString("record");
userSetting.setRecordPath(recordPath);
}
}
}
setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable()));
}
final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId();
@ -569,7 +559,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
logger.info("[ZLM] 正在设置 {} -> {}:{}",
mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
String protocol = sslEnabled ? "https" : "http";
String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort);
String hookPrefix = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort);
Map<String, Object> param = new HashMap<>();
param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
@ -578,20 +568,21 @@ public class MediaServerServiceImpl implements IMediaServerService {
}
param.put("hook.enable","1");
param.put("hook.on_flow_report","");
param.put("hook.on_play",String.format("%s/on_play", hookPrex));
param.put("hook.on_play",String.format("%s/on_play", hookPrefix));
param.put("hook.on_http_access","");
param.put("hook.on_publish", String.format("%s/on_publish", hookPrex));
param.put("hook.on_publish", String.format("%s/on_publish", hookPrefix));
param.put("hook.on_record_ts","");
param.put("hook.on_rtsp_auth","");
param.put("hook.on_rtsp_realm","");
param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex));
param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrefix));
param.put("hook.on_shell_login","");
param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrex));
param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrex));
param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex));
param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrex));
param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrex));
param.put("hook.on_rtp_server_timeout",String.format("%s/on_rtp_server_timeout", hookPrex));
param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrefix));
param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrefix));
param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrefix));
param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrefix));
param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrefix));
param.put("hook.on_rtp_server_timeout",String.format("%s/on_rtp_server_timeout", hookPrefix));
param.put("hook.on_record_mp4",String.format("%s/on_record_mp4", hookPrefix));
if (mediaServerItem.getRecordAssistPort() > 0) {
if(mediaConfig.getRecordAssistIp().equals("127.0.0.1")){
param.put("hook.on_record_mp4",String.format("http://127.0.0.1:%s/api/record/on_record_mp4", mediaServerItem.getRecordAssistPort()));
@ -610,15 +601,14 @@ public class MediaServerServiceImpl implements IMediaServerService {
param.put("protocol.continue_push_ms", "3000" );
// 最多等待未初始化的Track时间单位毫秒超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流
// 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项
// param.put("general.wait_track_ready_ms", "3000" );
if (mediaServerItem.isRtpEnable() && !ObjectUtils.isEmpty(mediaServerItem.getRtpPortRange())) {
param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-"));
}
if (userSetting.getRecordPath() != null) {
File recordPathFile = new File(userSetting.getRecordPath());
File mp4SavePathFile = recordPathFile.getParentFile().getAbsoluteFile();
param.put("protocol.mp4_save_path", mp4SavePathFile.getAbsoluteFile());
if (!ObjectUtils.isEmpty(mediaServerItem.getRecordPath())) {
File recordPathFile = new File(mediaServerItem.getRecordPath());
param.put("protocol.mp4_save_path", recordPathFile.getParentFile().getPath());
param.put("protocol.downloadRoot", recordPathFile.getParentFile().getPath());
param.put("record.appName", recordPathFile.getName());
}
@ -723,6 +713,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
ssrcFactory.initMediaServerSSRC(mediaServerItem.getId(), null);
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId();
redisTemplate.opsForValue().set(key, mediaServerItem);
resetOnlineServerItem(mediaServerItem);
clearRTPServer(mediaServerItem);
}
final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + mediaServerItem.getId();
@ -750,15 +741,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
}
}
@Override
public boolean checkRtpServer(MediaServerItem mediaServerItem, String app, String stream) {
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, stream);
if(rtpInfo.getInteger("code") == 0){
return rtpInfo.getBoolean("exist");
}
return false;
}
@Override
public MediaServerLoad getLoad(MediaServerItem mediaServerItem) {
MediaServerLoad result = new MediaServerLoad();
@ -772,88 +754,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
}
@Override
public List<RecordFile> getRecords(String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems) {
Assert.notNull(app, "app不存在");
Assert.notNull(stream, "stream不存在");
Assert.notNull(startTime, "startTime不存在");
Assert.notNull(endTime, "endTime不存在");
Assert.notEmpty(mediaServerItems, "流媒体列表为空");
CompletableFuture[] completableFutures = new CompletableFuture[mediaServerItems.size()];
for (int i = 0; i < mediaServerItems.size(); i++) {
completableFutures[i] = getRecordFilesForOne(app, stream, startTime, endTime, mediaServerItems.get(i));
}
List<RecordFile> result = new ArrayList<>();
for (int i = 0; i < completableFutures.length; i++) {
try {
List<RecordFile> list = (List<RecordFile>) completableFutures[i].get();
if (!list.isEmpty()) {
for (int g = 0; g < list.size(); g++) {
list.get(g).setMediaServerId(mediaServerItems.get(i).getId());
}
result.addAll(list);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
Comparator<RecordFile> comparator = Comparator.comparing(RecordFile::getFileName);
result.sort(comparator);
return result;
}
@Override
public List<String> getRecordDates(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems) {
Assert.notNull(app, "app不存在");
Assert.notNull(stream, "stream不存在");
Assert.notEmpty(mediaServerItems, "流媒体列表为空");
CompletableFuture[] completableFutures = new CompletableFuture[mediaServerItems.size()];
for (int i = 0; i < mediaServerItems.size(); i++) {
completableFutures[i] = getRecordDatesForOne(app, stream, year, month, mediaServerItems.get(i));
}
List<String> result = new ArrayList<>();
CompletableFuture.allOf(completableFutures).join();
for (CompletableFuture completableFuture : completableFutures) {
try {
List<String> list = (List<String>) completableFuture.get();
result.addAll(list);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
Collections.sort(result);
return result;
}
@Async
public CompletableFuture<List<String>> getRecordDatesForOne(String app, String stream, int year, int month, MediaServerItem mediaServerItem) {
JSONObject fileListJson = assistRESTfulUtils.getDateList(mediaServerItem, app, stream, year, month);
if (fileListJson != null && !fileListJson.isEmpty()) {
if (fileListJson.getString("code") != null && fileListJson.getInteger("code") == 0) {
JSONArray data = fileListJson.getJSONArray("data");
return CompletableFuture.completedFuture(data.toJavaList(String.class));
}
}
return CompletableFuture.completedFuture(new ArrayList<>());
}
@Async
public CompletableFuture<List<RecordFile>> getRecordFilesForOne(String app, String stream, String startTime, String endTime, MediaServerItem mediaServerItem) {
JSONObject fileListJson = assistRESTfulUtils.getFileList(mediaServerItem, 1, 100000000, app, stream, startTime, endTime);
if (fileListJson != null && !fileListJson.isEmpty()) {
if (fileListJson.getString("code") != null && fileListJson.getInteger("code") == 0) {
JSONObject data = fileListJson.getJSONObject("data");
JSONArray list = data.getJSONArray("list");
if (list != null) {
return CompletableFuture.completedFuture(list.toJavaList(RecordFile.class));
}
}
}
return CompletableFuture.completedFuture(new ArrayList<>());
public List<MediaServerItem> getAllWithAssistPort() {
return mediaServerMapper.queryAllWithAssistPort();
}
}

View File

@ -67,7 +67,7 @@ public class MediaServiceImpl implements IMediaService {
if (data == null) {
return null;
}
JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class);
JSONObject mediaJSON = data.getJSONObject(0);
JSONArray tracks = mediaJSON.getJSONArray("tracks");
if (authority) {
streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr, calld);

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
@ -27,6 +28,7 @@ import java.util.Map;
* @author lin
*/
@Service
@DS("master")
public class PlatformChannelServiceImpl implements IPlatformChannelService {
private final static Logger logger = LoggerFactory.getLogger(PlatformChannelServiceImpl.class);
@ -165,10 +167,9 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService {
catalogId = null;
}
if ((result = platformChannelMapper.delChannelForGBByCatalogId(platformId, catalogId)) > 0) {
List<DeviceChannel> deviceChannels = platformChannelMapper.queryAllChannelInCatalog(platformId, catalogId);
eventPublisher.catalogEventPublish(platformId, deviceChannels, CatalogEvent.DEL);
}
return result;
List<DeviceChannel> deviceChannels = platformChannelMapper.queryAllChannelInCatalog(platformId, catalogId);
eventPublisher.catalogEventPublish(platformId, deviceChannels, CatalogEvent.DEL);
return platformChannelMapper.delChannelForGBByCatalogId(platformId, catalogId);
}
}

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.*;
@ -38,6 +39,7 @@ import java.util.*;
* @author lin
*/
@Service
@DS("master")
public class PlatformServiceImpl implements IPlatformService {
private final static String REGISTER_KEY_PREFIX = "platform_register_";
@ -149,7 +151,7 @@ public class PlatformServiceImpl implements IPlatformService {
dynamicTask.stop(registerTaskKey);
// 注销旧的
try {
if (parentPlatformOld.isStatus()) {
if (parentPlatformOld.isStatus() && parentPlatformCatchOld != null) {
logger.info("保存平台{}时发现旧平台在线,发送注销命令", parentPlatformOld.getServerGBId());
commanderForPlatform.unregister(parentPlatformOld, parentPlatformCatchOld.getSipTransactionInfo(), null, eventResult -> {
logger.info("[国标级联] 注销成功, 平台:{}", parentPlatformOld.getServerGBId());
@ -266,6 +268,7 @@ public class PlatformServiceImpl implements IPlatformService {
}
if (parentPlatform.isAutoPushChannel()) {
if (subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId()) == null) {
logger.info("[国标级联]{}, 添加自动通道推送模拟订阅信息", parentPlatform.getServerGBId());
addSimulatedSubscribeInfo(parentPlatform);
}
}else {
@ -344,9 +347,16 @@ public class PlatformServiceImpl implements IPlatformService {
// 清除心跳任务
dynamicTask.stop(keepaliveTaskKey);
}
// 停止目录订阅回复
logger.info("[平台离线] {}, 停止订阅回复", parentPlatform.getServerGBId());
subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId());
// 停止订阅回复
SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId());
if (catalogSubscribe != null) {
if (catalogSubscribe.getExpires() > 0) {
logger.info("[平台离线] {}, 停止目录订阅回复", parentPlatform.getServerGBId());
subscribeHolder.removeCatalogSubscribe(parentPlatform.getServerGBId());
}
}
logger.info("[平台离线] {}, 停止移动位置订阅回复", parentPlatform.getServerGBId());
subscribeHolder.removeMobilePositionSubscribe(parentPlatform.getServerGBId());
// 发起定时自动重新注册
if (!stopRegister) {
// 设置为60秒自动尝试重新注册

View File

@ -1,6 +1,8 @@
package com.genersoft.iot.vmp.service.impl;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.common.InviteInfo;
import com.genersoft.iot.vmp.common.InviteSessionStatus;
import com.genersoft.iot.vmp.common.InviteSessionType;
@ -13,25 +15,22 @@ import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.*;
import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
import com.genersoft.iot.vmp.service.*;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.service.bean.*;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
import com.genersoft.iot.vmp.utils.CloudRecordUtils;
import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import gov.nist.javax.sip.message.SIPResponse;
@ -56,6 +55,7 @@ import java.util.Vector;
@SuppressWarnings(value = {"rawtypes", "unchecked"})
@Service
@DS("master")
public class PlayServiceImpl implements IPlayService {
private final static Logger logger = LoggerFactory.getLogger(PlayServiceImpl.class);
@ -76,7 +76,7 @@ public class PlayServiceImpl implements IPlayService {
private IInviteStreamService inviteStreamService;
@Autowired
private DeferredResultHolder resultHolder;
private ZlmHttpHookSubscribe subscribe;
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
@ -84,9 +84,6 @@ public class PlayServiceImpl implements IPlayService {
@Autowired
private ZLMServerFactory zlmServerFactory;
@Autowired
private AssistRESTfulUtils assistRESTfulUtils;
@Autowired
private IMediaService mediaService;
@ -106,7 +103,7 @@ public class PlayServiceImpl implements IPlayService {
private DynamicTask dynamicTask;
@Autowired
private ZlmHttpHookSubscribe subscribe;
private CloudRecordServiceMapper cloudRecordServiceMapper;
@Override
@ -444,23 +441,6 @@ public class PlayServiceImpl implements IPlayService {
return mediaServerItem;
}
@Override
public MediaServerItem getNewMediaServerItemHasAssist(Device device) {
if (device == null) {
return null;
}
MediaServerItem mediaServerItem;
if (ObjectUtils.isEmpty(device.getMediaServerId()) || "auto".equals(device.getMediaServerId())) {
mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(true);
} else {
mediaServerItem = mediaServerService.getOne(device.getMediaServerId());
}
if (mediaServerItem == null) {
logger.warn("[获取可用的ZLM节点]未找到可使用的ZLM...");
}
return mediaServerItem;
}
@Override
public void playBack(String deviceId, String channelId, String startTime,
String endTime, ErrorCallback<Object> callback) {
@ -678,7 +658,7 @@ public class PlayServiceImpl implements IPlayService {
if (device == null) {
return;
}
MediaServerItem newMediaServerItem = getNewMediaServerItemHasAssist(device);
MediaServerItem newMediaServerItem = this.getNewMediaServerItem(device);
if (newMediaServerItem == null) {
callback.run(InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getCode(),
InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getMsg(),
@ -759,6 +739,28 @@ public class PlayServiceImpl implements IPlayService {
// 处理收到200ok后的TCP主动连接以及SSRC不一致的问题
InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId,
downLoadTimeOutTaskKey, callback, inviteInfo, InviteSessionType.DOWNLOAD);
// 注册录像回调事件录像下载结束后写入下载地址
ZlmHttpHookSubscribe.Event hookEventForRecord = (mediaServerItemInuse, hookParam) -> {
logger.info("[录像下载] 收到录像写入磁盘消息: {}/{}-{}",
inviteInfo.getDeviceId(), inviteInfo.getChannelId(), ssrcInfo.getStream());
logger.info("[录像下载] 收到录像写入磁盘消息内容: " + hookParam);
OnRecordMp4HookParam recordMp4HookParam = (OnRecordMp4HookParam)hookParam;
String filePath = recordMp4HookParam.getFile_path();
DownloadFileInfo downloadFileInfo = CloudRecordUtils.getDownloadFilePath(mediaServerItem, filePath);
InviteInfo inviteInfoForNew = inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId()
, inviteInfo.getChannelId(), inviteInfo.getStream());
inviteInfoForNew.getStreamInfo().setDownLoadFilePath(downloadFileInfo);
inviteStreamService.updateInviteInfo(inviteInfoForNew);
};
HookSubscribeForRecordMp4 hookSubscribe = HookSubscribeFactory.on_record_mp4(
mediaServerItem.getId(), "rtp", ssrcInfo.getStream());
// 设置过期时间下载失败时自动处理订阅数据
// long difference = DateUtil.getDifference(startTime, endTime)/1000;
// Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.MINUTES.toSeconds(difference * 2));
// hookSubscribe.setExpires(expiresInstant);
subscribe.addSubscribe(hookSubscribe, hookEventForRecord);
});
} catch (InvalidArgumentException | SipException | ParseException e) {
logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
@ -774,47 +776,71 @@ public class PlayServiceImpl implements IPlayService {
@Override
public StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream) {
InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, stream);
if (inviteInfo == null || inviteInfo.getStreamInfo() == null) {
logger.warn("[获取下载进度] 未查询到录像下载的信息");
return null;
}
if (inviteInfo != null && inviteInfo.getStreamInfo() != null) {
if (inviteInfo.getStreamInfo().getProgress() == 1) {
return inviteInfo.getStreamInfo();
}
// 获取当前已下载时长
String mediaServerId = inviteInfo.getStreamInfo().getMediaServerId();
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
if (mediaServerItem == null) {
logger.warn("查询录像信息时发现节点已离线");
return null;
}
if (mediaServerItem.getRecordAssistPort() > 0) {
JSONObject jsonObject = assistRESTfulUtils.fileDuration(mediaServerItem, inviteInfo.getStreamInfo().getApp(), inviteInfo.getStreamInfo().getStream(), null);
if (jsonObject == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接Assist服务失败");
}
if (jsonObject.getInteger("code") == 0) {
long duration = jsonObject.getLong("data");
if (duration == 0) {
inviteInfo.getStreamInfo().setProgress(0);
} else {
String startTime = inviteInfo.getStreamInfo().getStartTime();
String endTime = inviteInfo.getStreamInfo().getEndTime();
long start = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
long end = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
BigDecimal currentCount = new BigDecimal(duration / 1000);
BigDecimal totalCount = new BigDecimal(end - start);
BigDecimal divide = currentCount.divide(totalCount, 2, RoundingMode.HALF_UP);
double process = divide.doubleValue();
inviteInfo.getStreamInfo().setProgress(process);
}
inviteStreamService.updateInviteInfo(inviteInfo);
}
}
if (inviteInfo.getStreamInfo().getProgress() == 1) {
return inviteInfo.getStreamInfo();
}
return null;
// 获取当前已下载时长
String mediaServerId = inviteInfo.getStreamInfo().getMediaServerId();
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
if (mediaServerItem == null) {
logger.warn("[获取下载进度] 查询录像信息时发现节点不存在");
return null;
}
SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(deviceId, channelId, null, stream);
if (ssrcTransaction == null) {
logger.warn("[获取下载进度] 下载已结束");
return null;
}
JSONObject mediaListJson= zlmresTfulUtils.getMediaList(mediaServerItem, "rtp", stream);
if (mediaListJson == null) {
logger.warn("[获取下载进度] 从zlm查询进度失败");
return null;
}
if (mediaListJson.getInteger("code") != 0) {
logger.warn("[获取下载进度] 从zlm查询进度出现错误 {}", mediaListJson.getString("msg"));
return null;
}
JSONArray data = mediaListJson.getJSONArray("data");
if (data == null) {
logger.warn("[获取下载进度] 从zlm查询进度时未返回数据");
return null;
}
JSONObject mediaJSON = data.getJSONObject(0);
JSONArray tracks = mediaJSON.getJSONArray("tracks");
if (tracks.isEmpty()) {
logger.warn("[获取下载进度] 从zlm查询进度时未返回数据");
return null;
}
JSONObject jsonObject = tracks.getJSONObject(0);
long duration = jsonObject.getLongValue("duration");
if (duration == 0) {
inviteInfo.getStreamInfo().setProgress(0);
} else {
String startTime = inviteInfo.getStreamInfo().getStartTime();
String endTime = inviteInfo.getStreamInfo().getEndTime();
// 此时start和end单位是秒
long start = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
long end = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
BigDecimal currentCount = new BigDecimal(duration);
BigDecimal totalCount = new BigDecimal((end - start) * 1000);
BigDecimal divide = currentCount.divide(totalCount, 2, RoundingMode.HALF_UP);
double process = divide.doubleValue();
if (process > 0.999) {
process = 1.0;
}
inviteInfo.getStreamInfo().setProgress(process);
}
inviteStreamService.updateInviteInfo(inviteInfo);
return inviteInfo.getStreamInfo();
}
private StreamInfo onPublishHandlerForDownload(MediaServerItem mediaServerItemInuse, HookParam hookParam, String deviceId, String channelId, String startTime, String endTime) {
@ -948,7 +974,12 @@ public class PlayServiceImpl implements IPlayService {
throw new ServiceException("mediaServer不存在");
}
// zlm 暂停RTP超时检查
JSONObject jsonObject = zlmresTfulUtils.pauseRtpCheck(mediaServerItem, streamId);
// 使用zlm中的流ID
String streamKey = inviteInfo.getStream();
if (!mediaServerItem.isRtpEnable()) {
streamKey = Long.toHexString(Long.parseLong(inviteInfo.getSsrcInfo().getSsrc())).toUpperCase();
}
JSONObject jsonObject = zlmresTfulUtils.pauseRtpCheck(mediaServerItem, streamKey);
if (jsonObject == null || jsonObject.getInteger("code") != 0) {
throw new ServiceException("暂停RTP接收失败");
}
@ -971,7 +1002,12 @@ public class PlayServiceImpl implements IPlayService {
throw new ServiceException("mediaServer不存在");
}
// zlm 暂停RTP超时检查
JSONObject jsonObject = zlmresTfulUtils.resumeRtpCheck(mediaServerItem, streamId);
// 使用zlm中的流ID
String streamKey = inviteInfo.getStream();
if (!mediaServerItem.isRtpEnable()) {
streamKey = Long.toHexString(Long.parseLong(inviteInfo.getSsrcInfo().getSsrc())).toUpperCase();
}
JSONObject jsonObject = zlmresTfulUtils.resumeRtpCheck(mediaServerItem, streamKey);
if (jsonObject == null || jsonObject.getInteger("code") != 0) {
throw new ServiceException("继续RTP接收失败");
}

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.service.IRoleService;
import com.genersoft.iot.vmp.storager.dao.RoleMapper;
import com.genersoft.iot.vmp.storager.dao.dto.Role;
@ -9,6 +10,7 @@ import org.springframework.stereotype.Service;
import java.util.List;
@Service
@DS("master")
public class RoleServerImpl implements IRoleService {
@Autowired

View File

@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.service.impl;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.common.GeneralCallback;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.DynamicTask;
@ -53,6 +54,7 @@ import java.util.stream.Collectors;
* 视频代理业务
*/
@Service
@DS("master")
public class StreamProxyServiceImpl implements IStreamProxyService {
private final static Logger logger = LoggerFactory.getLogger(StreamProxyServiceImpl.class);
@ -132,7 +134,13 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
}
JSONArray dataArray = jsonObject.getJSONArray("data");
JSONObject mediaServerConfig = dataArray.getJSONObject(0);
if (ObjectUtils.isEmpty(param.getFfmpegCmdKey())) {
param.setFfmpegCmdKey("ffmpeg.cmd");
}
String ffmpegCmd = mediaServerConfig.getString(param.getFfmpegCmdKey());
if (ffmpegCmd == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "ffmpeg拉流代理无法获取ffmpeg cmd");
}
String schema = getSchemaFromFFmpegCmd(ffmpegCmd);
if (schema == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "ffmpeg拉流代理无法从ffmpeg cmd中获取到输出格式");
@ -460,7 +468,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
streamProxyMapper.deleteAutoRemoveItemByMediaServerId(mediaServerId);
// 移除拉流代理生成的流信息
// syncPullStream(mediaServerId);
syncPullStream(mediaServerId);
// 恢复流代理, 只查找这个这个流媒体
List<StreamProxyItem> streamProxyListForEnable = storager.getStreamProxyListForEnableInMediaServer(

View File

@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.*;
@ -36,6 +37,7 @@ import java.util.*;
import java.util.stream.Collectors;
@Service
@DS("master")
public class StreamPushServiceImpl implements IStreamPushService {
private final static Logger logger = LoggerFactory.getLogger(StreamPushServiceImpl.class);
@ -282,6 +284,8 @@ public class StreamPushServiceImpl implements IStreamPushService {
redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
// 移除redis内流的信息
redisCatchStorage.removeStream(mediaServerItem.getId(), "PUSH", offlineOnStreamChangedHookParam.getApp(), offlineOnStreamChangedHookParam.getStream());
// 冗余数据自己系统中自用
redisCatchStorage.removePushListItem(offlineOnStreamChangedHookParam.getApp(), offlineOnStreamChangedHookParam.getStream(), mediaServerItem.getId());
}
}
@ -319,6 +323,9 @@ public class StreamPushServiceImpl implements IStreamPushService {
jsonObject.put("register", false);
jsonObject.put("mediaServerId", mediaServerId);
redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
// 冗余数据自己系统中自用
redisCatchStorage.removePushListItem(onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream(), mediaServerId);
}
}
}

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.service.IUserService;
import com.genersoft.iot.vmp.storager.dao.UserMapper;
import com.genersoft.iot.vmp.storager.dao.dto.User;
@ -12,6 +13,7 @@ import org.springframework.util.DigestUtils;
import java.util.List;
@Service
@DS("master")
public class UserServiceImpl implements IUserService {
@Autowired

View File

@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.*;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import org.slf4j.Logger;
@ -77,6 +78,9 @@ public class RedisGbPlayMsgListener implements MessageListener {
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private DynamicTask dynamicTask;
@ -113,8 +117,8 @@ public class RedisGbPlayMsgListener implements MessageListener {
while (!taskQueue.isEmpty()) {
Message msg = taskQueue.poll();
try {
JSONObject msgJSON = JSON.parseObject(msg.getBody(), JSONObject.class);
WvpRedisMsg wvpRedisMsg = JSON.to(WvpRedisMsg.class, msgJSON);
WvpRedisMsg wvpRedisMsg = JSON.parseObject(msg.getBody(), WvpRedisMsg.class);
logger.info("[收到REDIS通知] 消息: {}", JSON.toJSONString(wvpRedisMsg));
if (!userSetting.getServerId().equals(wvpRedisMsg.getToId())) {
continue;
}
@ -123,7 +127,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
switch (wvpRedisMsg.getCmd()){
case WvpRedisMsgCmd.GET_SEND_ITEM:
RequestSendItemMsg content = JSON.to(RequestSendItemMsg.class, wvpRedisMsg.getContent());
RequestSendItemMsg content = JSON.parseObject(wvpRedisMsg.getContent(), RequestSendItemMsg.class);
requestSendItemMsgHand(content, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial());
break;
case WvpRedisMsgCmd.REQUEST_PUSH_STREAM:
@ -242,7 +246,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
result.setData(content);
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId,
WvpRedisMsgCmd.REQUEST_PUSH_STREAM, serial, result);
WvpRedisMsgCmd.REQUEST_PUSH_STREAM, serial, JSON.toJSONString(result));
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
}
@ -260,7 +264,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
result.setMsg("流媒体不存在");
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId,
WvpRedisMsgCmd.GET_SEND_ITEM, serial, result);
WvpRedisMsgCmd.GET_SEND_ITEM, serial, JSON.toJSONString(result));
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
@ -283,7 +287,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
WVPResult<SendRtpItem> result = new WVPResult<>();
result.setCode(ERROR_CODE_TIMEOUT);
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(
userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result
userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, JSON.toJSONString(result)
);
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
@ -322,9 +326,10 @@ public class RedisGbPlayMsgListener implements MessageListener {
responseSendItemMsg.setSendRtpItem(sendRtpItem);
responseSendItemMsg.setMediaServerItem(mediaServerItem);
result.setData(responseSendItemMsg);
redisCatchStorage.updateSendRTPSever(sendRtpItem);
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(
userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result
userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, JSON.toJSONString(result)
);
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
@ -350,7 +355,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
requestSendItemMsg.setServerId(serverId);
String key = UUID.randomUUID().toString();
WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId, WvpRedisMsgCmd.GET_SEND_ITEM,
key, requestSendItemMsg);
key, JSON.toJSONString(requestSendItemMsg));
JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
logger.info("[请求推流SendItem] {}: {}", serverId, jsonObject);
@ -375,7 +380,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
public void sendMsgForStartSendRtpStream(String serverId, RequestPushStreamMsg param, PlayMsgCallbackForStartSendRtpStream callback) {
String key = UUID.randomUUID().toString();
WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId,
WvpRedisMsgCmd.REQUEST_PUSH_STREAM, key, param);
WvpRedisMsgCmd.REQUEST_PUSH_STREAM, key, JSON.toJSONString(param));
JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
logger.info("[REDIS 请求其他平台推流] {}: {}", serverId, jsonObject);

View File

@ -50,11 +50,12 @@ public class RedisGpsMsgListener implements MessageListener {
Message msg = taskQueue.poll();
try {
GPSMsgInfo gpsMsgInfo = JSON.parseObject(msg.getBody(), GPSMsgInfo.class);
logger.info("[REDIS的位置变化通知], {}", JSON.toJSONString(gpsMsgInfo));
// 只是放入redis缓存起来
redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo);
}catch (Exception e) {
logger.warn("[REDIS的ALARM通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
logger.error("[REDIS的ALARM通知] 异常内容: ", e);
logger.warn("[REDIS的位置变化通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
logger.error("[REDIS的位置变化通知] 异常内容: ", e);
}
}
});

View File

@ -73,12 +73,20 @@ public class RedisPushStreamCloseResponseListener implements MessageListener {
MessageForPushChannel pushChannel = JSON.parseObject(message.getBody(), MessageForPushChannel.class);
StreamPushItem push = streamPushService.getPush(pushChannel.getApp(), pushChannel.getStream());
if (push != null) {
if (redisCatchStorage.isChannelSendingRTP(push.getGbId())) {
List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByChnnelId(
push.getGbId());
if (sendRtpItems.size() > 0) {
for (SendRtpItem sendRtpItem : sendRtpItems) {
ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByChnnelId(
push.getGbId());
if (!sendRtpItems.isEmpty()) {
for (SendRtpItem sendRtpItem : sendRtpItems) {
ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
if (parentPlatform != null) {
redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), sendRtpItem.getCallId(), sendRtpItem.getStreamId());
try {
commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem);
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
}
}
if (push.isSelf()) {
// 停止向上级推流
String streamId = sendRtpItem.getStreamId();
Map<String, Object> param = new HashMap<>();
@ -90,12 +98,6 @@ public class RedisPushStreamCloseResponseListener implements MessageListener {
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), sendRtpItem.getCallId(), sendRtpItem.getStreamId());
zlmServerFactory.stopSendRtpStream(mediaInfo, param);
try {
commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem);
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
}
if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) {
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(),

View File

@ -208,4 +208,8 @@ public interface IRedisCatchStorage {
void sendPlatformStartPlayMsg(MessageForPushChannel messageForPushChannel);
void sendPlatformStopPlayMsg(MessageForPushChannel messageForPushChannel);
void addPushListItem(String app, String stream, OnStreamChangedHookParam param);
void removePushListItem(String app, String stream, String mediaServerId);
}

View File

@ -0,0 +1,122 @@
package com.genersoft.iot.vmp.storager.dao;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface CloudRecordServiceMapper {
@Insert(" <script>" +
"INSERT INTO wvp_cloud_record (" +
" app," +
" stream," +
"<if test=\"callId != null\"> call_id,</if>" +
" start_time," +
" end_time," +
" media_server_id," +
" file_name," +
" folder," +
" file_path," +
" file_size," +
" time_len ) " +
"VALUES (" +
" #{app}," +
" #{stream}," +
" <if test=\"callId != null\"> #{callId},</if>" +
" #{startTime}," +
" #{endTime}," +
" #{mediaServerId}," +
" #{fileName}," +
" #{folder}," +
" #{filePath}," +
" #{fileSize}," +
" #{timeLen})" +
" </script>")
int add(CloudRecordItem cloudRecordItem);
@Select(" <script>" +
"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= 'app != null '> and app=#{app}</if>" +
" <if test= 'stream != null '> and stream=#{stream}</if>" +
" <if test= 'startTimeStamp != null '> and end_time &gt;= #{startTimeStamp}</if>" +
" <if test= 'endTimeStamp != null '> and start_time &lt;= #{endTimeStamp}</if>" +
" <if test= 'callId != null '> and call_id = #{callId}</if>" +
" <if test= 'mediaServerItemList != null ' > and media_server_id in " +
" <foreach collection='mediaServerItemList' item='item' open='(' separator=',' close=')' > #{item.id}</foreach>" +
" </if>" +
" order by start_time DESC" +
" </script>")
List<CloudRecordItem> getList(@Param("query") String query, @Param("app") String app, @Param("stream") String stream,
@Param("startTimeStamp")Long startTimeStamp, @Param("endTimeStamp")Long endTimeStamp,
@Param("callId")String callId, List<MediaServerItem> mediaServerItemList);
@Select(" <script>" +
"select file_path" +
" from wvp_cloud_record " +
" where 0 = 0" +
" <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>" +
" <if test= 'endTimeStamp != null '> and start_time &lt;= #{endTimeStamp}</if>" +
" <if test= 'callId != null '> and call_id = #{callId}</if>" +
" <if test= 'mediaServerItemList != null ' > and media_server_id in " +
" <foreach collection='mediaServerItemList' item='item' open='(' separator=',' close=')' > #{item.id}</foreach>" +
" </if>" +
" </script>")
List<String> queryRecordFilePathList(@Param("app") String app, @Param("stream") String stream,
@Param("startTimeStamp")Long startTimeStamp, @Param("endTimeStamp")Long endTimeStamp,
@Param("callId")String callId, List<MediaServerItem> mediaServerItemList);
@Update(" <script>" +
"update wvp_cloud_record set collect = #{collect} where file_path in " +
" <foreach collection='cloudRecordItemList' item='item' open='(' separator=',' close=')' > #{item.filePath}</foreach>" +
" </script>")
int updateCollectList(@Param("collect") boolean collect, List<CloudRecordItem> cloudRecordItemList);
@Delete(" <script>" +
"delete from wvp_cloud_record where media_server_id=#{mediaServerId} and file_path in " +
" <foreach collection='filePathList' item='item' open='(' separator=',' close=')' > #{item}</foreach>" +
" </script>")
void deleteByFileList(List<String> filePathList, @Param("mediaServerId") String mediaServerId);
@Select(" <script>" +
"select *" +
" from wvp_cloud_record " +
" where collect = false and end_time &lt;= #{endTimeStamp} and media_server_id = #{mediaServerId} " +
" </script>")
List<CloudRecordItem> queryRecordListForDelete(@Param("endTimeStamp")Long endTimeStamp, String mediaServerId);
@Update(" <script>" +
"update wvp_cloud_record set collect = #{collect} where id = #{recordId} " +
" </script>")
int changeCollectById(@Param("collect") boolean collect, @Param("recordId") Integer recordId);
@Delete(" <script>" +
"delete from wvp_cloud_record where id in " +
" <foreach collection='cloudRecordItemIdList' item='item' open='(' separator=',' close=')' > #{item.id}</foreach>" +
" </script>")
int deleteList(List<CloudRecordItem> cloudRecordItemIdList);
@Select(" <script>" +
"select *" +
" from wvp_cloud_record " +
"where call_id = #{callId}" +
" </script>")
List<CloudRecordItem> getListByCallId(@Param("callId") String callId);
@Select(" <script>" +
"select *" +
" from wvp_cloud_record " +
"where id = #{id}" +
" </script>")
CloudRecordItem queryOne(@Param("id") Integer id);
}

View File

@ -107,7 +107,11 @@ public interface DeviceChannelMapper {
"wvp_device_channel dc " +
"WHERE " +
"dc.device_id = #{deviceId} " +
" <if test='query != null'> AND (dc.channel_id LIKE concat('%',#{query},'%') OR dc.name LIKE concat('%',#{query},'%') OR dc.name LIKE concat('%',#{query},'%'))</if> " +
" <if test='query != null'> AND (" +
"dc.channel_id LIKE concat('%',#{query},'%') " +
"OR dc.name LIKE concat('%',#{query},'%') " +
"OR dc.custom_name LIKE concat('%',#{query},'%')" +
")</if> " +
" <if test='parentChannelId != null'> AND (dc.parent_id=#{parentChannelId} OR dc.civil_code = #{parentChannelId}) </if> " +
" <if test='online == true' > AND dc.status= true</if>" +
" <if test='online == false' > AND dc.status= false</if>" +

View File

@ -158,7 +158,7 @@ public interface GbStreamMapper {
" <foreach collection='list' item='item' index='index' separator=';'>"+
"UPDATE wvp_gb_stream " +
" SET name=#{item.name},"+
" gb_id=#{item.gb_id}"+
" gb_id=#{item.gbId}"+
" WHERE app=#{item.app} and stream=#{item.stream}"+
"</foreach>"+
"</script>")

View File

@ -31,6 +31,8 @@ public interface MediaServerMapper {
"rtp_port_range,"+
"send_rtp_port_range,"+
"record_assist_port,"+
"record_day,"+
"record_path,"+
"default_server,"+
"create_time,"+
"update_time,"+
@ -55,6 +57,8 @@ public interface MediaServerMapper {
"#{rtpPortRange}, " +
"#{sendRtpPortRange}, " +
"#{recordAssistPort}, " +
"#{recordDay}, " +
"#{recordPath}, " +
"#{defaultServer}, " +
"#{createTime}, " +
"#{updateTime}, " +
@ -82,6 +86,8 @@ public interface MediaServerMapper {
"<if test=\"secret != null\">, secret=#{secret}</if>" +
"<if test=\"recordAssistPort != null\">, record_assist_port=#{recordAssistPort}</if>" +
"<if test=\"hookAliveInterval != null\">, hook_alive_interval=#{hookAliveInterval}</if>" +
"<if test=\"recordDay != null\">, record_day=#{recordDay}</if>" +
"<if test=\"recordPath != null\">, record_path=#{recordPath}</if>" +
"WHERE id=#{id}"+
" </script>"})
int update(MediaServerItem mediaServerItem);
@ -105,6 +111,8 @@ public interface MediaServerMapper {
"<if test=\"sendRtpPortRange != null\">, send_rtp_port_range=#{sendRtpPortRange}</if>" +
"<if test=\"secret != null\">, secret=#{secret}</if>" +
"<if test=\"recordAssistPort != null\">, record_assist_port=#{recordAssistPort}</if>" +
"<if test=\"recordDay != null\">, record_day=#{recordDay}</if>" +
"<if test=\"recordPath != null\">, record_path=#{recordPath}</if>" +
"<if test=\"hookAliveInterval != null\">, hook_alive_interval=#{hookAliveInterval}</if>" +
"WHERE ip=#{ip} and http_port=#{httpPort}"+
" </script>"})
@ -130,4 +138,8 @@ public interface MediaServerMapper {
@Select("SELECT * FROM wvp_media_server WHERE default_server=true")
MediaServerItem queryDefault();
@Select("SELECT * FROM wvp_media_server WHERE record_assist_port > 0")
List<MediaServerItem> queryAllWithAssistPort();
}

View File

@ -58,7 +58,10 @@ public interface PlatformChannelMapper {
@Select("SELECT dc.* from wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id WHERE dc.channel_id=#{channelId} and pgc.platform_id=#{platformId}")
List<DeviceChannel> queryChannelInParentPlatform(@Param("platformId") String platformId, @Param("channelId") String channelId);
@Select("SELECT dc.* from wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id WHERE pgc.platform_id=#{platformId} and pgc.catalog_id=#{catalogId}")
@Select("<script> "+
"SELECT dc.* from wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id WHERE pgc.platform_id=#{platformId} " +
" <if test='catalogId != null' > and pgc.catalog_id=#{catalogId}</if>" +
"</script>")
List<DeviceChannel> queryAllChannelInCatalog(@Param("platformId") String platformId, @Param("catalogId") String catalogId);
@Select(" select dc.channel_id as id, dc.name as name, pgc.platform_id as platform_id, pgc.catalog_id as parent_id, 0 as children_count, 1 as type " +

View File

@ -53,11 +53,14 @@ public interface PlatformGbStreamMapper {
"WHERE gs.app=#{app} AND gs.stream=#{stream} AND pgs.platform_id=#{platformId}")
StreamProxyItem selectOne(@Param("app") String app, @Param("stream") String stream, @Param("platformId") String platformId);
@Select("select gs.* \n" +
"from wvp_gb_stream gs\n" +
@Select("<script> " +
"select gs.* " +
" from wvp_gb_stream gs\n" +
" left join wvp_platform_gb_stream pgs\n" +
" on gs.gb_stream_id = pgs.gb_stream_id\n" +
"where pgs.platform_id=#{platformId} and pgs.catalog_id=#{catalogId}")
" where pgs.platform_id=#{platformId} " +
" <if test='catalogId != null' > and pgs.catalog_id=#{catalogId}</if>" +
"</script>")
List<GbStream> queryChannelInParentPlatformAndCatalog(@Param("platformId") String platformId, @Param("catalogId") String catalogId);
@Select("select gs.gb_id as id, gs.name as name, pgs.platform_id as platform_id, pgs.catalog_id as catalog_id , 0 as children_count, 2 as type\n" +
@ -103,6 +106,9 @@ public interface PlatformGbStreamMapper {
"</script>")
void delByAppAndStreamsByPlatformId(@Param("gbStreams") List<GbStream> gbStreams, @Param("platformId") String platformId);
@Delete("DELETE from wvp_platform_gb_stream WHERE platform_id=#{platformId} and catalog_id=#{catalogId}")
@Delete("<script> "+
"DELETE from wvp_platform_gb_stream WHERE platform_id=#{platformId}" +
" <if test='catalogId != null' > and catalog_id=#{catalogId}</if>" +
"</script>")
int delByPlatformAndCatalogId(@Param("platformId") String platformId, @Param("catalogId") String catalogId);
}

View File

@ -13,9 +13,9 @@ import java.util.List;
public interface StreamPushMapper {
@Insert("INSERT INTO wvp_stream_push (app, stream, total_reader_count, origin_type, origin_type_str, " +
"push_time, alive_second, media_server_id, update_time, create_time, push_ing, self) VALUES" +
"push_time, alive_second, media_server_id, server_id, update_time, create_time, push_ing, self) VALUES" +
"(#{app}, #{stream}, #{totalReaderCount}, #{originType}, #{originTypeStr}, " +
"#{pushTime}, #{aliveSecond}, #{mediaServerId} , #{updateTime} , #{createTime}, " +
"#{pushTime}, #{aliveSecond}, #{mediaServerId} , #{serverId} , #{updateTime} , #{createTime}, " +
"#{pushIng}, #{self} )")
int add(StreamPushItem streamPushItem);
@ -24,6 +24,7 @@ public interface StreamPushMapper {
"UPDATE wvp_stream_push " +
"SET update_time=#{updateTime}" +
"<if test=\"mediaServerId != null\">, media_server_id=#{mediaServerId}</if>" +
"<if test=\"serverId != null\">, server_id=#{serverId}</if>" +
"<if test=\"totalReaderCount != null\">, total_reader_count=#{totalReaderCount}</if>" +
"<if test=\"originType != null\">, origin_type=#{originType}</if>" +
"<if test=\"originTypeStr != null\">, origin_type_str=#{originTypeStr}</if>" +
@ -89,10 +90,10 @@ public interface StreamPushMapper {
@Insert("<script>" +
"Insert INTO wvp_stream_push (app, stream, total_reader_count, origin_type, origin_type_str, " +
"create_time, alive_second, media_server_id, status, push_ing) " +
"create_time, alive_second, media_server_id, server_id, status, push_ing) " +
"VALUES <foreach collection='streamPushItems' item='item' index='index' separator=','>" +
"( #{item.app}, #{item.stream}, #{item.totalReaderCount}, #{item.originType}, " +
"#{item.originTypeStr},#{item.createTime}, #{item.aliveSecond}, #{item.mediaServerId}, #{item.status} ," +
"#{item.originTypeStr},#{item.createTime}, #{item.aliveSecond}, #{item.mediaServerId},#{item.serverId}, #{item.status} ," +
" #{item.pushIng} )" +
" </foreach>" +
"</script>")

View File

@ -609,14 +609,13 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
@Override
public void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online) {
String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_DEVICE_STATUS;
logger.info("[redis通知] 发送 推送设备/通道状态, {}/{}-{}", deviceId, channelId, online);
StringBuilder msg = new StringBuilder();
msg.append(deviceId);
if (channelId != null) {
msg.append(":").append(channelId);
}
msg.append(" ").append(online? "ON":"OFF");
logger.info("[redis通知] 推送状态-> {} ", msg);
logger.info("[redis通知] 推送设备/通道状态-> {} ", msg);
// 使用 RedisTemplate<Object, Object> 发送字符串消息会导致发送的消息多带了双引号
stringRedisTemplate.convertAndSend(key, msg.toString());
}
@ -650,4 +649,20 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
logger.info("[redis发送通知] 发送 上级平台停止观看 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId());
redisTemplate.convertAndSend(key, JSON.toJSON(msg));
}
@Override
public void addPushListItem(String app, String stream, OnStreamChangedHookParam param) {
String key = VideoManagerConstants.PUSH_STREAM_LIST + app + "_" + stream;
redisTemplate.opsForValue().set(key, param);
}
@Override
public void removePushListItem(String app, String stream, String mediaServerId) {
String key = VideoManagerConstants.PUSH_STREAM_LIST + app + "_" + stream;
OnStreamChangedHookParam param = (OnStreamChangedHookParam)redisTemplate.opsForValue().get(key);
if (param != null && param.getMediaServerId().equalsIgnoreCase(mediaServerId)) {
redisTemplate.delete(key);
}
}
}

View File

@ -0,0 +1,22 @@
package com.genersoft.iot.vmp.utils;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
public class CloudRecordUtils {
public static DownloadFileInfo getDownloadFilePath(MediaServerItem mediaServerItem, String filePath) {
DownloadFileInfo downloadFileInfo = new DownloadFileInfo();
String pathTemplate = "%s://%s:%s/index/api/downloadFile?file_path=" + filePath;
downloadFileInfo.setHttpPath(String.format(pathTemplate, "http", mediaServerItem.getStreamIp(),
mediaServerItem.getHttpPort()));
if (mediaServerItem.getHttpSSlPort() > 0) {
downloadFileInfo.setHttpsPath(String.format(pathTemplate, "https", mediaServerItem.getStreamIp(),
mediaServerItem.getHttpSSlPort()));
}
return downloadFileInfo;
}
}

View File

@ -40,11 +40,17 @@ public class DateUtil {
*/
public static final String URL_PATTERN = "yyyyMMddHHmmss";
/**
* 日期格式
*/
public static final String date_PATTERN = "yyyy-MM-dd";
public static final String zoneStr = "Asia/Shanghai";
public static final DateTimeFormatter formatterCompatibleISO8601 = DateTimeFormatter.ofPattern(ISO8601_COMPATIBLE_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(ISO8601_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
public static final DateTimeFormatter DateFormatter = DateTimeFormatter.ofPattern(date_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
public static final DateTimeFormatter urlFormatter = DateTimeFormatter.ofPattern(URL_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) {
@ -71,6 +77,22 @@ public class DateUtil {
return instant.getEpochSecond();
}
/**
* 时间戳 yyyy_MM_dd_HH_mm_ss
*/
public static String timestampTo_yyyy_MM_dd_HH_mm_ss(long timestamp) {
Instant instant = Instant.ofEpochSecond(timestamp);
return formatter.format(LocalDateTime.ofInstant(instant, ZoneId.of(zoneStr)));
}
/**
* 时间戳 yyyy_MM_dd
*/
public static String timestampTo_yyyy_MM_dd(long timestamp) {
Instant instant = Instant.ofEpochMilli(timestamp);
return DateFormatter.format(LocalDateTime.ofInstant(instant, ZoneId.of(zoneStr)));
}
/**
* 获取当前时间
* @return
@ -117,4 +139,13 @@ public class DateUtil {
Instant beforeInstant = Instant.from(formatter.parse(keepaliveTime));
return ChronoUnit.MILLIS.between(beforeInstant, Instant.now());
}
public static long getDifference(String startTime, String endTime) {
if (ObjectUtils.isEmpty(startTime) || ObjectUtils.isEmpty(endTime)) {
return 0;
}
Instant startInstant = Instant.from(formatter.parse(startTime));
Instant endInstant = Instant.from(formatter.parse(endTime));
return ChronoUnit.MILLIS.between(endInstant, startInstant);
}
}

View File

@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.vmanager.bean;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(description = "流信息")
@ -93,6 +94,9 @@ public class StreamContent {
@Schema(description = "结束时间")
private String endTime;
@Schema(description = "文件下载地址(录像下载使用)")
private DownloadFileInfo downLoadFilePath;
private double progress;
public StreamContent(StreamInfo streamInfo) {
@ -170,6 +174,10 @@ public class StreamContent {
this.startTime = streamInfo.getStartTime();
this.endTime = streamInfo.getEndTime();
this.progress = streamInfo.getProgress();
if (streamInfo.getDownLoadFilePath() != null) {
this.downLoadFilePath = streamInfo.getDownLoadFilePath();
}
}
public String getApp() {
@ -411,4 +419,12 @@ public class StreamContent {
public void setProgress(double progress) {
this.progress = progress;
}
public DownloadFileInfo getDownLoadFilePath() {
return downLoadFilePath;
}
public void setDownLoadFilePath(DownloadFileInfo downLoadFilePath) {
this.downLoadFilePath = downLoadFilePath;
}
}

View File

@ -1,17 +1,19 @@
package com.genersoft.iot.vmp.vmanager.cloudRecord;
import com.alibaba.fastjson2.JSONArray;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.conf.security.JwtUtils;
import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager;
import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.ICloudRecordService;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import com.genersoft.iot.vmp.vmanager.bean.PageInfo;
import com.genersoft.iot.vmp.vmanager.bean.RecordFile;
import com.github.pagehelper.PageInfo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
@ -23,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
@ -34,28 +37,15 @@ import java.util.List;
@RequestMapping("/api/cloud/record")
public class CloudRecordController {
@Autowired
private ZLMServerFactory zlmServerFactory;
@Autowired
private SendRtpPortManager sendRtpPortManager;
private final static Logger logger = LoggerFactory.getLogger(CloudRecordController.class);
@Autowired
private ZlmHttpHookSubscribe hookSubscribe;
private ICloudRecordService cloudRecordService;
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private UserSetting userSetting;
@Autowired
private DynamicTask dynamicTask;
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
@ResponseBody
@GetMapping("/date/list")
@ -66,8 +56,8 @@ public class CloudRecordController {
@Parameter(name = "month", description = "月,置空则查询当月", required = false)
@Parameter(name = "mediaServerId", description = "流媒体ID置空则查询全部", required = false)
public List<String> openRtpServer(
@RequestParam String app,
@RequestParam String stream,
@RequestParam(required = true) String app,
@RequestParam(required = true) String stream,
@RequestParam(required = false) int year,
@RequestParam(required = false) int month,
@RequestParam(required = false) String mediaServerId
@ -97,26 +87,28 @@ public class CloudRecordController {
return new ArrayList<>();
}
return mediaServerService.getRecordDates(app, stream, year, month, mediaServerItems);
return cloudRecordService.getDateList(app, stream, year, month, mediaServerItems);
}
@ResponseBody
@GetMapping("/list")
@Operation(summary = "分页查询云端录像", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "app", description = "应用名", required = true)
@Parameter(name = "stream", description = "流ID", required = true)
@Parameter(name = "page", description = "当前页", required = false)
@Parameter(name = "count", description = "每页查询数量", required = false)
@Parameter(name = "startTime", description = "开始时间(yyyy-MM-dd HH:mm:ss)", required = true)
@Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = true)
@Parameter(name = "query", description = "检索内容", required = false)
@Parameter(name = "app", description = "应用名", required = false)
@Parameter(name = "stream", description = "流ID", required = false)
@Parameter(name = "page", description = "当前页", required = true)
@Parameter(name = "count", description = "每页查询数量", required = true)
@Parameter(name = "startTime", description = "开始时间(yyyy-MM-dd HH:mm:ss)", required = false)
@Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = false)
@Parameter(name = "mediaServerId", description = "流媒体ID置空则查询全部流媒体", required = false)
public PageInfo<RecordFile> openRtpServer(
@RequestParam String app,
@RequestParam String stream,
public PageInfo<CloudRecordItem> openRtpServer(
@RequestParam(required = false) String query,
@RequestParam(required = false) String app,
@RequestParam(required = false) String stream,
@RequestParam int page,
@RequestParam int count,
@RequestParam String startTime,
@RequestParam String endTime,
@RequestParam(required = false) String startTime,
@RequestParam(required = false) String endTime,
@RequestParam(required = false) String mediaServerId
) {
@ -135,13 +127,147 @@ public class CloudRecordController {
mediaServerItems = mediaServerService.getAll();
}
if (mediaServerItems.isEmpty()) {
return new PageInfo<>();
throw new ControllerException(ErrorCode.ERROR100.getCode(), "当前无流媒体");
}
List<RecordFile> records = mediaServerService.getRecords(app, stream, startTime, endTime, mediaServerItems);
PageInfo<RecordFile> pageInfo = new PageInfo<>(records);
pageInfo.startPage(page, count);
return pageInfo;
if (query != null && ObjectUtils.isEmpty(query.trim())) {
query = null;
}
if (app != null && ObjectUtils.isEmpty(app.trim())) {
app = null;
}
if (stream != null && ObjectUtils.isEmpty(stream.trim())) {
stream = null;
}
if (startTime != null && ObjectUtils.isEmpty(startTime.trim())) {
startTime = null;
}
if (endTime != null && ObjectUtils.isEmpty(endTime.trim())) {
endTime = null;
}
return cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServerItems);
}
@ResponseBody
@GetMapping("/task/add")
@Operation(summary = "添加合并任务")
@Parameter(name = "app", description = "应用名", required = false)
@Parameter(name = "stream", description = "流ID", required = false)
@Parameter(name = "mediaServerId", description = "流媒体ID", required = false)
@Parameter(name = "startTime", description = "鉴权ID", required = false)
@Parameter(name = "endTime", description = "鉴权ID", required = false)
@Parameter(name = "callId", description = "鉴权ID", required = false)
@Parameter(name = "remoteHost", description = "返回地址时的远程地址", required = false)
public String addTask(
HttpServletRequest request,
@RequestParam(required = false) String app,
@RequestParam(required = false) String stream,
@RequestParam(required = false) String mediaServerId,
@RequestParam(required = false) String startTime,
@RequestParam(required = false) String endTime,
@RequestParam(required = false) String callId,
@RequestParam(required = false) String remoteHost
){
MediaServerItem mediaServerItem;
if (mediaServerId == null) {
mediaServerItem = mediaServerService.getDefaultMediaServer();
}else {
mediaServerItem = mediaServerService.getOne(mediaServerId);
}
if (mediaServerItem == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的流媒体");
}else {
if (remoteHost == null) {
remoteHost = request.getScheme() + "://" + mediaServerItem.getIp() + ":" + mediaServerItem.getRecordAssistPort();
}
}
return cloudRecordService.addTask(app, stream, mediaServerItem, startTime, endTime, callId, remoteHost, mediaServerId != null);
}
@ResponseBody
@GetMapping("/task/list")
@Operation(summary = "查询合并任务")
@Parameter(name = "taskId", description = "任务Id", required = false)
@Parameter(name = "mediaServerId", description = "流媒体ID", required = false)
@Parameter(name = "isEnd", description = "是否结束", required = false)
public JSONArray queryTaskList(
HttpServletRequest request,
@RequestParam(required = false) String app,
@RequestParam(required = false) String stream,
@RequestParam(required = false) String callId,
@RequestParam(required = false) String taskId,
@RequestParam(required = false) String mediaServerId,
@RequestParam(required = false) Boolean isEnd
){
if (ObjectUtils.isEmpty(mediaServerId)) {
mediaServerId = null;
}
return cloudRecordService.queryTask(app, stream, callId, taskId, mediaServerId, isEnd, request.getScheme());
}
@ResponseBody
@GetMapping("/collect/add")
@Operation(summary = "添加收藏")
@Parameter(name = "app", description = "应用名", required = false)
@Parameter(name = "stream", description = "流ID", required = false)
@Parameter(name = "mediaServerId", description = "流媒体ID", required = false)
@Parameter(name = "startTime", description = "鉴权ID", required = false)
@Parameter(name = "endTime", description = "鉴权ID", required = false)
@Parameter(name = "callId", description = "鉴权ID", required = false)
@Parameter(name = "recordId", description = "录像记录的ID用于精准收藏一个视频文件", required = false)
public int addCollect(
@RequestParam(required = false) String app,
@RequestParam(required = false) String stream,
@RequestParam(required = false) String mediaServerId,
@RequestParam(required = false) String startTime,
@RequestParam(required = false) String endTime,
@RequestParam(required = false) String callId,
@RequestParam(required = false) Integer recordId
){
logger.info("[云端录像] 添加收藏app={}stream={},mediaServerId={},startTime={},endTime={},callId={},recordId={}",
app, stream, mediaServerId, startTime, endTime, callId, recordId);
if (recordId != null) {
return cloudRecordService.changeCollectById(recordId, true);
}else {
return cloudRecordService.changeCollect(true, app, stream, mediaServerId, startTime, endTime, callId);
}
}
@ResponseBody
@GetMapping("/collect/delete")
@Operation(summary = "移除收藏")
@Parameter(name = "app", description = "应用名", required = false)
@Parameter(name = "stream", description = "流ID", required = false)
@Parameter(name = "mediaServerId", description = "流媒体ID", required = false)
@Parameter(name = "startTime", description = "鉴权ID", required = false)
@Parameter(name = "endTime", description = "鉴权ID", required = false)
@Parameter(name = "callId", description = "鉴权ID", required = false)
@Parameter(name = "recordId", description = "录像记录的ID用于精准精准移除一个视频文件的收藏", required = false)
public int deleteCollect(
@RequestParam(required = false) String app,
@RequestParam(required = false) String stream,
@RequestParam(required = false) String mediaServerId,
@RequestParam(required = false) String startTime,
@RequestParam(required = false) String endTime,
@RequestParam(required = false) String callId,
@RequestParam(required = false) Integer recordId
){
logger.info("[云端录像] 移除收藏app={}stream={},mediaServerId={},startTime={},endTime={},callId={},recordId={}",
app, stream, mediaServerId, startTime, endTime, callId, recordId);
if (recordId != null) {
return cloudRecordService.changeCollectById(recordId, false);
}else {
return cloudRecordService.changeCollect(false, app, stream, mediaServerId, startTime, endTime, callId);
}
}
@ResponseBody
@GetMapping("/play/path")
@Operation(summary = "获取播放地址")
@Parameter(name = "recordId", description = "录像记录的ID", required = true)
public DownloadFileInfo getPlayUrlPath(
@RequestParam(required = true) Integer recordId
){
return cloudRecordService.getPlayUrlPath(recordId);
}
}

View File

@ -86,7 +86,7 @@ public class GbStreamController {
@ResponseBody
public void del(@RequestBody GbStreamParam gbStreamParam){
if (gbStreamParam.getGbStreams() == null || gbStreamParam.getGbStreams().size() == 0) {
if (gbStreamParam.getGbStreams() == null || gbStreamParam.getGbStreams().isEmpty()) {
if (gbStreamParam.isAll()) {
gbStreamService.delAllPlatformInfo(gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId());
}
@ -105,7 +105,7 @@ public class GbStreamController {
@PostMapping(value = "/add")
@ResponseBody
public void add(@RequestBody GbStreamParam gbStreamParam){
if (gbStreamParam.getGbStreams() == null || gbStreamParam.getGbStreams().size() == 0) {
if (gbStreamParam.getGbStreams() == null || gbStreamParam.getGbStreams().isEmpty()) {
if (gbStreamParam.isAll()) {
List<GbStream> allGBChannels = gbStreamService.getAllGBChannels(gbStreamParam.getPlatformId());
gbStreamService.addPlatformInfo(allGBChannels, gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId());

View File

@ -1,5 +1,7 @@
package com.genersoft.iot.vmp.vmanager.gb28181.record;
import com.genersoft.iot.vmp.common.InviteInfo;
import com.genersoft.iot.vmp.common.InviteSessionType;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
@ -11,7 +13,9 @@ 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.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.service.IDeviceService;
import com.genersoft.iot.vmp.service.IInviteStreamService;
import com.genersoft.iot.vmp.service.IPlayService;
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.utils.DateUtil;
@ -26,6 +30,7 @@ import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@ -58,6 +63,9 @@ public class GBRecordController {
@Autowired
private IPlayService playService;
@Autowired
private IInviteStreamService inviteStreamService;
@Autowired
private IDeviceService deviceService;

View File

@ -1,51 +0,0 @@
//package com.genersoft.iot.vmp.vmanager.record;
//
//import com.alibaba.fastjson2.JSONObject;
//import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
//import com.genersoft.iot.vmp.service.IRecordInfoServer;
//import com.genersoft.iot.vmp.storager.dao.dto.RecordInfo;
//import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
//import com.github.pagehelper.PageInfo;
//import io.swagger.annotations.Api;
//import io.swagger.annotations.ApiImplicitParam;
//import io.swagger.annotations.ApiImplicitParams;
//import io.swagger.annotations.ApiOperation;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.web.bind.annotation.*;
//
//@Tag(name = "云端录像")
//
//@RestController
//@RequestMapping("/api/record")
//public class RecordController {
//
// @Autowired
// private IRecordInfoServer recordInfoServer;
//
// //@ApiOperation("录像列表查询")
// @ApiImplicitParams({
// @ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class),
// @ApiImplicitParam(name="count", value = "每页查询数量", required = true, dataTypeClass = Integer.class),
// @ApiImplicitParam(name="query", value = "查询内容", dataTypeClass = String.class),
// })
// @GetMapping(value = "/app/list")
// @ResponseBody
// public Object list(@RequestParam(required = false)Integer page,
// @RequestParam(required = false)Integer count ){
//
// PageInfo<RecordInfo> recordList = recordInfoServer.getRecordList(page - 1, page - 1 + count);
// return recordList;
// }
//
// //@ApiOperation("获取录像详情")
// @ApiImplicitParams({
// @ApiImplicitParam(name="recordInfo", value = "录像记录", required = true, dataTypeClass = RecordInfo.class)
// })
// @GetMapping(value = "/detail")
// @ResponseBody
// public JSONObject list(RecordInfo recordInfo, String time ){
//
//
// return null;
// }
//}

View File

@ -68,6 +68,7 @@ public class ApiDeviceController {
// if (logger.isDebugEnabled()) {
// logger.debug("查询所有视频设备API调用");
// }
JSONObject result = new JSONObject();
List<Device> devices;
if (start == null || limit ==null) {
@ -80,7 +81,7 @@ public class ApiDeviceController {
}
JSONArray deviceJSONList = new JSONArray();
for (Device device : devices) {
devices.stream().forEach(device -> {
JSONObject deviceJsonObject = new JSONObject();
deviceJsonObject.put("ID", device.getDeviceId());
deviceJsonObject.put("Name", device.getName());
@ -99,7 +100,7 @@ public class ApiDeviceController {
deviceJsonObject.put("UpdatedAt", "");
deviceJsonObject.put("CreatedAt", "");
deviceJSONList.add(deviceJsonObject);
}
});
result.put("DeviceList",deviceJSONList);
return result;
}
@ -114,7 +115,6 @@ public class ApiDeviceController {
@RequestParam(required = false)String q,
@RequestParam(required = false)Boolean online ){
JSONObject result = new JSONObject();
List<DeviceChannelExtend> deviceChannels;
List<String> channelIds = null;
@ -127,13 +127,19 @@ public class ApiDeviceController {
deviceChannels = allDeviceChannelList;
result.put("ChannelCount", deviceChannels.size());
}else {
deviceChannels = storager.queryChannelsByDeviceIdWithStartAndLimit(serial,channelIds, null, null, online,start, limit);
int total = allDeviceChannelList.size();
result.put("ChannelCount", total);
if (start > allDeviceChannelList.size()) {
deviceChannels = new ArrayList<>();
}else {
if (start + limit < allDeviceChannelList.size()) {
deviceChannels = allDeviceChannelList.subList(start, start + limit);
}else {
deviceChannels = allDeviceChannelList.subList(start, allDeviceChannelList.size());
}
}
result.put("ChannelCount", allDeviceChannelList.size());
}
JSONArray channleJSONList = new JSONArray();
for (DeviceChannelExtend deviceChannelExtend : deviceChannels) {
deviceChannels.stream().forEach(deviceChannelExtend -> {
JSONObject deviceJOSNChannel = new JSONObject();
deviceJOSNChannel.put("ID", deviceChannelExtend.getChannelId());
deviceJOSNChannel.put("DeviceID", deviceChannelExtend.getDeviceId());
@ -166,7 +172,7 @@ public class ApiDeviceController {
deviceJOSNChannel.put("StreamID", deviceChannelExtend.getStreamId()); // StreamID 直播流ID, 有值表示正在直播
deviceJOSNChannel.put("NumOutputs ", -1); // 直播在线人数
channleJSONList.add(deviceJOSNChannel);
}
});
result.put("ChannelList", channleJSONList);
return result;
}

View File

@ -92,7 +92,7 @@ public class ApiStreamController {
result.put("error","device[ " + serial + " ]未找到");
resultDeferredResult.setResult(result);
return resultDeferredResult;
}else if (device.isOnLine()) {
}else if (!device.isOnLine()) {
JSONObject result = new JSONObject();
result.put("error","device[ " + code + " ]offline");
resultDeferredResult.setResult(result);

View File

@ -34,18 +34,42 @@ spring:
poolMaxWait: 5
# [必选] jdbc数据库配置
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
username: root
password: root123
hikari:
connection-timeout: 20000 # 是客户端等待连接池连接的最大毫秒数
initialSize: 10 # 连接池初始化连接数
maximum-pool-size: 200 # 连接池最大连接数
minimum-idle: 5 # 连接池最小空闲连接数
idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位)
max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位)
# kingbase配置
# type: com.zaxxer.hikari.HikariDataSource
# driver-class-name: com.kingbase8.Driver
# url: jdbc:kingbase8://192.168.1.55:54321/wvp?useUnicode=true&characterEncoding=utf8
# username: system
# password: system
# postgresql配置
# type: com.zaxxer.hikari.HikariDataSource
# driver-class-name: org.postgresql.Driver
# url: jdbc:postgresql://192.168.1.242:3306/242wvp
# username: root
# password: SYceshizu1234
# mysql配置
dynamic:
primary: master
datasource:
master:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
username: root
password: root123
hikari:
connection-timeout: 20000 # 是客户端等待连接池连接的最大毫秒数
initialSize: 50 # 连接池初始化连接数
maximum-pool-size: 200 # 连接池最大连接数
minimum-idle: 10 # 连接池最小空闲连接数
idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位)
max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位)
share:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/wvp269_1?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true
username: root
password: 12345678
# 修改分页插件为 postgresql 数据库类型为mysql不需要
@ -139,6 +163,10 @@ media:
auto-config: true
# [可选] zlm服务器的hook.admin_params=secret
secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
# 录像路径
record-path: ./www/record
# 录像保存时长
record-day: 7
# 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分 点播超时建议使用多端口测试
rtp:
# [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输
@ -182,7 +210,7 @@ user-settings:
# 使用推流状态作为推流通道状态
use-pushing-as-status: true
# 使用来源请求ip作为streamIp,当且仅当你只有zlm节点它与wvp在一起的情况下开启
use-source-ip-as-stream-ip: true
use-source-ip-as-stream-ip: false
# 国标点播 按需拉流, true有人观看拉流无人观看释放 false拉起后不自动释放
stream-on-demand: true
# 推流鉴权, 默认开启

View File

@ -24,18 +24,22 @@ spring:
timeout: 10000
# mysql数据源
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
username: root
password: root
hikari:
connection-timeout: 20000 # 是客户端等待连接池连接的最大毫秒数
initialSize: 10 # 连接池初始化连接数
maximum-pool-size: 200 # 连接池最大连接数
minimum-idle: 5 # 连接池最小空闲连接数
idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位)
max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位)
dynamic:
primary: master
datasource:
master:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
username: root
password: root123
hikari:
connection-timeout: 20000 # 是客户端等待连接池连接的最大毫秒数
initialSize: 50 # 连接池初始化连接数
maximum-pool-size: 200 # 连接池最大连接数
minimum-idle: 10 # 连接池最小空闲连接数
idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位)
max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位)
#[可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口
server:
port: 18978

View File

@ -18,13 +18,22 @@ spring:
timeout: 10000
# [必选] jdbc数据库配置
datasource:
# 使用mysql 打开23-28行注释 删除29-36行
name: wvp
url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&allowMultiQueries=true&useSSL=false&allowMultiQueries=true
username: root
password: root
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
dynamic:
primary: master
datasource:
master:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
username: root
password: root123
hikari:
connection-timeout: 20000 # 是客户端等待连接池连接的最大毫秒数
initialSize: 50 # 连接池初始化连接数
maximum-pool-size: 200 # 连接池最大连接数
minimum-idle: 10 # 连接池最小空闲连接数
idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位)
max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位)
# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口
server:

View File

@ -10,7 +10,6 @@ const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
@ -31,9 +30,8 @@ const devWebpackConfig = merge(baseWebpackConfig, {
hot: true,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true,
host: HOST || config.dev.host,
// host:'127.0.0.1',
port: PORT || config.dev.port,
host: config.dev.host,
port: config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }

26485
web_src/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,207 +1,281 @@
<template>
<div id="app" style="width: 100%">
<div id="app" style="width: 100%">
<div class="page-header">
<div class="page-title">
<el-page-header v-if="recordDetail" @back="backToList" content="云端录像"></el-page-header>
<div v-if="!recordDetail">云端录像</div>
<div >云端录像</div>
</div>
<div class="page-header-btn">
搜索:
<el-input @input="getMediaServerList" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
prefix-icon="el-icon-search" v-model="search" clearable></el-input>
开始时间:
<el-date-picker
v-model="startTime"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
@change="getMediaServerList"
placeholder="选择日期时间">
</el-date-picker>
结束时间:
<el-date-picker
v-model="endTime"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
@change="getMediaServerList"
placeholder="选择日期时间">
</el-date-picker>
节点选择:
<el-select size="mini" @change="chooseMediaChange" style="width: 16rem; margin-right: 1rem;" v-model="mediaServerId" placeholder="请选择" :disabled="recordDetail">
<el-select size="mini" @change="getMediaServerList" style="width: 16rem; margin-right: 1rem;"
v-model="mediaServerId" placeholder="请选择" >
<el-option label="全部" value=""></el-option>
<el-option
v-for="item in mediaServerList"
:key="item.id"
:label="item.id"
:value="item.id">
v-for="item in mediaServerList"
:key="item.id"
:label="item.id"
:value="item.id">
</el-option>
</el-select>
<el-button v-if="!recordDetail" icon="el-icon-refresh-right" circle size="mini" :loading="loading" @click="getRecordList()"></el-button>
<!-- <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteRecord()">批量删除</el-button>-->
<el-button icon="el-icon-refresh-right" circle size="mini" :loading="loading"
@click="getRecordList()"></el-button>
</div>
</div>
<div v-if="!recordDetail">
<!--设备列表-->
<el-table :data="recordList" style="width: 100%" :height="winHeight">
<el-table-column prop="app" label="应用名" >
</el-table-column>
<el-table-column prop="stream" label="流ID" >
</el-table-column>
<el-table-column prop="time" label="时间" >
</el-table-column>
<el-table-column label="操作" width="360" fixed="right">
<template slot-scope="scope">
<el-button size="medium" icon="el-icon-folder-opened" type="text" @click="showRecordDetail(scope.row)">查看</el-button>
<!-- <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteRecord(scope.row)">删除</el-button>-->
</template>
</el-table-column>
</el-table>
<el-pagination
style="float: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[15, 25, 35, 50]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
</div>
<!--设备列表-->
<el-table :data="recordList" style="width: 100%" :height="winHeight">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column prop="app" label="应用名">
</el-table-column>
<el-table-column prop="stream" label="流ID" width="380">
</el-table-column>
<el-table-column label="开始时间">
<template slot-scope="scope">
{{formatTimeStamp(scope.row.startTime)}}
</template>
</el-table-column>
<el-table-column label="结束时间">
<template slot-scope="scope">
{{formatTimeStamp(scope.row.endTime)}}
</template>
</el-table-column>
<el-table-column label="时长">
<template slot-scope="scope">
<el-tag>{{formatTime(scope.row.timeLen)}}</el-tag>
</template>
</el-table-column>
<el-table-column prop="fileName" label="文件名称">
</el-table-column>
<el-table-column prop="mediaServerId" label="流媒体">
</el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<template slot-scope="scope">
<el-button size="medium" icon="el-icon-video-play" type="text" @click="play(scope.row)">播放
</el-button>
<!-- <el-button size="medium" icon="el-icon-delete" type="text" style="color: #f56c6c"-->
<!-- @click="deleteRecord(scope.row)">删除-->
<!-- </el-button>-->
</template>
</el-table-column>
</el-table>
<el-pagination
style="float: right"
@size-change="handleSizeChange"
@current-change="currentChange"
:current-page="currentPage"
:page-size="count"
:page-sizes="[15, 25, 35, 50]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
<el-dialog
:title="playerTitle"
:visible.sync="showPlayer"
width="50%">
<easyPlayer ref="recordVideoPlayer" :videoUrl="videoUrl" :height="false" ></easyPlayer>
</el-dialog>
</div>
</template>
<script>
import uiHeader from '../layout/UiHeader.vue'
import MediaServer from './service/MediaServer'
export default {
name: 'app',
components: {
uiHeader
},
data() {
return {
mediaServerList: [], //
mediaServerId: null, //
mediaServerPath: null, //
recordList: [], //
chooseRecord: null, //
import uiHeader from '../layout/UiHeader.vue'
import MediaServer from './service/MediaServer'
import easyPlayer from './common/easyPlayer.vue'
import moment from 'moment'
import axios from "axios";
updateLooper: 0, //
winHeight: window.innerHeight - 250,
currentPage:1,
count:15,
total:0,
loading: false,
mediaServerObj : new MediaServer(),
recordDetail: false
export default {
name: 'app',
components: {
uiHeader,easyPlayer
},
data() {
return {
search: '',
startTime: '',
endTime: '',
showPlayer: false,
playerTitle: '',
videoUrl: '',
playerStyle: {
"margin": "auto",
"margin-bottom": "20px",
"width": window.innerWidth/2 + "px",
"height": this.winHeight/2 + "px",
},
mediaServerList: [], //
mediaServerId: "", //
mediaServerPath: null, //
recordList: [], //
chooseRecord: null, //
};
},
computed: {
updateLooper: 0, //
winHeight: window.innerHeight - 250,
currentPage: 1,
count: 15,
total: 0,
loading: false,
mediaServerObj: new MediaServer(),
},
mounted() {
this.initData();
},
destroyed() {
// this.$destroy('videojs');
},
methods: {
initData: function() {
//
this.getMediaServerList();
// this.getRecordList();
},
currentChange: function(val){
this.currentPage = val;
this.getRecordList();
},
handleSizeChange: function(val){
this.count = val;
this.getRecordList();
},
getMediaServerList: function (){
let that = this;
that.mediaServerObj.getOnlineMediaServerList((data)=>{
that.mediaServerList = data.data;
if (that.mediaServerList.length > 0) {
that.mediaServerId = that.mediaServerList[0].id
that.setMediaServerPath(that.mediaServerId);
that.getRecordList();
}
})
},
setMediaServerPath: function (serverId) {
let that = this;
let i;
for (i = 0; i < that.mediaServerList.length; i++) {
if (serverId === that.mediaServerList[i].id) {
break;
}
};
},
computed: {},
mounted() {
this.initData();
},
destroyed() {
this.$destroy('recordVideoPlayer');
},
methods: {
initData: function () {
//
this.getMediaServerList();
this.getRecordList();
},
currentChange: function (val) {
this.currentPage = val;
this.getRecordList();
},
handleSizeChange: function (val) {
this.count = val;
this.getRecordList();
},
getMediaServerList: function () {
let that = this;
that.mediaServerObj.getOnlineMediaServerList((data) => {
that.mediaServerList = data.data;
})
},
setMediaServerPath: function (serverId) {
let that = this;
let i;
for (i = 0; i < that.mediaServerList.length; i++) {
if (serverId === that.mediaServerList[i].id) {
break;
}
let port = that.mediaServerList[i].httpPort;
if (location.protocol === "https:" && that.mediaServerList[i].httpSSlPort) {
port = that.mediaServerList[i].httpSSlPort
}
let port = that.mediaServerList[i].httpPort;
if (location.protocol === "https:" && that.mediaServerList[i].httpSSlPort) {
port = that.mediaServerList[i].httpSSlPort
}
that.mediaServerPath = location.protocol + "//" + that.mediaServerList[i].streamIp + ":" + port
console.log(that.mediaServerPath)
},
getRecordList: function () {
this.$axios({
method: 'get',
url: `/api/cloud/record/list`,
params: {
app: '',
stream: '',
query: this.search,
startTime: this.startTime,
endTime: this.endTime,
mediaServerId: this.mediaServerId,
page: this.currentPage,
count: this.count
}
that.mediaServerPath = location.protocol + "//" + that.mediaServerList[i].streamIp + ":" + port
console.log(that.mediaServerPath)
},
getRecordList: function (){
let that = this;
this.$axios({
method: 'get',
url:`/record_proxy/${that.mediaServerId}/api/record/list`,
params: {
page: that.currentPage,
count: that.count
}).then((res) => {
console.log(res)
if (res.data.code === 0) {
this.total = res.data.data.total;
this.recordList = res.data.data.list;
}
this.loading = false;
}).catch((error) => {
console.log(error);
this.loading = false;
});
},
play(row) {
console.log(row)
this.chooseRecord = row;
this.showPlayer = true;
this.$axios({
method: 'get',
url: `/api/cloud/record/play/path`,
params: {
recordId: row.id,
}
}).then((res) => {
console.log(res)
if (res.data.code === 0) {
if (location.protocol === "https:") {
this.videoUrl = res.data.data.httpsPath;
}else {
this.videoUrl = res.data.data.httpPath;
}
}).then(function (res) {
console.log(res)
if (res.data.code === 0) {
that.total = res.data.data.total;
that.recordList = res.data.data.list;
console.log(222 )
console.log(this.videoUrl )
}
}).catch((error) => {
console.log(error);
});
},
getFileBasePath(item) {
let basePath = ""
if (axios.defaults.baseURL.startsWith("http")) {
basePath = `${axios.defaults.baseURL}/record_proxy/${item.mediaServerId}`
}else {
basePath = `${window.location.origin}${axios.defaults.baseURL}/record_proxy/${item.mediaServerId}`
}
that.loading = false;
}).catch(function (error) {
console.log(error);
that.loading = false;
});
},
backToList(){
this.recordDetail= false;
},
chooseMediaChange(val){
console.log(val)
this.total = 0;
this.recordList = [];
this.setMediaServerPath(val);
this.getRecordList();
},
showRecordDetail(row){
this.recordDetail = true;
this.chooseRecord = row;
//
// this.$axios({
// method: 'delete',
// url:`/record_proxy/api/record/delete`,
// params: {
// page: this.currentPage,
// count: this.count
// }
// }).then((res) => {
// console.log(res)
// this.total = res.data.data.total;
// this.recordList = res.data.data.list;
// }).catch(function (error) {
// console.log(error);
// });
this.$router.push(`/cloudRecordDetail/${row.app}/${row.stream}`)
},
deleteRecord(){
// TODO
let that = this;
this.$axios({
method: 'delete',
url:`/record_proxy/api/record/delete`,
params: {
page: that.currentPage,
count: that.count
}
}).then(function (res) {
console.log(res)
if (res.data.code === 0) {
that.total = res.data.data.total;
that.recordList = res.data.data.list;
}
}).catch(function (error) {
console.log(error);
});
return basePath;
},
deleteRecord() {
// TODO
let that = this;
this.$axios({
method: 'delete',
url: `/record_proxy/api/record/delete`,
params: {
page: that.currentPage,
count: that.count
}
}).then(function (res) {
console.log(res)
if (res.data.code === 0) {
that.total = res.data.data.total;
that.recordList = res.data.data.list;
}
}).catch(function (error) {
console.log(error);
});
},
formatTime(time) {
const h = parseInt(time / 3600)
const minute = parseInt(time / 60 % 60)
const second = Math.ceil(time % 60)
return (h > 0 ? h + `小时` : '') + (minute > 0 ? minute + '分' : '') + second + '秒'
},
formatTimeStamp(time) {
return moment.unix(time).format('yyyy-MM-DD HH:mm:ss')
}
}
};
}
};
</script>
<style>

View File

@ -37,13 +37,13 @@
<div class="record-list-box" :style="recordListStyle">
<ul v-if="detailFiles.length >0" class="infinite-list record-list" v-infinite-scroll="infiniteScroll" >
<li v-for="(item,index) in detailFiles" :key="index" class="infinite-list-item record-list-item" >
<el-tag v-if="choosedFile !== item.filename" @click="chooseFile(item)">
<el-tag v-if="choosedFile !== item.fileName" @click="chooseFile(item)">
<i class="el-icon-video-camera" ></i>
{{ getFileShowName(item.fileName) }}
{{ getFileShowName(item) }}
</el-tag>
<el-tag type="danger" v-if="choosedFile === item.filename">
<el-tag type="danger" v-if="choosedFile === item.fileName">
<i class="el-icon-video-camera" ></i>
{{ getFileShowName(item.fileName) }}
{{ getFileShowName(item) }}
</el-tag>
<a class="el-icon-download" style="color: #409EFF;font-weight: 600;margin-left: 10px;"
:href="`${getFileBasePath(item)}/download.html?url=download/${app}/${stream}/${chooseDate}/${item.fileName}`"
@ -135,7 +135,7 @@
<script>
// TODO
import uiHeader from '../layout/UiHeader.vue'
import player from './dialog/easyPlayer.vue'
import player from './common/easyPlayer.vue'
import moment from 'moment'
import axios from "axios";
export default {
@ -319,7 +319,7 @@
this.choosedFile = "";
}else {
this.choosedFile = file.fileName;
this.videoUrl = `${this.getFileBasePath(file)}/download/${this.app}/${this.stream}/${this.chooseDate}/${this.choosedFile}`
this.videoUrl = `${this.getFileBasePath(file)}/download/${this.app}/${this.stream}/${this.chooseDate}/${file.fileName}`
console.log(this.videoUrl)
}
@ -327,9 +327,8 @@
backToList() {
this.$router.back()
},
getFileShowName(name) {
return name.substring(0, 2) + ":" + name.substring(2, 4) + ":" + name.substring(4, 6) + "-" +
name.substring(7, 9) + ":" + name.substring(9, 11) + ":" + name.substring(11, 13)
getFileShowName(item) {
return moment.unix(item.startTime).format('HH:mm:ss') + "-" + moment.unix(item.endTime).format('HH:mm:ss')
},
chooseMediaChange() {
@ -376,13 +375,8 @@
},
getTimeForFile(file){
console.log(file)
let timeStr = file.fileName.substring(0, 17);
if(timeStr.indexOf("~") > 0){
timeStr = timeStr.replaceAll("-",":")
}
let timeArr = timeStr.split("-");
let starTime = new Date(this.chooseDate + " " + timeArr[0]);
let endTime = new Date(this.chooseDate + " " + timeArr[1]);
let starTime = new Date(file.startTime * 1000);
let endTime = new Date(file.endTime * 1000);
if(this.checkIsOver24h(starTime,endTime)){
endTime = new Date(this.chooseDate + " " + "23:59:59");
}
@ -486,12 +480,13 @@
let that = this;
this.$axios({
method: 'get',
url:`/record_proxy/${that.mediaServerId}/api/record/file/download/task/add`,
url:`/api/cloud/record/task/add`,
params: {
app: that.app,
stream: that.stream,
startTime: moment(this.taskTimeRange[0]).format('YYYY-MM-DD HH:mm:ss'),
endTime: moment(this.taskTimeRange[1]).format('YYYY-MM-DD HH:mm:ss'),
app: this.app,
stream: this.stream,
mediaServerId: this.mediaServerId,
startTime: moment(this.taskTimeRange[0]).format('YYYY-MM-DD HH:mm:ss'),
endTime: moment(this.taskTimeRange[1]).format('YYYY-MM-DD HH:mm:ss'),
}
}).then(function (res) {
if (res.data.code === 0 ) {
@ -511,8 +506,9 @@
let that = this;
this.$axios({
method: 'get',
url:`/record_proxy/${that.mediaServerId}/api/record/file/download/task/list`,
url:`/api/cloud/record/task/list`,
params: {
mediaServerId: this.mediaServerId,
isEnd: isEnd,
}
}).then(function (res) {

View File

@ -1,5 +1,5 @@
<template>
<div id="easyplayer"></div>
<div id="easyplayer" ></div>
</template>
<script>

View File

@ -1,6 +1,6 @@
<template>
<div ref="container" @dblclick="fullscreenSwich"
style="width:100%;height:100%;background-color: #000000;margin:0 auto;position: relative;">
style="width:100%;height:100%;min-height: 200px;background-color: #000000;margin:0 auto;position: relative;">
<div class="buttons-box" id="buttonsBox">
<div class="buttons-box-left">
<i v-if="!playing" class="iconfont icon-play jessibuca-btn" @click="playBtnClick"></i>
@ -80,9 +80,10 @@ export default {
height = clientHeight
width = (16 / 9) * height
}
dom.style.width = width + 'px';
dom.style.height = height + "px";
if (width > 0 && height > 0) {
dom.style.width = width + 'px';
dom.style.height = height + "px";
}
},
create() {
let options = {

View File

@ -20,7 +20,7 @@
<el-input v-model="form.name" clearable></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" v-model="form.password" clearable></el-input>
<el-input v-model="form.password" clearable></el-input>
</el-form-item>
<el-form-item label="收流IP" prop="sdpIp">
<el-input type="sdpIp" v-model="form.sdpIp" clearable></el-input>

View File

@ -6,8 +6,7 @@
<el-progress :percentage="percentage"></el-progress>
</el-col>
<el-col :span="6" >
<el-button icon="el-icon-download" v-if="percentage < 100" size="mini" title="点击下载可将以缓存部分下载到本地" @click="download()">停止缓存并下载</el-button>
<el-button icon="el-icon-download" v-if="downloadFile" size="mini" title="点击下载" @click="downloadFileClientEvent()">点击下载</el-button>
<el-button icon="el-icon-download" v-if="downloadFile" size="mini" title="点击下载" @click="downloadFileClientEvent()">下载</el-button>
</el-col>
</el-row>
</el-dialog>
@ -27,7 +26,7 @@ export default {
},
data() {
return {
title: "四倍速下载中...",
title: "下载中...",
deviceId: "",
channelId: "",
app: "",
@ -39,7 +38,6 @@ export default {
streamInfo: null,
taskId: null,
getProgressRun: false,
getProgressForFileRun: false,
timer: null,
downloadFile: null,
@ -62,7 +60,7 @@ export default {
return;
}
if (this.percentage == 100 ) {
this.getFileDownload();
return;
}
setTimeout( ()=>{
@ -75,7 +73,6 @@ export default {
method: 'get',
url: `/api/gb_record/download/progress/${this.deviceId}/${this.channelId}/${this.stream}`
}).then((res)=> {
console.log(res)
if (res.data.code === 0) {
this.streamInfo = res.data.data;
if (parseFloat(res.data.progress) == 1) {
@ -83,6 +80,15 @@ export default {
}else {
this.percentage = (parseFloat(res.data.data.progress)*100).toFixed(1);
}
if (this.streamInfo.downLoadFilePath) {
if (location.protocol === "https:") {
this.downloadFile = this.streamInfo.downLoadFilePath.httpsPath;
}else {
this.downloadFile = this.streamInfo.downLoadFilePath.httpPath;
}
this.getProgressRun = false;
this.downloadFileClientEvent()
}
if (callback)callback();
}else {
this.$message({
@ -108,24 +114,11 @@ export default {
}
this.showDialog=false;
this.getProgressRun = false;
this.getProgressForFileRun = false;
},
gbScale: function (scale){
this.scale = scale;
},
download: function (){
this.getProgressRun = false;
if (this.streamInfo != null ) {
if (this.streamInfo.progress < 1) {
//
this.stopDownloadRecord((res)=>{
this.getFileDownload()
})
}else {
this.getFileDownload()
}
}
},
stopDownloadRecord: function (callback) {
this.$axios({
method: 'get',
@ -134,74 +127,20 @@ export default {
if (callback) callback(res)
});
},
getFileDownload: function (){
this.$axios({
method: 'get',
url:`/record_proxy/${this.mediaServerId}/api/record/file/download/task/add`,
params: {
app: this.app,
stream: this.stream,
startTime: null,
endTime: null,
}
}).then((res) =>{
if (res.data.code === 0 ) {
//
this.title = "录像文件处理中..."
this.taskId = res.data.data;
this.percentage = 0.0;
this.getProgressForFileRun = true;
this.getProgressForFileTimer();
}
}).catch(function (error) {
console.log(error);
});
},
getProgressForFileTimer: function (){
if (!this.getProgressForFileRun || this.percentage == 100) {
return;
}
setTimeout( ()=>{
if (!this.showDialog) return;
this.getProgressForFile(this.getProgressForFileTimer)
}, 1000)
},
getProgressForFile: function (callback){
this.$axios({
method: 'get',
url:`/record_proxy/${this.mediaServerId}/api/record/file/download/task/list`,
params: {
app: this.app,
stream: this.stream,
taskId: this.taskId,
isEnd: true,
}
}).then((res) => {
console.log(res)
if (res.data.code === 0) {
if (res.data.data.length === 0){
this.percentage = 0
//
if (callback)callback()
return
}
// res.data.data
this.percentage = parseFloat(res.data.data[0].percentage)*100
if (res.data.data[0].percentage === '1') {
this.getProgressForFileRun = false;
this.downloadFile = res.data.data[0].downloadFile
this.title = "文件处理完成,点击按扭下载"
// window.open(res.data.data[0].downloadFile)
}else {
if (callback)callback()
}
}
}).catch(function (error) {
console.log(error);
});
},
downloadFileClientEvent: function (){
window.open(this.downloadFile )
// window.open(this.downloadFile )
let x = new XMLHttpRequest();
x.open("GET", this.downloadFile, true);
x.responseType = 'blob';
x.onload=(e)=> {
let url = window.URL.createObjectURL(x.response)
let a = document.createElement('a');
a.href = url
a.download = this.deviceId + "-" + this.channelId + ".mp4";
a.click()
}
x.send();
}
},
destroyed() {

0
web_src/static/js/jessibuca/decoder.wasm Normal file → Executable file
View File

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,63 @@
spring:
# REDIS数据库配置
redis:
# [可选] 超时时间
timeout: 10000
# 以下为单机配置
# [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1
host: 127.0.0.1
# [必须修改] 端口号
port: 6379
# [可选] 数据库 DB
database: 1
# [可选] 访问密码,若你的redis服务器没有设置密码就不需要用密码去连接
password: adminadmin123.
# 以下为集群配置
# cluster:
# nodes: 192.168.1.242:7001
# password: 4767cb971b40a1300fa09b7f87b09d1c
# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口
server:
port: 18081
# [可选] HTTPS配置 默认不开启
ssl:
# [可选] 是否开启HTTPS访问
enabled: false
# [可选] 证书文件路径放置在resource/目录下即可修改xxx为文件名
key-store: classpath:xxx.jks
# [可选] 证书密码
key-password: password
# [可选] 证书类型, 默认为jks根据实际修改
key-store-type: JKS
# [根据业务需求配置]
userSettings:
# [必选 ] 服务ID
id: 334533
# [可选 ] 录像下载合成临时文件保存时长, 不配置默认取值recordDay单位每天晚12点自动对过期文件执行清理
# recordTempDay: 7
# [必选 ] ffmpeg路径
ffmpeg: lib/ffmpeg
# [必选 ] ffprobe路径 一般安装ffmpeg就会自带 一般跟ffmpeg在同一目录,用于查询文件的信息
ffprobe: lib/ffprobe
# [可选 ] 限制 ffmpeg 合并文件使用的线程数间接限制cpu使用率 默认2 限制到50%
threads: 2
swagger-ui:
enabled: true
# [可选] 日志配置, 一般不需要改
logging:
file:
name: logs/wvp.log
max-history: 30
max-size: 10MB
total-size-cap: 300MB
level:
root: WARN
top:
panll:
assist: info
net:
bramp:
ffmpeg: error

166
打包/config/config.ini Executable file
View File

@ -0,0 +1,166 @@
; auto-generated by mINI class {
[api]
apiDebug=1
defaultSnap=./www/logo.png
secret=034523TF8yT83wh5Wvz73f7
snapRoot=./www/snap/
[cluster]
origin_url=
retry_count=3
timeout_sec=15
[ffmpeg]
bin=/usr/bin/ffmpeg
cmd=%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s
log=./ffmpeg/ffmpeg.log
restart_sec=0
snap=%s -i %s -y -f mjpeg -t 0.001 %s
[general]
check_nvidia_dev=1
enableVhost=0
enable_ffmpeg_log=0
flowThreshold=1024
maxStreamWaitMS=15000
mediaServerId=GQ3TF8yT83wh5Wvz
mergeWriteMS=0
resetWhenRePlay=1
streamNoneReaderDelayMS=15000
unready_frame_cache=100
wait_add_track_ms=3000
wait_track_ready_ms=10000
[hls]
broadcastRecordTs=0
deleteDelaySec=10
fileBufSize=65536
segDur=2
segKeep=0
segNum=3
segRetain=5
[hook]
admin_params=secret=034523TF8yT83wh5Wvz73f7
alive_interval=30.000000
enable=1
on_flow_report=
on_http_access=
on_play=http://192.168.1.3:18082/index/hook/on_play
on_publish=http://192.168.1.3:18082/index/hook/on_publish
on_record_mp4=
on_record_ts=
on_rtp_server_timeout=http://192.168.1.3:18082/index/hook/on_rtp_server_timeout
on_rtsp_auth=
on_rtsp_realm=
on_send_rtp_stopped=http://192.168.1.3:18082/index/hook/on_send_rtp_stopped
on_server_keepalive=http://192.168.1.3:18082/index/hook/on_server_keepalive
on_server_started=http://192.168.1.3:18082/index/hook/on_server_started
on_shell_login=
on_stream_changed=http://192.168.1.3:18082/index/hook/on_stream_changed
on_stream_none_reader=http://192.168.1.3:18082/index/hook/on_stream_none_reader
on_stream_not_found=http://192.168.1.3:18082/index/hook/on_stream_not_found
retry=1
retry_delay=3.000000
timeoutSec=20
[http]
charSet=utf-8
dirMenu=1
forbidCacheSuffix=
forwarded_ip_header=
keepAliveSecond=15
maxReqSize=40960
notFound=<html><head><title>404 Not Found</title></head><body bgcolor="white"><center><h1>您访问的资源不存在!</h1></center><hr><center>ZLMediaKit(git hash:f6cba98/2023-02-06T14:18:21+08:00,branch:master,build time:2023-02-07T10:51:47)</center></body></html>
port=6080
rootPath=./www
sendBufSize=65536
sslport=16080
virtualPath=
[multicast]
addrMax=239.255.255.255
addrMin=239.0.0.0
udpTTL=64
[protocol]
add_mute_audio=1
continue_push_ms=3000
enable_audio=1
enable_fmp4=1
enable_hls=1
enable_mp4=0
enable_rtmp=1
enable_rtsp=1
enable_ts=1
fmp4_demand=0
hls_demand=0
hls_save_path=./www
modify_stamp=0
mp4_as_player=0
mp4_max_second=3600
mp4_save_path=./www
rtmp_demand=0
rtsp_demand=0
ts_demand=0
[record]
appName=record
fastStart=0
fileBufSize=65536
fileRepeat=0
sampleMS=500
[rtc]
externIP=192.168.1.3
port=8000
preferredCodecA=PCMA,PCMU,opus,mpeg4-generic
preferredCodecV=H264,H265,AV1,VP9,VP8
rembBitRate=0
tcpPort=8000
timeoutSec=15
[rtmp]
handshakeSecond=15
keepAliveSecond=15
modifyStamp=0
port=1935
sslport=19350
[rtp]
audioMtuSize=600
lowLatency=0
rtpMaxSize=10
videoMtuSize=1400
[rtp_proxy]
dumpDir=
h264_pt=98
h265_pt=99
opus_pt=100
port=10000
port_range=40000-40500
ps_pt=96
timeoutSec=15
[rtsp]
authBasic=0
directProxy=1
handshakeSecond=15
keepAliveSecond=15
lowLatency=0
port=10554
sslport=
[shell]
maxReqSize=1024
port=9000
[srt]
latencyMul=4
pktBufSize=8192
port=9000
timeoutSec=5
; } ---

120
打包/config/wvp-application.yml Executable file
View File

@ -0,0 +1,120 @@
spring:
# [可选]上传文件大小限制
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
# REDIS数据库配置
redis:
# [可选] 超时时间
timeout: 10000
# 以下为单机配置
# [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1
host: 127.0.0.1
# # [必须修改] 端口号
port: 6379
# [可选] 数据库 DB
database: 1
# [可选] 访问密码,若你的redis服务器没有设置密码就不需要用密码去连接
password: adminadmin123.
# 以下为集群配置
# cluster:
# nodes: 192.168.1.66:9001,192.168.1.66:9002,192.168.1.66:9003,192.168.1.66:9004,192.168.1.66:9005,192.168.1.66:9006
# password: adminadmin123.
# [可选] jdbc数据库配置, 项目使用sqlite作为数据库一般不需要配置
# mysql数据源
datasource:
# kingbase配置
# type: com.zaxxer.hikari.HikariDataSource
# driver-class-name: com.kingbase8.Driver
# url: jdbc:kingbase8://192.168.1.55:54321/wvp?useUnicode=true&characterEncoding=utf8
# username: system
# password: system
# postgresql配置
# type: com.zaxxer.hikari.HikariDataSource
# driver-class-name: org.postgresql.Driver
# url: jdbc:postgresql://192.168.1.242:3306/242wvp
# username: root
# password: SYceshizu1234
# mysql配置
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.1.242:3306/242wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true
username: root
password: SYceshizu1234
# kingbase 和 postgresql需要开启这个配置
#pagehelper:
# helper-dialect: postgresql
#[可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口
server:
port: 18080
# 作为28181服务器的配置
sip:
# [必须修改] 本机的IP
ip: 192.168.1.3
# [可选] 28181服务监听的端口
port: 15060
# 根据国标6.1.2中规定domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码由省级、市级、区级、基层编号组成参照GB/T 2260-2007
# 后两位为行业编码定义参照附录D.3
# 3701020049标识山东济南历下区 信息行业接入
# [可选]
domain: 3402000001
# [可选]
id: 34020000013000000001
# [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验
password: 12345678
#zlm 默认服务器配置
media:
# [必须修改] zlm服务器唯一id用于触发hook时区别是哪台服务器,general.mediaServerId
id: GQ3TF8yT83wh5Wvz
# [必须修改] zlm服务器的内网IP
ip: 192.168.1.3
# [必须修改] zlm服务器的http.port
http-port: 6080
# [可选] zlm服务器的hook.admin_params=secret
secret: 034523TF8yT83wh5Wvz73f7
# 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分 点播超时建议使用多端口测试
rtp:
# [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输
enable: true
# [可选] 在此范围内选择端口用于媒体流传输,
port-range: 30000,30500 # 端口范围
# [可选] 国标级联在此范围内选择端口发送媒体流,请不要与收流端口范围重合
send-port-range: 50502,50506 # 端口范围
# 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载 0 表示不使用
record-assist-port: 18089
# 录像路径
record-path: ./www/record
# 录像保存时长
record-day: 7
# [可选] 日志配置, 一般不需要改
logging:
config: classpath:logback-spring-local.xml
# [根据业务需求配置]
user-settings:
server-id: 741266
auto-apply-play: true
interface-authentication: true
interface-authentication-excludes:
- /api/v1/**
- /api/media/stream_info_by_app_and_stream
- /api/v1/control/ptz
- /api/cloud/record/*/**
# 推流直播是否录制
record-push-live: true
# 国标是否录制
record-sip: false
# 使用推流状态作为推流通道状态
use-pushing-as-status: false
# 设备上线时是否自动同步通道
sync-channel-on-device-online: false
# 消息通道功能-缺少国标ID是否给所有上级发送消息
send-to-platforms-when-id-lost: false

View File

@ -0,0 +1,323 @@
/*建表*/
create table wvp_device (
id serial primary key ,
device_id character varying(50) not null ,
name character varying(255),
manufacturer character varying(255),
model character varying(255),
firmware character varying(255),
transport character varying(50),
stream_mode character varying(50),
on_line bool default false,
register_time character varying(50),
keepalive_time character varying(50),
ip character varying(50),
create_time character varying(50),
update_time character varying(50),
port integer,
expires integer,
subscribe_cycle_for_catalog integer DEFAULT 0,
subscribe_cycle_for_mobile_position integer DEFAULT 0,
mobile_position_submission_interval integer DEFAULT 5,
subscribe_cycle_for_alarm integer DEFAULT 0,
host_address character varying(50),
charset character varying(50),
ssrc_check bool default false,
geo_coord_sys character varying(50),
media_server_id character varying(50),
custom_name character varying(255),
sdp_ip character varying(50),
local_ip character varying(50),
password character varying(255),
as_message_channel bool default false,
keepalive_interval_time integer,
switch_primary_sub_stream bool default false,
broadcast_push_after_ack bool default false,
constraint uk_device_device unique (device_id)
);
create table wvp_device_alarm (
id serial primary key ,
device_id character varying(50) not null,
channel_id character varying(50) not null,
alarm_priority character varying(50),
alarm_method character varying(50),
alarm_time character varying(50),
alarm_description character varying(255),
longitude double precision,
latitude double precision,
alarm_type character varying(50),
create_time character varying(50) not null
);
create table wvp_device_channel (
id serial primary key ,
channel_id character varying(50) not null,
name character varying(255),
custom_name character varying(255),
manufacture character varying(50),
model character varying(50),
owner character varying(50),
civil_code character varying(50),
block character varying(50),
address character varying(50),
parent_id character varying(50),
safety_way integer,
register_way integer,
cert_num character varying(50),
certifiable integer,
err_code integer,
end_time character varying(50),
secrecy character varying(50),
ip_address character varying(50),
port integer,
password character varying(255),
ptz_type integer,
custom_ptz_type integer,
status bool default false,
longitude double precision,
custom_longitude double precision,
latitude double precision,
custom_latitude double precision,
stream_id character varying(255),
device_id character varying(50) not null,
parental character varying(50),
has_audio bool default false,
create_time character varying(50) not null,
update_time character varying(50) not null,
sub_count integer,
longitude_gcj02 double precision,
latitude_gcj02 double precision,
longitude_wgs84 double precision,
latitude_wgs84 double precision,
business_group_id character varying(50),
gps_time character varying(50),
constraint uk_wvp_device_channel_unique_device_channel unique (device_id, channel_id)
);
create table wvp_device_mobile_position (
id serial primary key,
device_id character varying(50) not null,
channel_id character varying(50) not null,
device_name character varying(255),
time character varying(50),
longitude double precision,
latitude double precision,
altitude double precision,
speed double precision,
direction double precision,
report_source character varying(50),
longitude_gcj02 double precision,
latitude_gcj02 double precision,
longitude_wgs84 double precision,
latitude_wgs84 double precision,
create_time character varying(50)
);
create table wvp_gb_stream (
gb_stream_id serial primary key,
app character varying(255) not null,
stream character varying(255) not null,
gb_id character varying(50) not null,
name character varying(255),
longitude double precision,
latitude double precision,
stream_type character varying(50),
media_server_id character varying(50),
create_time character varying(50),
constraint uk_gb_stream_unique_gb_id unique (gb_id),
constraint uk_gb_stream_unique_app_stream unique (app, stream)
);
create table wvp_log (
id serial primary key ,
name character varying(50),
type character varying(50),
uri character varying(200),
address character varying(50),
result character varying(50),
timing bigint,
username character varying(50),
create_time character varying(50)
);
create table wvp_media_server (
id character varying(255) primary key ,
ip character varying(50),
hook_ip character varying(50),
sdp_ip character varying(50),
stream_ip character varying(50),
http_port integer,
http_ssl_port integer,
rtmp_port integer,
rtmp_ssl_port integer,
rtp_proxy_port integer,
rtsp_port integer,
rtsp_ssl_port integer,
auto_config bool default false,
secret character varying(50),
rtp_enable bool default false,
rtp_port_range character varying(50),
send_rtp_port_range character varying(50),
record_assist_port integer,
default_server bool default false,
create_time character varying(50),
update_time character varying(50),
hook_alive_interval integer,
record_path character varying(255),
record_day integer default 7,
constraint uk_media_server_unique_ip_http_port unique (ip, http_port)
);
create table wvp_platform (
id serial primary key ,
enable bool default false,
name character varying(255),
server_gb_id character varying(50),
server_gb_domain character varying(50),
server_ip character varying(50),
server_port integer,
device_gb_id character varying(50),
device_ip character varying(50),
device_port character varying(50),
username character varying(255),
password character varying(50),
expires character varying(50),
keep_timeout character varying(50),
transport character varying(50),
character_set character varying(50),
catalog_id character varying(50),
ptz bool default false,
rtcp bool default false,
status bool default false,
start_offline_push bool default false,
administrative_division character varying(50),
catalog_group integer,
create_time character varying(50),
update_time character varying(50),
as_message_channel bool default false,
auto_push_channel bool default false,
constraint uk_platform_unique_server_gb_id unique (server_gb_id)
);
create table wvp_platform_catalog (
id character varying(50),
platform_id character varying(50),
name character varying(255),
parent_id character varying(50),
civil_code character varying(50),
business_group_id character varying(50),
constraint uk_platform_catalog_id_platform_id unique (id, platform_id)
);
create table wvp_platform_gb_channel (
id serial primary key ,
platform_id character varying(50),
catalog_id character varying(50),
device_channel_id integer,
constraint uk_platform_gb_channel_platform_id_catalog_id_device_channel_id unique (platform_id, catalog_id, device_channel_id)
);
create table wvp_platform_gb_stream (
id serial primary key,
platform_id character varying(50),
catalog_id character varying(50),
gb_stream_id integer,
constraint uk_platform_gb_stream_platform_id_catalog_id_gb_stream_id unique (platform_id, catalog_id, gb_stream_id)
);
create table wvp_stream_proxy (
id serial primary key,
type character varying(50),
app character varying(255),
stream character varying(255),
url character varying(255),
src_url character varying(255),
dst_url character varying(255),
timeout_ms integer,
ffmpeg_cmd_key character varying(255),
rtp_type character varying(50),
media_server_id character varying(50),
enable_audio bool default false,
enable_mp4 bool default false,
enable bool default false,
status boolean,
enable_remove_none_reader bool default false,
create_time character varying(50),
name character varying(255),
update_time character varying(50),
stream_key character varying(255),
enable_disable_none_reader bool default false,
constraint uk_stream_proxy_app_stream unique (app, stream)
);
create table wvp_stream_push (
id serial primary key,
app character varying(255),
stream character varying(255),
total_reader_count character varying(50),
origin_type integer,
origin_type_str character varying(50),
create_time character varying(50),
alive_second integer,
media_server_id character varying(50),
server_id character varying(50),
push_time character varying(50),
status bool default false,
update_time character varying(50),
push_ing bool default false,
self bool default false,
constraint uk_stream_push_app_stream unique (app, stream)
);
create table wvp_cloud_record (
id serial primary key,
app character varying(255),
stream character varying(255),
call_id character varying(255),
start_time bigint,
end_time bigint,
media_server_id character varying(50),
file_name character varying(255),
folder character varying(255),
file_path character varying(255),
collect bool default false,
file_size bigint,
time_len bigint,
constraint uk_stream_push_app_stream_path unique (app, stream, file_path)
);
create table wvp_user (
id serial primary key,
username character varying(255),
password character varying(255),
role_id integer,
create_time character varying(50),
update_time character varying(50),
push_key character varying(50),
constraint uk_user_username unique (username)
);
create table wvp_user_role (
id serial primary key,
name character varying(50),
authority character varying(50),
create_time character varying(50),
update_time character varying(50)
);
create table wvp_resources_tree (
id serial primary key ,
is_catalog bool default true,
device_channel_id integer ,
gb_stream_id integer,
name character varying(255),
parentId integer,
path character varying(255)
);
/*初始数据*/
INSERT INTO wvp_user VALUES (1, 'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021-04-13 14:14:57','2021-04-13 14:14:57','3e80d1762a324d5b0ff636e0bd16f1e3');
INSERT INTO wvp_user_role VALUES (1, 'admin','0','2021-04-13 14:14:57','2021-04-13 14:14:57');

View File

@ -164,6 +164,8 @@ create table wvp_media_server (
create_time character varying(50),
update_time character varying(50),
hook_alive_interval integer,
record_path character varying(255),
record_day integer default 7,
constraint uk_media_server_unique_ip_http_port unique (ip, http_port)
);
@ -259,6 +261,7 @@ create table wvp_stream_push (
create_time character varying(50),
alive_second integer,
media_server_id character varying(50),
server_id character varying(50),
push_time character varying(50),
status bool default false,
update_time character varying(50),
@ -266,6 +269,22 @@ create table wvp_stream_push (
self bool default false,
constraint uk_stream_push_app_stream unique (app, stream)
);
create table wvp_cloud_record (
id serial primary key,
app character varying(255),
stream character varying(255),
call_id character varying(255),
start_time int8,
end_time int8,
media_server_id character varying(50),
file_name character varying(255),
folder character varying(255),
file_path character varying(255),
collect bool default false,
file_size int8,
time_len int8,
constraint uk_stream_push_app_stream_path unique (app, stream, file_path)
);
create table wvp_user (
id serial primary key,

View File

@ -0,0 +1,503 @@
alter table device
change deviceId device_id varchar(50) not null;
alter table device
change streamMode stream_mode varchar(50) null;
alter table device
change registerTime register_time varchar(50) null;
alter table device
change keepaliveTime keepalive_time varchar(50) null;
alter table device
change createTime create_time varchar(50) not null;
alter table device
change updateTime update_time varchar(50) not null;
alter table device
change subscribeCycleForCatalog subscribe_cycle_for_catalog bool default false;
alter table device
change subscribeCycleForMobilePosition subscribe_cycle_for_mobile_position bool default false;
alter table device
change mobilePositionSubmissionInterval mobile_position_submission_interval int default 5 not null;
alter table device
change subscribeCycleForAlarm subscribe_cycle_for_alarm bool default false;
alter table device
change hostAddress host_address varchar(50) null;
alter table device
change ssrcCheck ssrc_check bool default false;
alter table device
change geoCoordSys geo_coord_sys varchar(50) not null;
alter table device
drop column treeType;
alter table device
change mediaServerId media_server_id varchar(50) default 'auto' null;
alter table device
change sdpIp sdp_ip varchar(50) null;
alter table device
change localIp local_ip varchar(50) null;
alter table device
change asMessageChannel as_message_channel bool default false;
alter table device
change keepaliveIntervalTime keepalive_interval_time int null;
alter table device
change online on_line varchar(50) null;
alter table device
add COLUMN switch_primary_sub_stream bool default false comment '开启主子码流切换的开关0-不开启1-开启)现在已知支持设备为 大华、TP——LINK全系设备';
alter table device_alarm
change deviceId device_id varchar(50) not null;
alter table device_alarm
change channelId channel_id varchar(50) not null;
alter table device_alarm
change alarmPriority alarm_priority varchar(50) not null;
alter table device_alarm
change alarmMethod alarm_method varchar(50) null;
alter table device_alarm
change alarmTime alarm_time varchar(50) not null;
alter table device_alarm
change alarmDescription alarm_description varchar(255) null;
alter table device_alarm
change alarmType alarm_type varchar(50) null;
alter table device_alarm
change createTime create_time varchar(50) null;
alter table device_channel
change channelId channel_id varchar(50) not null;
alter table device_channel
change civilCode civil_code varchar(50) null;
alter table device_channel
change parentId parent_id varchar(50) null;
alter table device_channel
change safetyWay safety_way int null;
alter table device_channel
change registerWay register_way int null;
alter table device_channel
change certNum cert_num varchar(50) null;
alter table device_channel
change errCode err_code int null;
alter table device_channel
change endTime end_time varchar(50) null;
alter table device_channel
change ipAddress ip_address varchar(50) null;
alter table device_channel
change PTZType ptz_type int null;
alter table device_channel
change status status bool default false;
alter table device_channel
change streamId stream_id varchar(255) null;
alter table device_channel
change deviceId device_id varchar(50) not null;
alter table device_channel
change hasAudio has_audio bool default false;
alter table device_channel
change createTime create_time varchar(50) not null;
alter table device_channel
change updateTime update_time varchar(50) not null;
alter table device_channel
change subCount sub_count int default 0 null;
alter table device_channel
change longitudeGcj02 longitude_gcj02 double null;
alter table device_channel
change latitudeGcj02 latitude_gcj02 double null;
alter table device_channel
change longitudeWgs84 longitude_wgs84 double null;
alter table device_channel
change latitudeWgs84 latitude_wgs84 double null;
alter table device_channel
change businessGroupId business_group_id varchar(50) null;
alter table device_channel
change gpsTime gps_time varchar(50) null;
alter table device_mobile_position
change deviceId device_id varchar(50) not null;
alter table device_mobile_position
change channelId channel_id varchar(50) not null;
alter table device_mobile_position
change deviceName device_name varchar(255) null;
alter table device_mobile_position
change reportSource report_source varchar(50) null;
alter table device_mobile_position
change longitudeGcj02 longitude_gcj02 double null;
alter table device_mobile_position
change latitudeGcj02 latitude_gcj02 double null;
alter table device_mobile_position
change longitudeWgs84 longitude_wgs84 double null;
alter table device_mobile_position
change latitudeWgs84 latitude_wgs84 double null;
alter table device_mobile_position
change createTime create_time varchar(50) null;
alter table gb_stream
change gbStreamId gb_stream_id int auto_increment;
alter table gb_stream
change gbId gb_id varchar(50) not null;
alter table gb_stream
change streamType stream_type varchar(50) null;
alter table gb_stream
change mediaServerId media_server_id varchar(50) null;
alter table gb_stream
change createTime create_time varchar(50) null;
alter table log
change createTime create_time varchar(50) not null;
alter table media_server
change hookIp hook_ip varchar(50) not null;
alter table media_server
add column send_rtp_port_range varchar(50) default null;
alter table media_server
change sdpIp sdp_ip varchar(50) not null;
alter table media_server
change streamIp stream_ip varchar(50) not null;
alter table media_server
change httpPort http_port int not null;
alter table media_server
change httpSSlPort http_ssl_port int not null;
alter table media_server
change rtmpPort rtmp_port int not null;
alter table media_server
change rtmpSSlPort rtmp_ssl_port int not null;
alter table media_server
change rtpProxyPort rtp_proxy_port int not null;
alter table media_server
change rtspPort rtsp_port int not null;
alter table media_server
change rtspSSLPort rtsp_ssl_port int not null;
alter table media_server
change autoConfig auto_config bool default true;
alter table media_server
change rtpEnable rtp_enable bool default false;
alter table media_server
change rtpPortRange rtp_port_range varchar(50) not null;
alter table media_server
change recordAssistPort record_assist_port int not null;
alter table media_server
change defaultServer default_server bool default false;
alter table media_server
change createTime create_time varchar(50) not null;
alter table media_server
change updateTime update_time varchar(50) not null;
alter table media_server
change hookAliveInterval hook_alive_interval int not null;
alter table parent_platform
change serverGBId server_gb_id varchar(50) not null;
alter table parent_platform
change serverGBDomain server_gb_domain varchar(50) null;
alter table parent_platform
change serverIP server_ip varchar(50) null;
alter table parent_platform
change serverPort server_port int null;
alter table parent_platform
change deviceGBId device_gb_id varchar(50) not null;
alter table parent_platform
change deviceIp device_ip varchar(50) null;
alter table parent_platform
change devicePort device_port varchar(50) null;
alter table parent_platform
change keepTimeout keep_timeout varchar(50) null;
alter table parent_platform
change characterSet character_set varchar(50) null;
alter table parent_platform
change catalogId catalog_id varchar(50) not null;
alter table parent_platform
change startOfflinePush start_offline_push bool default false;
alter table parent_platform
change administrativeDivision administrative_division varchar(50) not null;
alter table parent_platform
change catalogGroup catalog_group int default 1 null;
alter table parent_platform
change createTime create_time varchar(50) null;
alter table parent_platform
change updateTime update_time varchar(50) null;
alter table parent_platform
drop column treeType;
alter table parent_platform
change asMessageChannel as_message_channel bool default false;
alter table parent_platform
change enable enable bool default false;
alter table parent_platform
change ptz ptz bool default false;
alter table parent_platform
change rtcp rtcp bool default false;
alter table parent_platform
change status status bool default false;
alter table parent_platform
change status status bool default false;
alter table platform_catalog
change platformId platform_id varchar(50) not null;
alter table platform_catalog
change parentId parent_id varchar(50) null;
alter table platform_catalog
change civilCode civil_code varchar(50) null;
alter table platform_catalog
change businessGroupId business_group_id varchar(50) null;
alter table platform_gb_channel
change platformId platform_id varchar(50) not null;
alter table platform_gb_channel
change catalogId catalog_id varchar(50) not null;
alter table platform_gb_channel
change deviceChannelId device_channel_id int not null;
alter table platform_gb_stream
change platformId platform_id varchar(50) not null;
alter table platform_gb_stream
change catalogId catalog_id varchar(50) not null;
alter table platform_gb_stream
change gbStreamId gb_stream_id int not null;
alter table stream_proxy
change mediaServerId media_server_id varchar(50) null;
alter table stream_proxy
change createTime create_time varchar(50) not null;
alter table stream_proxy
change updateTime update_time varchar(50) null;
alter table stream_proxy
change enable_remove_none_reader enable_remove_none_reader bool default false;
alter table stream_proxy
change enable_disable_none_reader enable_disable_none_reader bool default false;
alter table stream_proxy
change enable_audio enable_audio bool default false;
alter table stream_proxy
change enable_mp4 enable_mp4 bool default false;
alter table stream_proxy
change enable enable bool default false;
alter table stream_push
change totalReaderCount total_reader_count varchar(50) null;
alter table stream_push
change originType origin_type int null;
alter table stream_push
change originTypeStr origin_type_str varchar(50) null;
alter table stream_push
change createTime create_time varchar(50) null;
alter table stream_push
change aliveSecond alive_second int null;
alter table stream_push
change mediaServerId media_server_id varchar(50) null;
alter table stream_push
change status status bool default false;
alter table stream_push
change pushTime push_time varchar(50) null;
alter table stream_push
change updateTime update_time varchar(50) null;
alter table stream_push
change pushIng push_ing bool default false;
alter table stream_push
change status status bool default false;
alter table stream_push
change self self bool default false;
alter table stream_push
drop column serverId;
alter table user
change roleId role_id int not null;
alter table user
change createTime create_time varchar(50) not null;
alter table user
change updateTime update_time varchar(50) not null;
alter table user
change pushKey push_key varchar(50) null;
alter table user_role
change createTime create_time varchar(50) not null;
alter table user_role
change updateTime update_time varchar(50) not null;
rename table device to wvp_device;
rename table device_alarm to wvp_device_alarm;
rename table device_channel to wvp_device_channel;
rename table device_mobile_position to wvp_device_mobile_position;
rename table gb_stream to wvp_gb_stream;
rename table log to wvp_log;
rename table media_server to wvp_media_server;
rename table parent_platform to wvp_platform;
rename table platform_catalog to wvp_platform_catalog;
rename table platform_gb_channel to wvp_platform_gb_channel;
rename table platform_gb_stream to wvp_platform_gb_stream;
rename table stream_proxy to wvp_stream_proxy;
rename table stream_push to wvp_stream_push;
rename table user to wvp_user;
rename table user_role to wvp_user_role;
alter table wvp_device add column broadcast_push_after_ack bool default false;
alter table wvp_device_channel add column custom_name varchar(255) null ;
alter table wvp_device_channel add column custom_longitude double null ;
alter table wvp_device_channel add column custom_latitude double null ;
alter table wvp_device_channel add column custom_ptz_type int null ;
create table wvp_resources_tree (
id serial primary key ,
is_catalog bool default true,
device_channel_id integer ,
gb_stream_id integer,
name character varying(255),
parentId integer,
path character varying(255)
);
alter table wvp_platform
add auto_push_channel bool default false;
alter table wvp_stream_proxy
add stream_key character varying(255);
create table wvp_cloud_record (
id serial primary key,
app character varying(255),
stream character varying(255),
call_id character varying(255),
start_time bigint,
end_time bigint,
media_server_id character varying(50),
file_name character varying(255),
folder character varying(255),
file_path character varying(255),
collect bool default false,
file_size bigint,
time_len bigint,
constraint uk_stream_push_app_stream_path unique (app, stream, file_path)
);
alter table wvp_media_server
add record_path character varying(255);
alter table wvp_media_server
add record_day integer default 7;
alter table wvp_stream_push
add server_id character varying(50);

View File

@ -0,0 +1,505 @@
alter table device
change deviceId device_id varchar(50) not null;
alter table device
change streamMode stream_mode varchar(50) null;
alter table device
change registerTime register_time varchar(50) null;
alter table device
change keepaliveTime keepalive_time varchar(50) null;
alter table device
change createTime create_time varchar(50) not null;
alter table device
change updateTime update_time varchar(50) not null;
alter table device
change subscribeCycleForCatalog subscribe_cycle_for_catalog bool default false;
alter table device
change subscribeCycleForMobilePosition subscribe_cycle_for_mobile_position bool default false;
alter table device
change mobilePositionSubmissionInterval mobile_position_submission_interval int default 5 not null;
alter table device
change subscribeCycleForAlarm subscribe_cycle_for_alarm bool default false;
alter table device
change hostAddress host_address varchar(50) null;
alter table device
change ssrcCheck ssrc_check bool default false;
alter table device
change geoCoordSys geo_coord_sys varchar(50) not null;
alter table device
drop column treeType;
alter table device
change mediaServerId media_server_id varchar(50) default 'auto' null;
alter table device
change sdpIp sdp_ip varchar(50) null;
alter table device
change localIp local_ip varchar(50) null;
alter table device
change asMessageChannel as_message_channel bool default false;
alter table device
change keepaliveIntervalTime keepalive_interval_time int null;
alter table device
change online on_line varchar(50) null;
alter table device
add COLUMN switch_primary_sub_stream bool default false comment '开启主子码流切换的开关0-不开启1-开启)现在已知支持设备为 大华、TP——LINK全系设备'
alter table device_alarm
change deviceId device_id varchar(50) not null;
alter table device_alarm
change channelId channel_id varchar(50) not null;
alter table device_alarm
change alarmPriority alarm_priority varchar(50) not null;
alter table device_alarm
change alarmMethod alarm_method varchar(50) null;
alter table device_alarm
change alarmTime alarm_time varchar(50) not null;
alter table device_alarm
change alarmDescription alarm_description varchar(255) null;
alter table device_alarm
change alarmType alarm_type varchar(50) null;
alter table device_alarm
change createTime create_time varchar(50) null;
alter table device_channel
change channelId channel_id varchar(50) not null;
alter table device_channel
change civilCode civil_code varchar(50) null;
alter table device_channel
change parentId parent_id varchar(50) null;
alter table device_channel
change safetyWay safety_way int null;
alter table device_channel
change registerWay register_way int null;
alter table device_channel
change certNum cert_num varchar(50) null;
alter table device_channel
change errCode err_code int null;
alter table device_channel
change endTime end_time varchar(50) null;
alter table device_channel
change ipAddress ip_address varchar(50) null;
alter table device_channel
change PTZType ptz_type int null;
alter table device_channel
change status status bool default false;
alter table device_channel
change streamId stream_id varchar(255) null;
alter table device_channel
change deviceId device_id varchar(50) not null;
alter table device_channel
change hasAudio has_audio bool default false;
alter table device_channel
change createTime create_time varchar(50) not null;
alter table device_channel
change updateTime update_time varchar(50) not null;
alter table device_channel
change subCount sub_count int default 0 null;
alter table device_channel
change longitudeGcj02 longitude_gcj02 double null;
alter table device_channel
change latitudeGcj02 latitude_gcj02 double null;
alter table device_channel
change longitudeWgs84 longitude_wgs84 double null;
alter table device_channel
change latitudeWgs84 latitude_wgs84 double null;
alter table device_channel
change businessGroupId business_group_id varchar(50) null;
alter table device_channel
change gpsTime gps_time varchar(50) null;
alter table device_mobile_position
change deviceId device_id varchar(50) not null;
alter table device_mobile_position
change channelId channel_id varchar(50) not null;
alter table device_mobile_position
change deviceName device_name varchar(255) null;
alter table device_mobile_position
change reportSource report_source varchar(50) null;
alter table device_mobile_position
change longitudeGcj02 longitude_gcj02 double null;
alter table device_mobile_position
change latitudeGcj02 latitude_gcj02 double null;
alter table device_mobile_position
change longitudeWgs84 longitude_wgs84 double null;
alter table device_mobile_position
change latitudeWgs84 latitude_wgs84 double null;
alter table device_mobile_position
change createTime create_time varchar(50) null;
alter table gb_stream
change gbStreamId gb_stream_id int auto_increment;
alter table gb_stream
change gbId gb_id varchar(50) not null;
alter table gb_stream
change streamType stream_type varchar(50) null;
alter table gb_stream
change mediaServerId media_server_id varchar(50) null;
alter table gb_stream
change createTime create_time varchar(50) null;
alter table log
change createTime create_time varchar(50) not null;
alter table media_server
change hookIp hook_ip varchar(50) not null;
alter table media_server
add column send_rtp_port_range varchar(50) default null;
alter table media_server
change sdpIp sdp_ip varchar(50) not null;
alter table media_server
change streamIp stream_ip varchar(50) not null;
alter table media_server
change httpPort http_port int not null;
alter table media_server
change httpSSlPort http_ssl_port int not null;
alter table media_server
change rtmpPort rtmp_port int not null;
alter table media_server
change rtmpSSlPort rtmp_ssl_port int not null;
alter table media_server
change rtpProxyPort rtp_proxy_port int not null;
alter table media_server
change rtspPort rtsp_port int not null;
alter table media_server
change rtspSSLPort rtsp_ssl_port int not null;
alter table media_server
change autoConfig auto_config bool default true;
alter table media_server
change rtpEnable rtp_enable bool default false;
alter table media_server
change rtpPortRange rtp_port_range varchar(50) not null;
alter table media_server
change recordAssistPort record_assist_port int not null;
alter table media_server
change defaultServer default_server bool default false;
alter table media_server
change createTime create_time varchar(50) not null;
alter table media_server
change updateTime update_time varchar(50) not null;
alter table media_server
change hookAliveInterval hook_alive_interval int not null;
alter table parent_platform
change serverGBId server_gb_id varchar(50) not null;
alter table parent_platform
change serverGBDomain server_gb_domain varchar(50) null;
alter table parent_platform
change serverIP server_ip varchar(50) null;
alter table parent_platform
change serverPort server_port int null;
alter table parent_platform
change deviceGBId device_gb_id varchar(50) not null;
alter table parent_platform
change deviceIp device_ip varchar(50) null;
alter table parent_platform
change devicePort device_port varchar(50) null;
alter table parent_platform
change keepTimeout keep_timeout varchar(50) null;
alter table parent_platform
change characterSet character_set varchar(50) null;
alter table parent_platform
change catalogId catalog_id varchar(50) not null;
alter table parent_platform
change startOfflinePush start_offline_push bool default false;
alter table parent_platform
change administrativeDivision administrative_division varchar(50) not null;
alter table parent_platform
change catalogGroup catalog_group int default 1 null;
alter table parent_platform
change createTime create_time varchar(50) null;
alter table parent_platform
change updateTime update_time varchar(50) null;
alter table parent_platform
drop column treeType;
alter table parent_platform
change asMessageChannel as_message_channel bool default false;
alter table parent_platform
change enable enable bool default false;
alter table parent_platform
change ptz ptz bool default false;
alter table parent_platform
change rtcp rtcp bool default false;
alter table parent_platform
change status status bool default false;
alter table parent_platform
change status status bool default false;
alter table platform_catalog
change platformId platform_id varchar(50) not null;
alter table platform_catalog
change parentId parent_id varchar(50) null;
alter table platform_catalog
change civilCode civil_code varchar(50) null;
alter table platform_catalog
change businessGroupId business_group_id varchar(50) null;
alter table platform_gb_channel
change platformId platform_id varchar(50) not null;
alter table platform_gb_channel
change catalogId catalog_id varchar(50) not null;
alter table platform_gb_channel
change deviceChannelId device_channel_id int not null;
alter table platform_gb_stream
change platformId platform_id varchar(50) not null;
alter table platform_gb_stream
change catalogId catalog_id varchar(50) not null;
alter table platform_gb_stream
change gbStreamId gb_stream_id int not null;
alter table stream_proxy
change mediaServerId media_server_id varchar(50) null;
alter table stream_proxy
change createTime create_time varchar(50) not null;
alter table stream_proxy
change updateTime update_time varchar(50) null;
alter table stream_proxy
change enable_remove_none_reader enable_remove_none_reader bool default false;
alter table stream_proxy
change enable_disable_none_reader enable_disable_none_reader bool default false;
alter table stream_proxy
change enable_audio enable_audio bool default false;
alter table stream_proxy
change enable_mp4 enable_mp4 bool default false;
alter table stream_proxy
change enable enable bool default false;
alter table stream_push
change totalReaderCount total_reader_count varchar(50) null;
alter table stream_push
change originType origin_type int null;
alter table stream_push
change originTypeStr origin_type_str varchar(50) null;
alter table stream_push
change createTime create_time varchar(50) null;
alter table stream_push
change aliveSecond alive_second int null;
alter table stream_push
change mediaServerId media_server_id varchar(50) null;
alter table stream_push
change status status bool default false;
alter table stream_push
change pushTime push_time varchar(50) null;
alter table stream_push
change updateTime update_time varchar(50) null;
alter table stream_push
change pushIng push_ing bool default false;
alter table stream_push
change status status bool default false;
alter table stream_push
change self self bool default false;
alter table stream_push
drop column serverId;
alter table user
change roleId role_id int not null;
alter table user
change createTime create_time varchar(50) not null;
alter table user
change updateTime update_time varchar(50) not null;
alter table user
change pushKey push_key varchar(50) null;
alter table user_role
change createTime create_time varchar(50) not null;
alter table user_role
change updateTime update_time varchar(50) not null;
rename table device to wvp_device;
rename table device_alarm to wvp_device_alarm;
rename table device_channel to wvp_device_channel;
rename table device_mobile_position to wvp_device_mobile_position;
rename table gb_stream to wvp_gb_stream;
rename table log to wvp_log;
rename table media_server to wvp_media_server;
rename table parent_platform to wvp_platform;
rename table platform_catalog to wvp_platform_catalog;
rename table platform_gb_channel to wvp_platform_gb_channel;
rename table platform_gb_stream to wvp_platform_gb_stream;
rename table stream_proxy to wvp_stream_proxy;
rename table stream_push to wvp_stream_push;
rename table user to wvp_user;
rename table user_role to wvp_user_role;
alter table wvp_device add column broadcast_push_after_ack bool default false;
alter table wvp_device_channel add column custom_name varchar(255) null ;
alter table wvp_device_channel add column custom_longitude double null ;
alter table wvp_device_channel add column custom_latitude double null ;
alter table wvp_device_channel add column custom_ptz_type int null ;
create table wvp_resources_tree (
id serial primary key ,
is_catalog bool default true,
device_channel_id integer ,
gb_stream_id integer,
name character varying(255),
parentId integer,
path character varying(255)
);
alter table wvp_platform
add auto_push_channel bool default false;
alter table wvp_stream_proxy
add stream_key character varying(255);
create table wvp_cloud_record (
id serial primary key,
app character varying(255),
stream character varying(255),
call_id character varying(255),
start_time int8,
end_time int8,
media_server_id character varying(50),
file_name character varying(255),
folder character varying(255),
file_path character varying(255),
collect bool default false,
file_size int8,
time_len int8,
constraint uk_stream_push_app_stream_path unique (app, stream, file_path)
);
alter table wvp_media_server
add record_path character varying(255);
alter table wvp_media_server
add record_day integer default 7;
alter table wvp_stream_push
add server_id character varying(50);

View File

@ -120,7 +120,7 @@ alter table device_channel
change status status bool default false;
alter table device_channel
change streamId stream_id varchar(50) null;
change streamId stream_id varchar(255) null;
alter table device_channel
change deviceId device_id varchar(50) not null;

Some files were not shown because too many files have changed in this diff Show More