Commit 05a324a07a1848b020a06d671680907f8b92207d

Authored by mk1990
Committed by GitHub
2 parents a0537d74 db865cc9

Merge branch '648540858:wvp-28181-2.0' into wvp-28181-2.0

src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java
... ... @@ -136,4 +136,7 @@ public class SsrcConfig {
136 136 this.notUsed = notUsed;
137 137 }
138 138  
  139 + public boolean checkSsrc(String ssrcInResponse) {
  140 + return !isUsed.contains(ssrcInResponse);
  141 + }
139 142 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
... ... @@ -93,8 +93,8 @@ public interface ISIPCommander {
93 93 * @param device 视频设备
94 94 * @param channelId 预览通道
95 95 */
96   - void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
97   -
  96 + void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent);
  97 +
98 98 /**
99 99 * 请求回放视频流
100 100 *
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
... ... @@ -343,7 +343,7 @@ public class SIPCommander implements ISIPCommander {
343 343 */
344 344 @Override
345 345 public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
346   - ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) {
  346 + ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {
347 347 String streamId = ssrcInfo.getStream();
348 348 try {
349 349 if (device == null) return;
... ... @@ -436,6 +436,7 @@ public class SIPCommander implements ISIPCommander {
436 436 // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值
437 437 streamSession.put(device.getDeviceId(), channelId ,"play", streamId, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play);
438 438 streamSession.put(device.getDeviceId(), channelId ,"play", e.dialog);
  439 + okEvent.response(e);
439 440 });
440 441  
441 442  
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
... ... @@ -202,6 +202,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
202 202 String platformId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
203 203 String deviceID = XmlUtil.getText(rootElement, "DeviceID");
204 204 ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
  205 + if (platform == null)return;
205 206 SubscribeInfo subscribeInfo = new SubscribeInfo(evt, platformId);
206 207 if (evt.getServerTransaction() == null) {
207 208 ServerTransaction serverTransaction = platform.getTransport().equals("TCP") ? tcpSipProvider.getNewServerTransaction(evt.getRequest())
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
... ... @@ -222,7 +222,24 @@ public class XmlUtil {
222 222 // 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1
223 223 deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")) == 1?1:0);
224 224 }
225   - deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID"));
  225 + /**
  226 + * 行政区划展示设备树与业务分组展示设备树是两种不同的模式
  227 + * 行政区划展示设备树 各个目录之间主要靠deviceId做关联,摄像头通过CivilCode指定其属于那个行政区划;都是不超过十位的编号; 结构如下:
  228 + * 河北省
  229 + * --> 石家庄市
  230 + * --> 摄像头
  231 + * --> 正定县
  232 + * --> 摄像头
  233 + * --> 摄像头
  234 + *
  235 + * 业务分组展示设备树是顶级是业务分组,其下的虚拟组织靠BusinessGroupID指定其所属的业务分组;摄像头通过ParentId来指定其所属于的虚拟组织:
  236 + * 业务分组
  237 + * --> 虚拟组织
  238 + * --> 摄像头
  239 + * --> 虚拟组织
  240 + * --> 摄像头
  241 + * --> 摄像头
  242 + */
226 243 String parentId = XmlUtil.getText(itemDevice, "ParentID");
227 244 if (parentId != null) {
228 245 if (parentId.contains("/")) {
... ...
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
... ... @@ -46,7 +46,7 @@ public interface IMediaServerService {
46 46  
47 47 SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck);
48 48  
49   - SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback);
  49 + SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback);
50 50  
51 51 void closeRTPServer(String deviceId, String channelId, String ssrc);
52 52  
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
... ... @@ -118,11 +118,11 @@ public class MediaServerServiceImpl implements IMediaServerService {
118 118  
119 119 @Override
120 120 public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck) {
121   - return openRTPServer(mediaServerItem, streamId, ssrcCheck,false);
  121 + return openRTPServer(mediaServerItem, streamId, null, ssrcCheck,false);
122 122 }
123 123  
124 124 @Override
125   - public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback) {
  125 + public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback) {
126 126 if (mediaServerItem == null || mediaServerItem.getId() == null) {
127 127 return null;
128 128 }
... ... @@ -135,10 +135,14 @@ public class MediaServerServiceImpl implements IMediaServerService {
135 135 return null;
136 136 }else {
137 137 String ssrc = null;
138   - if (isPlayback) {
139   - ssrc = ssrcConfig.getPlayBackSsrc();
  138 + if (presetSsrc != null) {
  139 + ssrc = presetSsrc;
140 140 }else {
141   - ssrc = ssrcConfig.getPlaySsrc();
  141 + if (isPlayback) {
  142 + ssrc = ssrcConfig.getPlayBackSsrc();
  143 + }else {
  144 + ssrc = ssrcConfig.getPlaySsrc();
  145 + }
142 146 }
143 147  
144 148 if (streamId == null) {
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
... ... @@ -39,6 +39,7 @@ import org.springframework.stereotype.Service;
39 39 import org.springframework.util.ResourceUtils;
40 40 import org.springframework.web.context.request.async.DeferredResult;
41 41  
  42 +import javax.sip.ResponseEvent;
42 43 import java.io.FileNotFoundException;
43 44 import java.math.BigDecimal;
44 45 import java.util.*;
... ... @@ -256,7 +257,7 @@ public class PlayServiceImpl implements IPlayService {
256 257 }
257 258 }
258 259 }, userSetting.getPlayTimeout());
259   -
  260 + final String ssrc = ssrcInfo.getSsrc();
260 261 cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
261 262 logger.info("收到订阅消息: " + response.toJSONString());
262 263 timer.cancel();
... ... @@ -264,10 +265,38 @@ public class PlayServiceImpl implements IPlayService {
264 265 onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid);
265 266 hookEvent.response(mediaServerItemInuse, response);
266 267 }, (event) -> {
  268 + ResponseEvent responseEvent = (ResponseEvent)event.event;
  269 + String contentString = new String(responseEvent.getResponse().getRawContent());
  270 + // 获取ssrc
  271 + int ssrcIndex = contentString.indexOf("y=");
  272 + // 检查是否有y字段
  273 + if (ssrcIndex >= 0) {
  274 + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
  275 + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
  276 + if (!ssrc.equals(ssrcInResponse) && device.isSsrcCheck()) { // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
  277 + // 查询 ssrcInResponse 是否可用
  278 + if (mediaServerItem.isRtpEnable() && !mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
  279 + // ssrc 不可用
  280 + // 释放ssrc
  281 + mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
  282 + streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
  283 + event.msg = "下级自定义了ssrc,但是此ssrc不可用";
  284 + event.statusCode = 400;
  285 + errorEvent.response(event);
  286 + return;
  287 + }
  288 + // 关闭rtp server
  289 + mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
  290 + // 重新开启ssrc server
  291 + mediaServerService.openRTPServer(mediaServerItem, finalSsrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false);
  292 + }
  293 + }
  294 + }, (event) -> {
267 295 timer.cancel();
268 296 mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
269 297 // 释放ssrc
270 298 mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
  299 +
271 300 streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
272 301 errorEvent.response(event);
273 302 });
... ...
web_src/src/components/channelList.vue
... ... @@ -93,7 +93,7 @@
93 93 <el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.subCount > 0 || scope.row.parental === 1"
94 94 @click="changeSubchannel(scope.row)">查看
95 95 </el-button>
96   - <el-button size="mini" icon="el-icon-video-camera" type="primary" @click="queryRecords(scope.row)">设备录
  96 + <el-button size="mini" icon="el-icon-video-camera" type="primary" @click="queryRecords(scope.row)">设备录
97 97 </el-button>
98 98 <!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> -->
99 99 </el-button-group>
... ...
web_src/src/components/dialog/catalogEdit.vue
... ... @@ -12,25 +12,22 @@
12 12 >
13 13 <div id="shared" style="margin-top: 1rem;margin-right: 100px;">
14 14 <el-form ref="form" :rules="rules" :model="form" label-width="140px" >
  15 +<!-- <el-form-item >-->
  16 +<!-- 建议的类型:-->
  17 +<!-- <br/>-->
  18 +<!-- &emsp;&emsp;行政区划(可选2位/4位/6位/8位/10位数字,例如:130432,表示河北省邯郸市广平县)-->
  19 +<!-- <br/>-->
  20 +<!-- &emsp;&emsp;业务分组(第11、12、13位215,例如:34020000002150000001)-->
  21 +<!-- <br/>-->
  22 +<!-- &emsp;&emsp;虚拟组织(第11、12、13位216,例如:34020000002160000001)-->
  23 +<!-- </el-form-item>-->
15 24 <el-form-item label="节点编号" prop="id" >
16   - <el-tooltip class="item" effect="dark" content="" placement="top-start">
17   - <div slot="content">
18   - 建议的类型:
19   - <br/>
20   - &emsp;&emsp;行政区划(可选2位/4位/6位/8位/10位数字,例如:130432,表示河北省邯郸市广平县)
21   - <br/>
22   - &emsp;&emsp;业务分组(第11、12、13位215,例如:34020000002150000001)
23   - <br/>
24   - &emsp;&emsp;虚拟组织(第11、12、13位216,例如:34020000002160000001)
25   - </div>
26   - <el-input v-model="form.id" :disabled="isEdit"></el-input>
27   - </el-tooltip>
  25 + <el-input v-model="form.id" :disabled="isEdit" clearable></el-input>
28 26 </el-form-item>
29 27 <el-form-item label="节点名称" prop="name">
30 28 <el-input v-model="form.name" clearable></el-input>
31 29 </el-form-item>
32 30  
33   -
34 31 <el-form-item>
35 32 <div style="float: right;">
36 33 <el-button type="primary" @click="onSubmit" >确认</el-button>
... ... @@ -65,13 +62,14 @@ export default {
65 62 },
66 63 rules: {
67 64 name: [{ required: true, message: "请输入名称", trigger: "blur" }],
68   - id: [{ required: true, message: "请输入id", trigger: "blur" }]
  65 + id: [{ required: true, message: "请输入ID", trigger: "blur" }]
69 66 },
70 67 };
71 68 },
72 69 methods: {
73 70 openDialog: function (isEdit, id, name, parentId, callback) {
74 71 console.log("parentId: " + parentId)
  72 + console.log(this.form)
75 73 this.isEdit = isEdit;
76 74 this.form.id = id;
77 75 this.form.name = name;
... ... @@ -105,8 +103,14 @@ export default {
105 103 });
106 104 },
107 105 close: function () {
  106 + this.isEdit = false;
  107 + this.form.id = null;
  108 + this.form.name = null;
  109 + this.form.platformId = null;
  110 + this.form.parentId = null;
  111 + this.callback = null;
108 112 this.showDialog = false;
109   - this.$refs.form.resetFields();
  113 + console.log(this.form)
110 114 },
111 115 },
112 116 };
... ...