@@ -181,13 +181,17 @@ export default {
return;
}
this.loading = true
+ this.linkPlan({
+ planId: this.planId,
+ channelIds: channels
+ }).cache
+
this.$axios({
method: 'post',
url: `/api/record/plan/link`,
data: {
planId: this.planId,
- all: true,
channelIds: channels
}
}).then((res)=> {
@@ -220,20 +224,19 @@ export default {
type: 'warning'
}).then(() => {
this.loading = true
-
this.$axios({
method: 'post',
- url: `/api/platform/channel/add`,
+ url: `/api/record/plan/link`,
data: {
- platformId: this.platformId,
+ planId: this.planId,
all: true
}
}).then((res)=> {
if (res.data.code === 0) {
this.$message.success({
- showClose: true,
- message: "保存成功"
- })
+ showClose: true,
+ message: "保存成功"
+ })
this.getChannelList()
}else {
this.$message.error({
@@ -261,10 +264,10 @@ export default {
}
this.$axios({
method: 'post',
- url: `/api/platform/channel/device/add`,
+ url: `/api/record/plan/link`,
data: {
- platformId: this.platformId,
- deviceIds: deviceIds,
+ planId: this.planId,
+ deviceDbIds: deviceIds
}
}).then((res)=> {
if (res.data.code === 0) {
@@ -272,13 +275,14 @@ export default {
showClose: true,
message: "保存成功"
})
- this.initData()
+ this.getChannelList()
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
+ this.loading = false
}).catch((error)=> {
this.$message.error({
showClose: true,
@@ -297,10 +301,9 @@ export default {
}
this.$axios({
method: 'post',
- url: `/api/platform/channel/device/remove`,
+ url: `/api/record/plan/link`,
data: {
- platformId: this.platformId,
- deviceIds: deviceIds,
+ deviceDbIds: deviceIds
}
}).then((res)=> {
if (res.data.code === 0) {
@@ -308,13 +311,14 @@ export default {
showClose: true,
message: "保存成功"
})
- this.initData()
+ this.getChannelList()
}else {
this.$message.error({
showClose: true,
message: res.data.msg
})
}
+ this.loading = false
}).catch((error)=> {
this.$message.error({
showClose: true,
From 812ddd3bbc713a71dfa9f1a652b0aeccfe7a78e3 Mon Sep 17 00:00:00 2001
From: 648540858 <648540858@qq.com>
Date: Wed, 27 Nov 2024 17:59:54 +0800
Subject: [PATCH 026/128] =?UTF-8?q?[=E5=BD=95=E5=88=B6=E8=AE=A1=E5=88=92]?=
=?UTF-8?q?=20=E4=BF=AE=E5=A4=8D=E5=85=B3=E8=81=94=E9=80=9A=E9=81=93?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../iot/vmp/gb28181/bean/CatalogData.java | 1 +
.../vmp/gb28181/dao/DeviceChannelMapper.java | 6 +-
.../gb28181/session/CatalogDataManager.java | 2 +-
.../service/impl/RecordPlanServiceImpl.java | 7 +-
.../recordPlan/RecordPlanController.java | 17 +-
.../recordPlan/bean/RecordPlanParam.java | 4 +-
.../components/dialog/linkChannelRecord.vue | 259 ++++--------------
7 files changed, 75 insertions(+), 221 deletions(-)
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java
index 25407ed4a..fb687b355 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java
@@ -20,6 +20,7 @@ public class CatalogData {
private Device device;
private String errorMsg;
private Set redisKeysForChannel = new HashSet<>();
+ private Set errorChannel = new HashSet<>();
private Set redisKeysForRegion = new HashSet<>();
private Set redisKeysForGroup = new HashSet<>();
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceChannelMapper.java
index 13f1976a8..99bb6e36f 100755
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceChannelMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceChannelMapper.java
@@ -93,8 +93,10 @@ public interface DeviceChannelMapper {
@SelectProvider(type = DeviceChannelProvider.class, method = "queryChannelsByDeviceDbId")
List queryChannelsByDeviceDbId(@Param("deviceDbId") int deviceDbId);
- @Select("select id from wvp_device_channel where device_db_id in " +
- " #{item} ")
+ @Select(value = {" "})
List queryChaneIdListByDeviceDbIds(List deviceDbIds);
@Delete("DELETE FROM wvp_device_channel WHERE device_db_id=#{deviceId}")
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataManager.java
index 049fc00e4..9be9a519a 100755
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataManager.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataManager.java
@@ -283,7 +283,7 @@ public class CatalogDataManager implements CommandLineRunner {
if (catalogData == null) {
return 0;
}
- return catalogData.getRedisKeysForChannel().size();
+ return catalogData.getRedisKeysForChannel().size() + catalogData.getErrorChannel().size();
}
public int sumNum(String deviceId, int sn) {
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
index f8f7e9c35..44b49ddde 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
@@ -2,10 +2,8 @@ package com.genersoft.iot.vmp.service.impl;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel;
-import com.genersoft.iot.vmp.gb28181.bean.PlatformChannel;
import com.genersoft.iot.vmp.gb28181.dao.CommonGBChannelMapper;
import com.genersoft.iot.vmp.service.IRecordPlanService;
-import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
import com.genersoft.iot.vmp.service.bean.RecordPlan;
import com.genersoft.iot.vmp.service.bean.RecordPlanItem;
import com.genersoft.iot.vmp.storager.dao.RecordPlanMapper;
@@ -99,8 +97,11 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
@Override
public void link(List channelIds, Integer planId) {
+ if (channelIds == null || channelIds.isEmpty()) {
+ log.info("[录制计划] 关联/移除关联时, 通道编号必须存在");
+ throw new ControllerException(ErrorCode.ERROR100.getCode(), "通道编号必须存在");
+ }
if (planId == null) {
- log.info("[录制计划] 移除通道关联的计划");
channelMapper.removeRecordPlan(channelIds);
}else {
channelMapper.addRecordPlan(channelIds, planId);
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/recordPlan/RecordPlanController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/recordPlan/RecordPlanController.java
index 3675acbb8..f01c11e5f 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/recordPlan/RecordPlanController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/recordPlan/RecordPlanController.java
@@ -3,7 +3,6 @@ package com.genersoft.iot.vmp.vmanager.recordPlan;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.conf.security.JwtUtils;
import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel;
-import com.genersoft.iot.vmp.gb28181.bean.PlatformChannel;
import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService;
import com.genersoft.iot.vmp.service.IRecordPlanService;
import com.genersoft.iot.vmp.service.bean.RecordPlan;
@@ -50,25 +49,27 @@ public class RecordPlanController {
@ResponseBody
@PostMapping("/link")
@Operation(summary = "通道关联录制计划", security = @SecurityRequirement(name = JwtUtils.HEADER))
- @Parameter(name = "param", description = "通道关联录制计划", required = false)
+ @Parameter(name = "param", description = "通道关联录制计划", required = true)
public void link(@RequestBody RecordPlanParam param) {
- if (param.getChannelIds() == null && param.getDeviceDbIds() == null) {
- throw new ControllerException(ErrorCode.ERROR100.getCode(), "通道ID和国标设备ID不可都为NULL");
- }
- if (param.getAll() != null) {
- if (param.getAll()) {
+ if (param.getAllLink() != null) {
+ if (param.getAllLink()) {
recordPlanService.linkAll(param.getPlanId());
}else {
recordPlanService.cleanAll(param.getPlanId());
}
return;
}
+
+ if (param.getChannelIds() == null && param.getDeviceDbIds() == null) {
+ throw new ControllerException(ErrorCode.ERROR100.getCode(), "通道ID和国标设备ID不可都为NULL");
+ }
+
List channelIds = new ArrayList<>();
if (param.getChannelIds() != null) {
channelIds.addAll(param.getChannelIds());
}else {
List chanelIdList = deviceChannelService.queryChaneIdListByDeviceDbIds(param.getDeviceDbIds());
- if (chanelIdList == null || chanelIdList.isEmpty()) {
+ if (chanelIdList != null && !chanelIdList.isEmpty()) {
channelIds = chanelIdList;
}
}
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/recordPlan/bean/RecordPlanParam.java b/src/main/java/com/genersoft/iot/vmp/vmanager/recordPlan/bean/RecordPlanParam.java
index e74314098..9f56a0fbb 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/recordPlan/bean/RecordPlanParam.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/recordPlan/bean/RecordPlanParam.java
@@ -1,7 +1,5 @@
package com.genersoft.iot.vmp.vmanager.recordPlan.bean;
-import com.genersoft.iot.vmp.service.bean.RecordPlan;
-import com.genersoft.iot.vmp.service.bean.RecordPlanItem;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -18,7 +16,7 @@ public class RecordPlanParam {
private List deviceDbIds;
@Schema(description = "全部关联/全部取消关联")
- private Boolean all;
+ private Boolean allLink;
@Schema(description = "录制计划ID, ID为空是删除关联的计划")
private Integer planId;
diff --git a/web_src/src/components/dialog/linkChannelRecord.vue b/web_src/src/components/dialog/linkChannelRecord.vue
index 6f2a299a3..2046c1eb7 100755
--- a/web_src/src/components/dialog/linkChannelRecord.vue
+++ b/web_src/src/components/dialog/linkChannelRecord.vue
@@ -116,17 +116,19 @@ export default {
};
},
- created() {
- this.initData();
- },
+ created() {},
destroyed() {},
methods: {
openDialog(planId, closeCallback) {
this.planId = planId
this.showDialog = true
this.closeCallback = closeCallback
+ this.initData()
},
initData: function () {
+ this.currentPage= 1;
+ this.count= 15;
+ this.total= 0;
this.getChannelList();
},
currentChange: function (val) {
@@ -168,6 +170,36 @@ export default {
handleSelectionChange: function (val){
this.multipleSelection = val;
},
+
+ linkPlan: function (data){
+ this.loading = true
+ return this.$axios({
+ method: 'post',
+ url: `/api/record/plan/link`,
+ data: data
+ }).then((res)=> {
+ if (res.data.code === 0) {
+ this.$message.success({
+ showClose: true,
+ message: "保存成功"
+ })
+ this.getChannelList()
+ }else {
+ this.$message.error({
+ showClose: true,
+ message: res.data.msg
+ })
+ }
+ this.loading = false
+ }).catch((error)=> {
+ this.$message.error({
+ showClose: true,
+ message: error
+ })
+ this.loading = false
+ })
+ },
+
add: function (row) {
let channels = []
for (let i = 0; i < this.multipleSelection.length; i++) {
@@ -180,41 +212,10 @@ export default {
})
return;
}
- this.loading = true
this.linkPlan({
planId: this.planId,
channelIds: channels
- }).cache
-
-
- this.$axios({
- method: 'post',
- url: `/api/record/plan/link`,
- data: {
- planId: this.planId,
- channelIds: channels
- }
- }).then((res)=> {
- if (res.data.code === 0) {
- this.$message.success({
- showClose: true,
- message: "保存成功"
- })
- this.getChannelList()
- }else {
- this.$message.error({
- showClose: true,
- message: res.data.msg
- })
- }
- this.loading = false
- }).catch((error)=> {
- this.$message.error({
- showClose: true,
- message: error
- })
- this.loading = false
- });
+ })
},
addAll: function (row) {
this.$confirm("确定全部添加?", '提示', {
@@ -223,36 +224,11 @@ export default {
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
- this.loading = true
- this.$axios({
- method: 'post',
- url: `/api/record/plan/link`,
- data: {
- planId: this.planId,
- all: true
- }
- }).then((res)=> {
- if (res.data.code === 0) {
- this.$message.success({
- showClose: true,
- message: "保存成功"
- })
- this.getChannelList()
- }else {
- this.$message.error({
- showClose: true,
- message: res.data.msg
- })
- }
- this.loading = false
- }).catch((error)=> {
- this.$message.error({
- showClose: true,
- message: error
- })
- this.loading = false
- });
- }).catch(() => {
+ this.linkPlan({
+ planId: this.planId,
+ allLink: true
+ })
+ }).catch(() => {
});
},
@@ -262,34 +238,10 @@ export default {
for (let i = 0; i < rows.length; i++) {
deviceIds.push(rows[i].id)
}
- this.$axios({
- method: 'post',
- url: `/api/record/plan/link`,
- data: {
- planId: this.planId,
- deviceDbIds: deviceIds
- }
- }).then((res)=> {
- if (res.data.code === 0) {
- this.$message.success({
- showClose: true,
- message: "保存成功"
- })
- this.getChannelList()
- }else {
- this.$message.error({
- showClose: true,
- message: res.data.msg
- })
- }
- this.loading = false
- }).catch((error)=> {
- this.$message.error({
- showClose: true,
- message: error
- })
- this.loading = false
- });
+ this.linkPlan({
+ planId: this.planId,
+ deviceDbIds: deviceIds
+ })
})
},
@@ -299,33 +251,9 @@ export default {
for (let i = 0; i < rows.length; i++) {
deviceIds.push(rows[i].id)
}
- this.$axios({
- method: 'post',
- url: `/api/record/plan/link`,
- data: {
- deviceDbIds: deviceIds
- }
- }).then((res)=> {
- if (res.data.code === 0) {
- this.$message.success({
- showClose: true,
- message: "保存成功"
- })
- this.getChannelList()
- }else {
- this.$message.error({
- showClose: true,
- message: res.data.msg
- })
- }
- this.loading = false
- }).catch((error)=> {
- this.$message.error({
- showClose: true,
- message: error
- })
- this.loading = false
- });
+ this.linkPlan({
+ deviceDbIds: deviceIds
+ })
})
},
remove: function (row) {
@@ -340,36 +268,10 @@ export default {
})
return;
}
- this.loading = true
- this.$axios({
- method: 'delete',
- url: `/api/platform/channel/remove`,
- data: {
- platformId: this.platformId,
- channelIds: channels
- }
- }).then((res)=> {
- if (res.data.code === 0) {
- this.$message.success({
- showClose: true,
- message: "保存成功"
- })
- this.getChannelList()
- }else {
- this.$message.error({
- showClose: true,
- message: res.data.msg
- })
- }
- this.loading = false
- }).catch((error)=> {
- this.$message.error({
- showClose: true,
- message: error
- })
- this.loading = false
- });
+ this.linkPlan({
+ channelIds: channels
+ })
},
removeAll: function (row) {
@@ -379,62 +281,11 @@ export default {
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
- this.loading = true
- this.$axios({
- method: 'delete',
- url: `/api/platform/channel/remove`,
- data: {
- platformId: this.platformId,
- all: true
- }
- }).then((res)=> {
- if (res.data.code === 0) {
- this.$message.success({
- showClose: true,
- message: "保存成功"
- })
- this.getChannelList()
- }else {
- this.$message.error({
- showClose: true,
- message: res.data.msg
- })
- }
- this.loading = false
- }).catch((error)=> {
- this.$message.error({
- showClose: true,
- message: error
- })
- this.loading = false
- });
- }).catch(() => {
- });
-
- },
- saveCustom: function (row) {
- this.$axios({
- method: 'post',
- url: `/api/platform/channel/custom/update`,
- data: row
- }).then((res)=> {
- if (res.data.code === 0) {
- this.$message.success({
- showClose: true,
- message: "保存成功"
- })
- this.initData()
- }else {
- this.$message.error({
- showClose: true,
- message: res.data.msg
- })
- }
- }).catch((error)=> {
- this.$message.error({
- showClose: true,
- message: error
+ this.linkPlan({
+ planId: this.planId,
+ allLink: false
})
+ }).catch(() => {
});
},
search: function () {
From cb12bd731d3c449d359dd1252892b0e7bfeb30df Mon Sep 17 00:00:00 2001
From: 648540858 <648540858@qq.com>
Date: Wed, 27 Nov 2024 22:44:22 +0800
Subject: [PATCH 027/128] =?UTF-8?q?=E4=B8=B4=E6=97=B6=E6=8F=90=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../iot/vmp/service/bean/RecordPlanItem.java | 8 +--
.../service/impl/RecordPlanServiceImpl.java | 55 ++++++++++++++++++-
.../vmp/storager/dao/RecordPlanMapper.java | 6 +-
.../src/components/dialog/editRecordPlan.vue | 47 +++++++++-------
数据库/2.7.3/初始化-mysql-2.7.3.sql | 6 +-
.../2.7.3/初始化-postgresql-kingbase-2.7.3.sql | 4 +-
6 files changed, 91 insertions(+), 35 deletions(-)
diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/RecordPlanItem.java b/src/main/java/com/genersoft/iot/vmp/service/bean/RecordPlanItem.java
index 14383cb15..31fa3214e 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/bean/RecordPlanItem.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/bean/RecordPlanItem.java
@@ -10,11 +10,11 @@ public class RecordPlanItem {
@Schema(description = "计划项数据库ID")
private int id;
- @Schema(description = "计划开始时间")
- private Long startTime;
+ @Schema(description = "计划开始时间的序号, 从0点开始,每半个小时增加1")
+ private Integer start;
- @Schema(description = "计划结束时间")
- private Long stopTime;
+ @Schema(description = "计划结束时间的序号, 从0点开始,每半个小时增加1")
+ private Integer stop;
@Schema(description = "计划周几执行")
private Integer weekDay;
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
index 44b49ddde..dc3de6834 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
@@ -1,8 +1,14 @@
package com.genersoft.iot.vmp.service.impl;
+import com.genersoft.iot.vmp.common.InviteInfo;
+import com.genersoft.iot.vmp.common.InviteSessionStatus;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
-import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel;
+import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
+import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.dao.CommonGBChannelMapper;
+import com.genersoft.iot.vmp.gb28181.service.IGbChannelService;
+import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
+import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent;
import com.genersoft.iot.vmp.service.IRecordPlanService;
import com.genersoft.iot.vmp.service.bean.RecordPlan;
import com.genersoft.iot.vmp.service.bean.RecordPlanItem;
@@ -13,9 +19,15 @@ import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import javax.sip.InvalidArgumentException;
+import javax.sip.SipException;
+import java.text.ParseException;
import java.util.List;
@Service
@@ -28,6 +40,43 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
@Autowired
private CommonGBChannelMapper channelMapper;
+ @Autowired
+ private IGbChannelService channelService;
+
+
+ /**
+ * 流到来的处理
+ */
+ @Async("taskExecutor")
+ @org.springframework.context.event.EventListener
+ public void onApplicationEvent(MediaArrivalEvent event) {
+
+ }
+
+ /**
+ * 流离开的处理
+ */
+ @Async("taskExecutor")
+ @EventListener
+ public void onApplicationEvent(MediaDepartureEvent event) {
+ // 流断开,检查是否还处于录像状态, 如果是则继续录像
+
+ }
+
+ @Scheduled(cron = "0 */30 * * * *")
+ public void execution() {
+ // 执行计划
+ // 查询startTime等于现在的, 开始录像
+
+ // 查询stopTime等于现在的,结束录像
+ // 查询处于中间的,验证录像是否正在进行
+
+
+ // TODO 无人观看要确保处于录像状态的通道不被移除
+ }
+
+ // 系统启动时
+
@Override
@Transactional
public void add(RecordPlan plan) {
@@ -61,8 +110,8 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
public void update(RecordPlan plan) {
plan.setUpdateTime(DateUtil.getNow());
recordPlanMapper.update(plan);
- recordPlanMapper.cleanItems(plan.getId());
- if (plan.getPlanItemList() != null){
+ if (plan.getPlanItemList() != null && !plan.getPlanItemList().isEmpty()){
+ recordPlanMapper.cleanItems(plan.getId());
recordPlanMapper.batchAddItem(plan.getId(), plan.getPlanItemList());
}
// TODO 更新录像队列
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordPlanMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordPlanMapper.java
index 705e59aae..2cdc6468e 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordPlanMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordPlanMapper.java
@@ -26,13 +26,13 @@ public interface RecordPlanMapper {
@Insert(" ")
void batchAddItem(@Param("planId") int planId, List planItemList);
diff --git a/web_src/src/components/dialog/editRecordPlan.vue b/web_src/src/components/dialog/editRecordPlan.vue
index 3772c4909..5ae900c3b 100644
--- a/web_src/src/components/dialog/editRecordPlan.vue
+++ b/web_src/src/components/dialog/editRecordPlan.vue
@@ -157,7 +157,6 @@ export default {
// 把 336长度的 list 分成 7 组,每组 48 个
for (let i = 0; i < this.byteTime.length; i += DayTimes) {
let planArray = this.byteTime2Plan(this.byteTime.slice(i, i + DayTimes));
- console.log(planArray)
if(!planArray || planArray.length === 0) {
week ++;
continue
@@ -165,8 +164,8 @@ export default {
for (let j = 0; j < planArray.length; j++) {
planList.push({
id: this.id,
- startTime: planArray[j].startTime,
- stopTime: planArray[j].stopTime,
+ start: planArray[j].start,
+ stop: planArray[j].stop,
weekDay: week
})
}
@@ -175,44 +174,52 @@ export default {
return planList
},
byteTime2Plan(weekItem){
- let startTime = 0;
- let endTime = 0;
+ let start = null;
+ let stop = null;
let result = []
for (let i = 0; i < weekItem.length; i++) {
let item = weekItem[i]
- if (item === '1') {
- endTime = 30*i
- if (startTime === 0 ) {
- startTime = 30*i
+ if (item === '1') { // 表示选中
+ stop = i
+ if (start === null ) {
+ start = i
+ }
+ if (i === weekItem.length - 1) {
+ result.push({
+ start: start,
+ stop: stop,
+ })
}
} else {
- if (endTime !== 0){
+ if (stop !== 0){
result.push({
- startTime: startTime * 60 * 1000,
- stopTime: endTime * 60 * 1000,
+ start: start,
+ stop: stop,
})
- startTime = 0
- endTime = 0
+ start = 0
+ stop = 0
}
}
}
return result;
},
plan2Byte(planList) {
- console.log(planList);
let byte = ""
let indexArray = {}
for (let i = 0; i < planList.length; i++) {
- let index = planList[i].startTime/1000/60/30
- let endIndex = planList[i].stopTime/1000/60/30
+
+ let weekDay = planList[i].weekDay
+ let index = planList[i].start
+ let endIndex = planList[i].stop
+ console.log(index + "===" + endIndex)
for (let j = index; j <= endIndex; j++) {
- indexArray[j + (planList[i].weekDay - 1 )*48] = j + i*48
+ indexArray["key_" + (j + (weekDay - 1 )*48)] = 1
+ console.log("key_" + (j + (weekDay - 1 )*48))
}
}
- console.log(indexArray)
for (let i = 0; i < 336; i++) {
- if (indexArray[i]){
+ if (indexArray["key_" + i]){
byte += "1"
}else {
byte += "0"
diff --git a/数据库/2.7.3/初始化-mysql-2.7.3.sql b/数据库/2.7.3/初始化-mysql-2.7.3.sql
index faada9cd4..e12677da5 100644
--- a/数据库/2.7.3/初始化-mysql-2.7.3.sql
+++ b/数据库/2.7.3/初始化-mysql-2.7.3.sql
@@ -440,10 +440,10 @@ create table wvp_record_plan
create table wvp_record_plan_item
(
id serial primary key,
- start_time bigint,
- stop_time bigint,
+ start int,
+ stop int,
week_day int,
- plan_id int,
+ plan_id int,
create_time character varying(50),
update_time character varying(50)
);
diff --git a/数据库/2.7.3/初始化-postgresql-kingbase-2.7.3.sql b/数据库/2.7.3/初始化-postgresql-kingbase-2.7.3.sql
index 9ef3e926b..c632f9a83 100644
--- a/数据库/2.7.3/初始化-postgresql-kingbase-2.7.3.sql
+++ b/数据库/2.7.3/初始化-postgresql-kingbase-2.7.3.sql
@@ -457,8 +457,8 @@ create table wvp_record_plan
create table wvp_record_plan_item
(
id serial primary key,
- start_time int8,
- stop_time int8,
+ start int,
+ stop int,
week_day int,
plan_id int,
create_time character varying(50),
From 25008d50c78816095f20f5f14b0b8dead34869d8 Mon Sep 17 00:00:00 2001
From: 648540858 <648540858@qq.com>
Date: Thu, 28 Nov 2024 10:15:03 +0800
Subject: [PATCH 028/128] =?UTF-8?q?[=E5=BD=95=E5=88=B6=E8=AE=A1=E5=88=92]?=
=?UTF-8?q?=20=E4=BF=AE=E5=A4=8D=E8=AE=A1=E5=88=92=E9=80=89=E6=8B=A9?=
=?UTF-8?q?=E4=B8=8E=E5=9B=9E=E6=98=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../service/impl/RecordPlanServiceImpl.java | 25 ++++++++++++-------
.../src/components/dialog/editRecordPlan.vue | 18 ++++++-------
2 files changed, 24 insertions(+), 19 deletions(-)
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
index dc3de6834..061769b82 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
@@ -1,10 +1,7 @@
package com.genersoft.iot.vmp.service.impl;
-import com.genersoft.iot.vmp.common.InviteInfo;
-import com.genersoft.iot.vmp.common.InviteSessionStatus;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
-import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
-import com.genersoft.iot.vmp.gb28181.bean.*;
+import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel;
import com.genersoft.iot.vmp.gb28181.dao.CommonGBChannelMapper;
import com.genersoft.iot.vmp.gb28181.service.IGbChannelService;
import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
@@ -25,9 +22,7 @@ import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import javax.sip.InvalidArgumentException;
-import javax.sip.SipException;
-import java.text.ParseException;
+import java.util.ArrayList;
import java.util.List;
@Service
@@ -110,9 +105,21 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
public void update(RecordPlan plan) {
plan.setUpdateTime(DateUtil.getNow());
recordPlanMapper.update(plan);
+ recordPlanMapper.cleanItems(plan.getId());
if (plan.getPlanItemList() != null && !plan.getPlanItemList().isEmpty()){
- recordPlanMapper.cleanItems(plan.getId());
- recordPlanMapper.batchAddItem(plan.getId(), plan.getPlanItemList());
+ List planItemList = new ArrayList<>();
+ for (RecordPlanItem recordPlanItem : plan.getPlanItemList()) {
+ if (recordPlanItem.getStart() == null || recordPlanItem.getStop() == null || recordPlanItem.getWeekDay() == null){
+ continue;
+ }
+ if (recordPlanItem.getPlanId() == null) {
+ recordPlanItem.setPlanId(plan.getId());
+ }
+ planItemList.add(recordPlanItem);
+ }
+ if(!planItemList.isEmpty()) {
+ recordPlanMapper.batchAddItem(plan.getId(), planItemList);
+ }
}
// TODO 更新录像队列
diff --git a/web_src/src/components/dialog/editRecordPlan.vue b/web_src/src/components/dialog/editRecordPlan.vue
index 5ae900c3b..1de115cca 100644
--- a/web_src/src/components/dialog/editRecordPlan.vue
+++ b/web_src/src/components/dialog/editRecordPlan.vue
@@ -53,7 +53,6 @@ export default {
},
methods: {
openDialog: function (recordPlan, endCallback) {
- console.log(recordPlan);
this.endCallback = endCallback;
this.showDialog = true;
this.byteTime= "";
@@ -68,7 +67,7 @@ export default {
planId: recordPlan.id,
}
}).then((res) => {
- if (res.data.code === 0) {
+ if (res.data.code === 0 && res.data.data.planItemList) {
this.byteTime = this.plan2Byte(res.data.data.planItemList)
}
}).catch((error) => {
@@ -163,7 +162,7 @@ export default {
}
for (let j = 0; j < planArray.length; j++) {
planList.push({
- id: this.id,
+ planId: this.id,
start: planArray[j].start,
stop: planArray[j].stop,
weekDay: week
@@ -177,28 +176,29 @@ export default {
let start = null;
let stop = null;
let result = []
-
+ console.log("===================")
for (let i = 0; i < weekItem.length; i++) {
let item = weekItem[i]
+ console.log(item)
if (item === '1') { // 表示选中
stop = i
if (start === null ) {
start = i
}
- if (i === weekItem.length - 1) {
+ if (i === weekItem.length - 1 && start != null && stop != null) {
result.push({
start: start,
stop: stop,
})
}
} else {
- if (stop !== 0){
+ if (stop !== null){
result.push({
start: start,
stop: stop,
})
- start = 0
- stop = 0
+ start = null
+ stop = null
}
}
}
@@ -212,10 +212,8 @@ export default {
let weekDay = planList[i].weekDay
let index = planList[i].start
let endIndex = planList[i].stop
- console.log(index + "===" + endIndex)
for (let j = index; j <= endIndex; j++) {
indexArray["key_" + (j + (weekDay - 1 )*48)] = 1
- console.log("key_" + (j + (weekDay - 1 )*48))
}
}
for (let i = 0; i < 336; i++) {
From ae339269403712fe9be941f5d620370b41d3b609 Mon Sep 17 00:00:00 2001
From: 648540858 <648540858@qq.com>
Date: Thu, 28 Nov 2024 18:00:52 +0800
Subject: [PATCH 029/128] =?UTF-8?q?[=E5=BD=95=E5=88=B6=E8=AE=A1=E5=88=92]?=
=?UTF-8?q?=20=E5=A2=9E=E5=8A=A0=E5=BD=95=E5=88=B6=E8=AE=A1=E5=88=92?=
=?UTF-8?q?=E6=89=A7=E8=A1=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../gb28181/dao/CommonGBChannelMapper.java | 50 +++++++++
.../zlm/dto/hook/HookResultForOnPublish.java | 52 +--------
.../iot/vmp/service/IRecordPlanService.java | 3 +-
.../vmp/service/impl/MediaServiceImpl.java | 7 ++
.../service/impl/RecordPlanServiceImpl.java | 100 ++++++++++++++----
.../vmp/storager/dao/RecordPlanMapper.java | 3 +
.../src/components/dialog/editRecordPlan.vue | 2 -
7 files changed, 148 insertions(+), 69 deletions(-)
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/CommonGBChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/CommonGBChannelMapper.java
index 2f636a4d5..d14ba09c2 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/CommonGBChannelMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/CommonGBChannelMapper.java
@@ -498,6 +498,7 @@ public interface CommonGBChannelMapper {
" wdc.stream_proxy_id,\n" +
" wdc.create_time,\n" +
" wdc.update_time,\n" +
+ " wdc.record_plan_id,\n" +
" coalesce( wdc.gb_device_id, wdc.device_id) as gb_device_id,\n" +
" coalesce( wdc.gb_name, wdc.name) as gb_name,\n" +
" coalesce( wdc.gb_manufacturer, wdc.manufacturer) as gb_manufacturer,\n" +
@@ -548,4 +549,53 @@ public interface CommonGBChannelMapper {
List queryForRecordPlanForWebList(@Param("planId") Integer planId, @Param("query") String query,
@Param("channelType") Integer channelType, @Param("online") Boolean online,
@Param("hasLink") Boolean hasLink);
+
+ @Select("")
+ List queryForRecordPlan(List planIdList);
}
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
index 33f9856f4..3777e19ec 100755
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
@@ -1,7 +1,11 @@
package com.genersoft.iot.vmp.media.zlm.dto.hook;
import com.genersoft.iot.vmp.media.bean.ResultForOnPublish;
+import lombok.Getter;
+import lombok.Setter;
+@Setter
+@Getter
public class HookResultForOnPublish extends HookResult{
private boolean enable_audio;
@@ -34,54 +38,6 @@ public class HookResultForOnPublish extends HookResult{
setMsg(msg);
}
- public boolean isEnable_audio() {
- return enable_audio;
- }
-
- public void setEnable_audio(boolean enable_audio) {
- this.enable_audio = enable_audio;
- }
-
- public boolean isEnable_mp4() {
- return enable_mp4;
- }
-
- public void setEnable_mp4(boolean enable_mp4) {
- this.enable_mp4 = enable_mp4;
- }
-
- public int getMp4_max_second() {
- return mp4_max_second;
- }
-
- public void setMp4_max_second(int mp4_max_second) {
- this.mp4_max_second = mp4_max_second;
- }
-
- public String getMp4_save_path() {
- return mp4_save_path;
- }
-
- public void setMp4_save_path(String mp4_save_path) {
- this.mp4_save_path = mp4_save_path;
- }
-
- public String getStream_replace() {
- return stream_replace;
- }
-
- public void setStream_replace(String stream_replace) {
- 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{" +
diff --git a/src/main/java/com/genersoft/iot/vmp/service/IRecordPlanService.java b/src/main/java/com/genersoft/iot/vmp/service/IRecordPlanService.java
index 359e0072a..abec8e0cb 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/IRecordPlanService.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/IRecordPlanService.java
@@ -1,7 +1,6 @@
package com.genersoft.iot.vmp.service;
import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel;
-import com.genersoft.iot.vmp.gb28181.bean.PlatformChannel;
import com.genersoft.iot.vmp.service.bean.RecordPlan;
import com.github.pagehelper.PageInfo;
@@ -27,4 +26,6 @@ public interface IRecordPlanService {
void linkAll(Integer planId);
void cleanAll(Integer planId);
+
+ boolean recording(String app, String stream);
}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
index ad575042b..0959c7843 100755
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
@@ -15,6 +15,7 @@ import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.bean.ResultForOnPublish;
import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
import com.genersoft.iot.vmp.service.IMediaService;
+import com.genersoft.iot.vmp.service.IRecordPlanService;
import com.genersoft.iot.vmp.service.IUserService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy;
@@ -59,6 +60,9 @@ public class MediaServiceImpl implements IMediaService {
@Autowired
private SipInviteSessionManager sessionManager;
+ @Autowired
+ private IRecordPlanService recordPlanService;
+
@Override
public boolean authenticatePlay(String app, String stream, String callId) {
if (app == null || stream == null) {
@@ -205,6 +209,9 @@ public class MediaServiceImpl implements IMediaService {
@Override
public boolean closeStreamOnNoneReader(String mediaServerId, String app, String stream, String schema) {
boolean result = false;
+ if (recordPlanService.recording(app, stream)) {
+ return false;
+ }
// 国标类型的流
if ("rtp".equals(app)) {
result = userSetting.getStreamOnDemand();
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
index 061769b82..4bcc945a0 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
@@ -1,12 +1,15 @@
package com.genersoft.iot.vmp.service.impl;
+import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel;
import com.genersoft.iot.vmp.gb28181.dao.CommonGBChannelMapper;
-import com.genersoft.iot.vmp.gb28181.service.IGbChannelService;
-import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
+import com.genersoft.iot.vmp.gb28181.service.IGbChannelPlayService;
+import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent;
+import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IRecordPlanService;
+import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
import com.genersoft.iot.vmp.service.bean.RecordPlan;
import com.genersoft.iot.vmp.service.bean.RecordPlanItem;
import com.genersoft.iot.vmp.storager.dao.RecordPlanMapper;
@@ -22,8 +25,8 @@ import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import java.util.ArrayList;
-import java.util.List;
+import java.time.LocalDateTime;
+import java.util.*;
@Service
@Slf4j
@@ -36,17 +39,12 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
private CommonGBChannelMapper channelMapper;
@Autowired
- private IGbChannelService channelService;
+ private IGbChannelPlayService channelPlayService;
+
+ @Autowired
+ private IMediaServerService mediaServerService;
- /**
- * 流到来的处理
- */
- @Async("taskExecutor")
- @org.springframework.context.event.EventListener
- public void onApplicationEvent(MediaArrivalEvent event) {
-
- }
/**
* 流离开的处理
@@ -55,23 +53,89 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
@EventListener
public void onApplicationEvent(MediaDepartureEvent event) {
// 流断开,检查是否还处于录像状态, 如果是则继续录像
+ if (recording(event.getApp(), event.getStream())) {
+ // 重新拉起
+ }
}
+ Map recordStreamMap = new HashMap<>();
+
@Scheduled(cron = "0 */30 * * * *")
public void execution() {
// 执行计划
+
+ // 获取当前时间在一周内的序号
+ LocalDateTime now = LocalDateTime.now();
+ int week = now.getDayOfWeek().getValue();
+ int index = now.getHour() * 2 + (now.getMinute() > 30?1:0);
// 查询startTime等于现在的, 开始录像
+ List startPlanList = recordPlanMapper.queryStart(week, index);
- // 查询stopTime等于现在的,结束录像
- // 查询处于中间的,验证录像是否正在进行
-
-
- // TODO 无人观看要确保处于录像状态的通道不被移除
+ Map channelMapWithoutRecord = new HashMap<>();
+ if (startPlanList.isEmpty()) {
+ // 停止所有正在录像的
+ if(recordStreamMap.isEmpty()) {
+ // 暂无录像任务
+ return;
+ }else {
+ channelMapWithoutRecord.putAll(recordStreamMap);
+ recordStreamMap.clear();
+ }
+ }else {
+ channelMapWithoutRecord.putAll(recordStreamMap);
+ // 获取所有的关联的通道
+ List channelList = channelMapper.queryForRecordPlan(startPlanList);
+ if (channelList.isEmpty()) {
+ recordStreamMap.clear();
+ }else {
+ // 查找是否已经开启录像, 如果没有则开启录像
+ for (CommonGBChannel channel : channelList) {
+ if (recordStreamMap.get(channel.getGbId()) != null) {
+ channelMapWithoutRecord.remove(channel.getGbId());
+ }else {
+ // 开启点播,
+ channelPlayService.play(channel, null, ((code, msg, streamInfo) -> {
+ if (code == InviteErrorCode.SUCCESS.getCode() && streamInfo != null) {
+ log.info("[录像] 开启成功, 通道ID: {}", channel.getGbId());
+ recordStreamMap.put(channel.getGbId(), streamInfo);
+ channelMapWithoutRecord.remove(channel.getGbId(), streamInfo);
+ }
+ }));
+ }
+ }
+ }
+ }
+ // 结束录像
+ if(!channelMapWithoutRecord.isEmpty()) {
+ for (Integer channelId : channelMapWithoutRecord.keySet()) {
+ StreamInfo streamInfo = channelMapWithoutRecord.get(channelId);
+ if (streamInfo == null) {
+ continue;
+ }
+ // 查看是否有人观看,存在则不做处理,等待后续自然处理,如果无人观看,则关闭该流
+ MediaInfo mediaInfo = mediaServerService.getMediaInfo(streamInfo.getMediaServer(), streamInfo.getApp(), streamInfo.getStream());
+ if (mediaInfo.getReaderCount() == null || mediaInfo.getReaderCount() == 0) {
+ mediaServerService.closeStreams(streamInfo.getMediaServer(), streamInfo.getApp(), streamInfo.getStream());
+ log.info("[录像] 停止, 通道ID: {}", channelId);
+ }
+ }
+ }
}
// 系统启动时
+
+ @Override
+ public boolean recording(String app, String stream) {
+ for (StreamInfo streamInfo : recordStreamMap.values()) {
+ if (streamInfo.getApp().equals(app) && streamInfo.getStream().equals(stream)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
@Transactional
public void add(RecordPlan plan) {
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordPlanMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordPlanMapper.java
index 2cdc6468e..e9304df05 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordPlanMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordPlanMapper.java
@@ -58,4 +58,7 @@ public interface RecordPlanMapper {
@Delete("DELETE FROM wvp_record_plan_item WHERE plan_id = #{planId}")
void cleanItems(@Param("planId") Integer planId);
+
+ @Select("select plan_id from wvp_record_plan_item where week_day = #{week} and start >= #{index} and stop <= #{index} group by plan_id")
+ List queryStart(@Param("week") int week, @Param("index") int index);
}
diff --git a/web_src/src/components/dialog/editRecordPlan.vue b/web_src/src/components/dialog/editRecordPlan.vue
index 1de115cca..4d425779a 100644
--- a/web_src/src/components/dialog/editRecordPlan.vue
+++ b/web_src/src/components/dialog/editRecordPlan.vue
@@ -176,10 +176,8 @@ export default {
let start = null;
let stop = null;
let result = []
- console.log("===================")
for (let i = 0; i < weekItem.length; i++) {
let item = weekItem[i]
- console.log(item)
if (item === '1') { // 表示选中
stop = i
if (start === null ) {
From 0f0be7d7c77d4382961ca0f48ee7d290efca0c3b Mon Sep 17 00:00:00 2001
From: 648540858 <648540858@qq.com>
Date: Fri, 29 Nov 2024 15:38:55 +0800
Subject: [PATCH 030/128] =?UTF-8?q?[=E5=BD=95=E5=88=B6=E8=AE=A1=E5=88=92]?=
=?UTF-8?q?=20=E5=AE=8C=E5=96=84=E5=92=8C=E4=BC=98=E5=8C=96=E5=BD=95?=
=?UTF-8?q?=E5=88=B6=E8=AE=A1=E5=88=92=E6=89=A7=E8=A1=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../gb28181/dao/CommonGBChannelMapper.java | 48 -------
.../iot/vmp/service/IRecordPlanService.java | 2 +-
.../vmp/service/impl/MediaServiceImpl.java | 2 +-
.../service/impl/RecordPlanServiceImpl.java | 132 ++++++++++++------
.../vmp/storager/dao/RecordPlanMapper.java | 7 +-
5 files changed, 93 insertions(+), 98 deletions(-)
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/CommonGBChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/CommonGBChannelMapper.java
index d14ba09c2..64f7dad85 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/CommonGBChannelMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/CommonGBChannelMapper.java
@@ -550,52 +550,4 @@ public interface CommonGBChannelMapper {
@Param("channelType") Integer channelType, @Param("online") Boolean online,
@Param("hasLink") Boolean hasLink);
- @Select("")
- List queryForRecordPlan(List planIdList);
}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/IRecordPlanService.java b/src/main/java/com/genersoft/iot/vmp/service/IRecordPlanService.java
index abec8e0cb..f3b34912c 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/IRecordPlanService.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/IRecordPlanService.java
@@ -27,5 +27,5 @@ public interface IRecordPlanService {
void cleanAll(Integer planId);
- boolean recording(String app, String stream);
+ Integer recording(String app, String stream);
}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
index 0959c7843..fcb7570fb 100755
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
@@ -209,7 +209,7 @@ public class MediaServiceImpl implements IMediaService {
@Override
public boolean closeStreamOnNoneReader(String mediaServerId, String app, String stream, String schema) {
boolean result = false;
- if (recordPlanService.recording(app, stream)) {
+ if (recordPlanService.recording(app, stream) != null) {
return false;
}
// 国标类型的流
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
index 4bcc945a0..ac146e867 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java
@@ -17,6 +17,7 @@ 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 com.google.common.base.Joiner;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
@@ -27,6 +28,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.*;
+import java.util.concurrent.TimeUnit;
@Service
@Slf4j
@@ -53,63 +55,94 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
@EventListener
public void onApplicationEvent(MediaDepartureEvent event) {
// 流断开,检查是否还处于录像状态, 如果是则继续录像
- if (recording(event.getApp(), event.getStream())) {
- // 重新拉起
-
+ Integer channelId = recording(event.getApp(), event.getStream());
+ if(channelId == null) {
+ return;
}
+ // 重新拉起
+ CommonGBChannel channel = channelMapper.queryById(channelId);
+ if (channel == null) {
+ log.warn("[录制计划] 流离开时拉起需要录像的流时, 发现通道不存在, id: {}", channelId);
+ return;
+ }
+ // 开启点播,
+ channelPlayService.play(channel, null, ((code, msg, streamInfo) -> {
+ if (code == InviteErrorCode.SUCCESS.getCode() && streamInfo != null) {
+ log.info("[录像] 流离开时拉起需要录像的流, 开启成功, 通道ID: {}", channel.getGbId());
+ recordStreamMap.put(channel.getGbId(), streamInfo);
+ } else {
+ recordStreamMap.remove(channelId);
+ log.info("[录像] 流离开时拉起需要录像的流, 开启失败, 十分钟后重试, 通道ID: {}", channel.getGbId());
+ }
+ }));
}
Map recordStreamMap = new HashMap<>();
- @Scheduled(cron = "0 */30 * * * *")
+// @Scheduled(cron = "0 */30 * * * *")
+ @Scheduled(fixedRate = 10, timeUnit = TimeUnit.MINUTES)
public void execution() {
- // 执行计划
+ log.info("[录制计划] 执行");
+ // 查询现在需要录像的通道Id
+ List startChannelIdList = queryCurrentChannelRecord();
- // 获取当前时间在一周内的序号
- LocalDateTime now = LocalDateTime.now();
- int week = now.getDayOfWeek().getValue();
- int index = now.getHour() * 2 + (now.getMinute() > 30?1:0);
- // 查询startTime等于现在的, 开始录像
- List startPlanList = recordPlanMapper.queryStart(week, index);
-
- Map channelMapWithoutRecord = new HashMap<>();
- if (startPlanList.isEmpty()) {
- // 停止所有正在录像的
- if(recordStreamMap.isEmpty()) {
- // 暂无录像任务
- return;
- }else {
- channelMapWithoutRecord.putAll(recordStreamMap);
+ if (startChannelIdList.isEmpty()) {
+ // 当前没有录像任务, 如果存在旧的正在录像的就移除
+ if(!recordStreamMap.isEmpty()) {
+ stopStreams(recordStreamMap.keySet(), recordStreamMap);
recordStreamMap.clear();
}
}else {
- channelMapWithoutRecord.putAll(recordStreamMap);
- // 获取所有的关联的通道
- List channelList = channelMapper.queryForRecordPlan(startPlanList);
- if (channelList.isEmpty()) {
- recordStreamMap.clear();
- }else {
- // 查找是否已经开启录像, 如果没有则开启录像
- for (CommonGBChannel channel : channelList) {
- if (recordStreamMap.get(channel.getGbId()) != null) {
- channelMapWithoutRecord.remove(channel.getGbId());
- }else {
+ // 当前存在录像任务, 获取正在录像中存在但是当前录制列表不存在的内容,进行停止; 获取正在录像中没有但是当前需录制的列表中存在的进行开启.
+ Set recordStreamSet = new HashSet<>(recordStreamMap.keySet());
+ startChannelIdList.forEach(recordStreamSet::remove);
+ if (!recordStreamSet.isEmpty()) {
+ // 正在录像中存在但是当前录制列表不存在的内容,进行停止;
+ stopStreams(recordStreamSet, recordStreamMap);
+ }
+
+ // 移除startChannelIdList中已经在录像的部分, 剩下的都是需要新添加的(正在录像中没有但是当前需录制的列表中存在的进行开启)
+ recordStreamMap.keySet().forEach(startChannelIdList::remove);
+ if (!startChannelIdList.isEmpty()) {
+ // 获取所有的关联的通道
+ List channelList = channelMapper.queryByIds(startChannelIdList);
+ if (!channelList.isEmpty()) {
+ // 查找是否已经开启录像, 如果没有则开启录像
+ for (CommonGBChannel channel : channelList) {
// 开启点播,
channelPlayService.play(channel, null, ((code, msg, streamInfo) -> {
if (code == InviteErrorCode.SUCCESS.getCode() && streamInfo != null) {
log.info("[录像] 开启成功, 通道ID: {}", channel.getGbId());
recordStreamMap.put(channel.getGbId(), streamInfo);
- channelMapWithoutRecord.remove(channel.getGbId(), streamInfo);
+ } else {
+ log.info("[录像] 开启失败, 十分钟后重试, 通道ID: {}", channel.getGbId());
}
}));
}
+ } else {
+ log.error("[录制计划] 数据异常, 这些关联的通道已经不存在了: {}", Joiner.on(",").join(startChannelIdList));
}
}
}
- // 结束录像
- if(!channelMapWithoutRecord.isEmpty()) {
- for (Integer channelId : channelMapWithoutRecord.keySet()) {
- StreamInfo streamInfo = channelMapWithoutRecord.get(channelId);
+ }
+
+ /**
+ * 获取当前时间段应该录像的通道Id列表
+ */
+ private List queryCurrentChannelRecord(){
+ // 获取当前时间在一周内的序号, 数据库存储的从第几个30分钟开始, 0-47, 包括首尾
+ LocalDateTime now = LocalDateTime.now();
+ int week = now.getDayOfWeek().getValue();
+ int index = now.getHour() * 2 + (now.getMinute() > 30?1:0);
+
+ // 查询现在需要录像的通道Id
+ return recordPlanMapper.queryRecordIng(week, index);
+ }
+
+ private void stopStreams(Collection channelIds, Map recordStreamMap) {
+ for (Integer channelId : channelIds) {
+ try {
+ StreamInfo streamInfo = recordStreamMap.get(channelId);
if (streamInfo == null) {
continue;
}
@@ -117,23 +150,25 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
MediaInfo mediaInfo = mediaServerService.getMediaInfo(streamInfo.getMediaServer(), streamInfo.getApp(), streamInfo.getStream());
if (mediaInfo.getReaderCount() == null || mediaInfo.getReaderCount() == 0) {
mediaServerService.closeStreams(streamInfo.getMediaServer(), streamInfo.getApp(), streamInfo.getStream());
- log.info("[录像] 停止, 通道ID: {}", channelId);
+ log.info("[录制计划] 停止, 通道ID: {}", channelId);
}
+ }catch (Exception e) {
+ log.error("[录制计划] 停止时异常", e);
+ }finally {
+ recordStreamMap.remove(channelId);
}
}
}
- // 系统启动时
-
-
@Override
- public boolean recording(String app, String stream) {
- for (StreamInfo streamInfo : recordStreamMap.values()) {
- if (streamInfo.getApp().equals(app) && streamInfo.getStream().equals(stream)) {
- return true;
+ public Integer recording(String app, String stream) {
+ for (Integer channelId : recordStreamMap.keySet()) {
+ StreamInfo streamInfo = recordStreamMap.get(channelId);
+ if (streamInfo != null && streamInfo.getApp().equals(app) && streamInfo.getStream().equals(stream)) {
+ return channelId;
}
}
- return false;
+ return null;
}
@Override
@@ -226,7 +261,12 @@ public class RecordPlanServiceImpl implements IRecordPlanService {
}else {
channelMapper.addRecordPlan(channelIds, planId);
}
- // TODO 更新录像队列
+ // 查看当前的待录制列表是否变化,如果变化,则调用录制计划马上开始录制
+ List currentChannelRecord = queryCurrentChannelRecord();
+ recordStreamMap.keySet().forEach(currentChannelRecord::remove);
+ if (!currentChannelRecord.isEmpty()) {
+ execution();
+ }
}
@Override
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordPlanMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordPlanMapper.java
index e9304df05..ae0649aa2 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordPlanMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordPlanMapper.java
@@ -59,6 +59,9 @@ public interface RecordPlanMapper {
@Delete("DELETE FROM wvp_record_plan_item WHERE plan_id = #{planId}")
void cleanItems(@Param("planId") Integer planId);
- @Select("select plan_id from wvp_record_plan_item where week_day = #{week} and start >= #{index} and stop <= #{index} group by plan_id")
- List queryStart(@Param("week") int week, @Param("index") int index);
+ @Select(" ")
+ List queryRecordIng(@Param("week") int week, @Param("index") int index);
}
From 6004c512284b8a831d6e57e03c8f47805186c781 Mon Sep 17 00:00:00 2001
From: 648540858 <648540858@qq.com>
Date: Mon, 9 Dec 2024 15:46:40 +0800
Subject: [PATCH 031/128] =?UTF-8?q?=E6=9B=B4=E6=96=B0README?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/README.md b/README.md
index bbe1975ee..1116581f7 100644
--- a/README.md
+++ b/README.md
@@ -8,16 +8,16 @@
[](https://github.com/xia-chu/ZLMediaKit/pulls)
-WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的开箱即用的网络视频平台,负责实现核心信令与设备管理后台部分,支持NAT穿透,支持海康、大华、宇视等品牌的IPC、NVR接入。支持国标级联,支持将不带国标功能的摄像机/直播流/直播推流转发到其他国标平台。
+WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的开箱即用的网络视频平台,负责实现核心信令与设备管理后台部分,支持NAT穿透,支持海康、大华、宇视等品牌的IPC、NVR接入。支持国标级联,支持将不带国标功能的摄像机/直播流/直播推流转发到其他国标平台。
流媒体服务基于@夏楚 ZLMediaKit [https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit)
播放器使用@dexter jessibuca [https://github.com/langhuihui/jessibuca/tree/v3](https://github.com/langhuihui/jessibuca/tree/v3)
-前端页面基于@Kyle MediaServerUI [https://gitee.com/kkkkk5G/MediaServerUI](https://gitee.com/kkkkk5G/MediaServerUI) 进行修改.
+前端页面基于@Kyle MediaServerUI [https://gitee.com/kkkkk5G/MediaServerUI](https://gitee.com/kkkkk5G/MediaServerUI) 进行修改.
# 应用场景:
支持浏览器无插件播放摄像头视频。
支持国标设备(摄像机、平台、NVR等)设备接入
-支持非国标(onvif, rtsp, rtmp,直播设备等等)设备接入,充分利旧。
+支持非国标(onvif, rtsp, rtmp,直播设备等等)设备接入,充分利旧。
支持国标级联。多平台级联。跨网视频预览。
支持跨网网闸平台互联。
@@ -43,7 +43,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git


-# 功能特性
+# 功能特性
- [X] 集成web界面
- [X] 兼容性良好
- [X] 接入设备
@@ -97,10 +97,10 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
- [X] GPS订阅与通知(直播推流)
- [X] 语音对讲
- [X] 支持同时级联到多个上级平台
-- [X] 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题;
+- [X] 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题;
- [X] 多流媒体节点,自动选择负载最低的节点使用。
- [X] 支持启用udp多端口模式, 提高udp模式下媒体传输性能;
-- [X] 支持公网部署;
+- [X] 支持公网部署;
- [X] 支持wvp与zlm分开部署,提升平台并发能力
- [X] 支持拉流RTSP/RTMP,分发为各种流格式,或者推送到其他国标平台
- [X] 支持推流RTSP/RTMP,分发为各种流格式,或者推送到其他国标平台
@@ -110,6 +110,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
- [X] 支持打包可执行jar和war
- [X] 支持跨域请求,支持前后端分离部署
- [X] 支持Mysql,Postgresql,金仓等数据库
+- [X] 支持录制计划, 根据设定的时间对通道进行录制. 暂不支持将录制的内容转发到国标上级
- [X] 支持Onvif, 目前付费提供, 永久免费试用包在知识星球获取
- [X] 支持国标28181-2022协议, 目前付费提供, 永久免费试用包在知识星球获取
@@ -122,7 +123,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
# 授权协议
本项目自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; 由于使用本项目而产生的商业纠纷或侵权行为一概与本项目及开发者无关,请自行承担法律风险。 在使用本项目代码时,也应该在授权协议中同时表明本项目依赖的第三方库的协议
-# 技术支持
+# 技术支持
[知识星球](https://t.zsxq.com/0d8VAD3Dm)专栏列表:,
- [使用入门系列一:WVP-PRO能做什么](https://t.zsxq.com/0dLguVoSp)
@@ -134,7 +135,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
感谢作者[dexter langhuihui](https://github.com/langhuihui) 开源这么好用的WEB播放器。
感谢作者[Kyle](https://gitee.com/kkkkk5G) 开源了好用的前端页面
感谢各位大佬的赞助以及对项目的指正与帮助。包括但不限于代码贡献、问题反馈、资金捐赠等各种方式的支持!以下排名不分先后:
-[lawrencehj](https://github.com/lawrencehj) [Smallwhitepig](https://github.com/Smallwhitepig) [swwhaha](https://github.com/swwheihei)
+[lawrencehj](https://github.com/lawrencehj) [Smallwhitepig](https://github.com/Smallwhitepig) [swwhaha](https://github.com/swwheihei)
[hotcoffie](https://github.com/hotcoffie) [xiaomu](https://github.com/nikmu) [TristingChen](https://github.com/TristingChen)
[chenparty](https://github.com/chenparty) [Hotleave](https://github.com/hotleave) [ydwxb](https://github.com/ydwxb)
[ydpd](https://github.com/ydpd) [szy833](https://github.com/szy833) [ydwxb](https://github.com/ydwxb) [Albertzhu666](https://github.com/Albertzhu666)
From 0f12c240e84826fa0c1bf9d592ba85eb9751888b Mon Sep 17 00:00:00 2001
From: 648540858 <648540858@qq.com>
Date: Tue, 10 Dec 2024 21:59:40 +0800
Subject: [PATCH 032/128] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BD=95=E5=88=B6?=
=?UTF-8?q?=E8=AE=A1=E5=88=92=E5=85=B3=E8=81=94=E5=85=A8=E9=83=A8=E9=80=9A?=
=?UTF-8?q?=E9=81=93=E6=8F=90=E7=A4=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
web_src/src/components/dialog/linkChannelRecord.vue | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/web_src/src/components/dialog/linkChannelRecord.vue b/web_src/src/components/dialog/linkChannelRecord.vue
index 2046c1eb7..d30e25bcf 100755
--- a/web_src/src/components/dialog/linkChannelRecord.vue
+++ b/web_src/src/components/dialog/linkChannelRecord.vue
@@ -37,8 +37,8 @@
按设备添加
按设备移除
- 全部添加
- 全部移除
+ 添加所有通道
+ 移除所有通道
刷新