diff --git a/.github/ISSUE_TEMPLATE/solve.md b/.github/ISSUE_TEMPLATE/solve.md index 75a0eed76..473dbd19f 100644 --- a/.github/ISSUE_TEMPLATE/solve.md +++ b/.github/ISSUE_TEMPLATE/solve.md @@ -16,7 +16,7 @@ assignees: '' - 5. 是否使用https - 6. 方便的话提供下使用的设备品牌或平台 - 7. 你做过哪些尝试 - - 8. 代码更新时间 + - 8. 代码更新时间(旧版本请更新最新版本代码测试) **内容描述:** diff --git a/README.md b/README.md index 4f09aba7c..22551a054 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,6 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git ![3](doc/_media/3.png "3.png") ![3-1](doc/_media/3-1.png "3-1.png") ![3-2](doc/_media/3-2.png "3-2.png") -![3-3](doc/_media/3-3.png "3-3.png") ![build_1](https://images.gitee.com/uploads/images/2022/0304/101919_ee5b8c79_1018729.png "2022-03-04_10-13.png") # 功能特性 diff --git a/doc/README.md b/doc/README.md index cdf3a0810..c5c4006db 100644 --- a/doc/README.md +++ b/doc/README.md @@ -3,11 +3,15 @@ > 开箱即用的28181协议视频平台 # 概述 -- WVP-PRO基于GB/T 28181-2016标准实现的流媒体平台,依托优秀的开源流媒体服务[ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit),提供完善丰富的功能。 + +- WVP-PRO基于GB/T + 28181-2016标准实现的流媒体平台,依托优秀的开源流媒体服务[ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit) + ,提供完善丰富的功能。 - GB/T 28181-2016 中文标准名称是《公共安全视频监控联网系统信息传输、交换、控制技术要求》是监控领域的国家标准。大量应用于政府视频平台。 - 通过28181协议你可以将IPC摄像头接入平台,可以观看也可以使用28181/rtsp/rtmp/flv等协议将视频流分发到其他平台。 # 特性 + - 实现标准的28181信令,兼容常见的品牌设备,比如海康、大华、宇视等品牌的IPC、NVR以及平台。 - 支持将国标设备级联到其他国标平台,也支持将不支持国标的设备的图像或者直播推送到其他国标平台 - 前端完善,自带完整前端页面,无需二次开发可直接部署使用。 @@ -15,92 +19,94 @@ - 支持多流媒体节点负载均衡。 # 付费社群 + [![社群](_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm) > 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接退款,大家不需要有顾虑,来白嫖三天也不是不可以。 # 我们实现了哪些国标功能 + **作为上级平台** + - [X] 注册 - [X] 注销 - [X] 实时视音频点播 - [X] 设备控制 - - [X] 云台控制 - - [X] 远程启动 - - [X] 录像控制 - - [X] 报警布防/撤防 - - [X] 报警复位 - - [X] 强制关键帧 - - [X] 拉框放大 - - [X] 拉框缩小 - - [X] 看守位控制 - - [X] 设备配置 + - [X] 云台控制 + - [X] 远程启动 + - [X] 录像控制 + - [X] 报警布防/撤防 + - [X] 报警复位 + - [X] 强制关键帧 + - [X] 拉框放大 + - [X] 拉框缩小 + - [X] 看守位控制 + - [X] 设备配置 - [X] 报警事件通知和分发 - [X] 设备目录订阅 - [X] 网络设备信息查询 - - [X] 设备目录查询 - - [X] 设备状态查询 - - [X] 设备配置查询 - - [X] 设备预置位查询 + - [X] 设备目录查询 + - [X] 设备状态查询 + - [X] 设备配置查询 + - [X] 设备预置位查询 - [X] 状态信息报送 - [X] 设备视音频文件检索 - [X] 历史视音频的回放 - - [X] 播放 - - [X] 暂停 - - [X] 进/退 - - [X] 停止 + - [X] 播放 + - [X] 暂停 + - [X] 进/退 + - [X] 停止 - [X] 视音频文件下载 - [X] 校时 - [X] 订阅和通知 - - [X] 事件订阅 - - [X] 移动设备位置订阅 - - [X] 报警订阅 - - [X] 目录订阅 + - [X] 事件订阅 + - [X] 移动设备位置订阅 + - [X] 报警订阅 + - [X] 目录订阅 - [X] 语音广播 - [X] 语音喊话 **作为下级平台** + - [X] 注册 - [X] 注销 - [X] 实时视音频点播 - [X] 设备控制 - - [X] 云台控制 - - [ ] 远程启动 - - [X] 录像控制 - - [X] 报警布防/撤防 - - [X] 报警复位 - - [X] 强制关键帧 - - [X] 拉框放大 - - [X] 拉框缩小 - - [X] 看守位控制 - - [ ] 设备配置 + - [X] 云台控制 + - [ ] 远程启动 + - [X] 录像控制 + - [X] 报警布防/撤防 + - [X] 报警复位 + - [X] 强制关键帧 + - [X] 拉框放大 + - [X] 拉框缩小 + - [X] 看守位控制 + - [ ] 设备配置 - [ ] 报警事件通知和分发 - [X] 设备目录订阅 - [X] 网络设备信息查询 - - [X] 设备目录查询 - - [X] 设备状态查询 - - [ ] 设备配置查询 - - [X] 设备预置位查询 + - [X] 设备目录查询 + - [X] 设备状态查询 + - [ ] 设备配置查询 + - [X] 设备预置位查询 - [X] 状态信息报送 - [X] 设备视音频文件检索 - [X] 历史视音频的回放 - - [X] 播放 - - [x] 暂停 - - [x] 进/退 - - [x] 停止 + - [X] 播放 + - [x] 暂停 + - [x] 进/退 + - [x] 停止 - [X] 视音频文件下载 - [ ] ~~校时~~ - [X] 订阅和通知 - - [X] 事件订阅 - - [X] 移动设备位置订阅 - - [ ] 报警订阅 - - [X] 目录订阅 + - [X] 事件订阅 + - [X] 移动设备位置订阅 + - [ ] 报警订阅 + - [X] 目录订阅 - [X] 语音广播 - [X] 语音喊话 - - - # 社区 + 代码目前托管在GitHub和Gitee,Gitee目前作为加速仓库使用,不接受issue。 GitHub: [https://github.com/648540858/wvp-GB28181-pro](https://github.com/648540858/wvp-GB28181-pro) Gitee: [https://gitee.com/pan648540858/wvp-GB28181-pro](https://gitee.com/pan648540858/wvp-GB28181-pro) \ No newline at end of file diff --git a/doc/_content/ability/_media/img_16.png b/doc/_content/ability/_media/img_16.png index b09e8cd39..5a27be09b 100644 Binary files a/doc/_content/ability/_media/img_16.png and b/doc/_content/ability/_media/img_16.png differ diff --git a/doc/_content/ability/_media/img_17.png b/doc/_content/ability/_media/img_17.png index 02b303e36..483d522a3 100644 Binary files a/doc/_content/ability/_media/img_17.png and b/doc/_content/ability/_media/img_17.png differ diff --git a/doc/_content/ability/_media/img_18.png b/doc/_content/ability/_media/img_18.png index 5ca4faf9d..3b33b2189 100644 Binary files a/doc/_content/ability/_media/img_18.png and b/doc/_content/ability/_media/img_18.png differ diff --git a/doc/_content/ability/auto_play.md b/doc/_content/ability/auto_play.md index 5259e0b52..2ce30120f 100644 --- a/doc/_content/ability/auto_play.md +++ b/doc/_content/ability/auto_play.md @@ -1,2 +1,3 @@ + # 自动点播 diff --git a/doc/_content/ability/cascade.md b/doc/_content/ability/cascade.md index 7942bda4b..0766a7a93 100644 --- a/doc/_content/ability/cascade.md +++ b/doc/_content/ability/cascade.md @@ -1,34 +1,56 @@ + # 国标级联的使用 + 国标28181不同平台之间支持两种连接方式,平级和上下级,WVP目前支持向上级级联。 + ## 1 接入平台 + ### 1.1 wvp-pro + #### 1.1.1 wvp-pro管理页面点击添加 - ![cascade1](_media/cascade1.png) + +![cascade1](_media/cascade1.png) + #### 1.1.2 填入wvp-pro上级平台信息 - ![cascade1](_media/img_4.png) - ![cascade1](_media/img_5.png) + +![cascade1](_media/img_4.png) +![cascade1](_media/img_5.png) + #### 1.1.3 编辑wvp-pro上级设备信息,开启订阅 - ![cascade1](_media/img_6.png) + +![cascade1](_media/img_6.png) + ### 1.2 大华平台 + ### 1.3 海康平台 + ### 1.4 liveGBS -#### 1.4.1. wvp-pro管理页面点击添加 - ![添加](_media/cascade1.png) -#### 1.4.2. 填入liveGBS平台信息 - ![填入liveGBS平台信息1](_media/cascade2.png) - ![填入liveGBS平台信息2](_media/cascade3.png) -#### 1.4.3. 编辑liveGBS设备信息,开启目录订阅 - ![cascade1](_media/cascade4.png) -#### 1.4.4. 编辑liveGBS设备信息,开启GPS订阅 - ![cascade1](_media/img_7.png) + +#### 1.4.1. wvp-pro管理页面点击添加 + +![添加](_media/cascade1.png) + +#### 1.4.2. 填入liveGBS平台信息 + +![填入liveGBS平台信息1](_media/cascade2.png) +![填入liveGBS平台信息2](_media/cascade3.png) + +#### 1.4.3. 编辑liveGBS设备信息,开启目录订阅 + +![cascade1](_media/cascade4.png) + +#### 1.4.4. 编辑liveGBS设备信息,开启GPS订阅 + +![cascade1](_media/img_7.png) ## 2 添加目录与通道 + 1. 级联平台添加目录信息 ![cascade1](_media/img_1.png) 2. 为目录添加通道 ![cascade1](_media/img_2.png) 3. 设置默认流目录 -如果需要后续自动生成的流信息都在某一个节点下,可以在对应节点右键设置为默认 + 如果需要后续自动生成的流信息都在某一个节点下,可以在对应节点右键设置为默认 ![cascade1](_media/img_3.png) diff --git a/doc/_content/ability/cascade2.md b/doc/_content/ability/cascade2.md index 7bd6c7929..00f4fcb59 100644 --- a/doc/_content/ability/cascade2.md +++ b/doc/_content/ability/cascade2.md @@ -1,18 +1,71 @@ + # 国标级联的使用 + 国标28181不同平台之间支持两种连接方式,平级和上下级,WVP目前支持向上级级联。 + ## 添加上级平台 + 在国标级联页面点击“添加”按钮,以推送到上级WVP为例子,参看[接入设备](./_content/ability/device.md) ![cascade17](_media/img_17.png) -点击保存可以在上级的国标通道列表看到新增加的设备; + +1. 名称 + 上级平台看到的下级平台名称; +2. 本地IP + 本地连接上级使用的具体哪个网卡; +3. SIP认证用户名 + 可以设置为与"设备国标编号"一致; +4. 注册周期 + 间隔多久发起一次注册,单位秒; +5. 心跳周期 + 间隔多久发送一次心跳,一般上级平台三次收不到心跳就会认为下级离线了, 所以建议{心跳周期}x3 < 注册周期; +6. SDP发流IP + 调用媒体节点发送视频流给上级时,使用的本地IP; +7. 信令传输 + 信令传输模式,支持udp和TCP,没有特殊需求,默认UDP即可; +8. 目录分组 + 上级发送"CATALOG"消息查询通道信息,每一条消息中携带几条通道信息,默认为1,增大该值,可以加快通道发送速度; +9. 字符集 + 发送给上级"MESSAGE"消息中的消息体使用的编码格式,国标28181-2016默认为GB2312; +10. 行政区划 + 如果勾选"其他选项/推送平台信息"选项,会给上级推送平台信息,这里就是平台的行政区划信息 +11. 平台厂商 + 如果勾选"其他选项/推送平台信息"选项,会给上级推送平台信息,这里就是平台的平台厂商信息 +12. 平台型号 + 如果勾选"其他选项/推送平台信息"选项,会给上级推送平台信息,这里就是平台的平台型号信息 +13. 平台安装地址 + 如果勾选"其他选项/推送平台信息"选项,会给上级推送平台信息,这里就是平台的平台安装地址信息 +14. 其他选项 + - RTCP保活 + 在上级的流传输模式为UDP时,因为UDP的无状态特性,会无法知道上级是否在正常收流,启用RTCP保活时,就可以主动发送RTCP消息确认上级是否在正常收流, + 异常情况下,可以下级主动停止发流; + - 消息通道 + 支持通过报警消息给上级级WVP推送消息,消息内容由redis消息发送给wvp,wvp编辑成报警消息发送给上级; + - 主动推送通道 + WVP模拟一条目录订阅信息,然后在共享通道变化时,发送CATAOLOG事件给上级,通知具体的通道变化, + 目前支持的状态有: 状态改变事件 ON:上线,OFF:离线,VLOST:视频丢失,DEFECT:故障,ADD:增加,DEL:删除,UPDATE:更新; + - 推送平台信息 + 勾选此项,上级收到的通道信息中会多出一个平台信息的通道.内容在平台的编辑中修改; + - 推送分组信息 + 勾选此项,如果你共享的通道分配了具体的业务分组以及虚拟组织,那么上级收到的通道中会包括业务分组以及虚拟组织节点信息; + - 推送行政区划 + 勾选此项,如果你共享的通道分配了具体的行政区划,那么上级收到的通道中会包括行政区划信息; + 国标级联列表出现了级联的这个平台;同时状态显示为在线,如果状态为离线那么可能是你的服务信息配置有误或者网络不通。 订阅信息列有三个图标,表示上级开启订阅,从左到右依次是:报警订阅,目录订阅,移动位置订阅。 -## 推送通道 -点击你要推送的平台的“选择通道”按钮。 + +## 通道共享 + +点击你要推送的平台的“通道共享”按钮。 ![cascade18](_media/img_18.png) -- **页面结构** - - 左侧为目录结构 - 选择未分配,则右侧显示待分配的通道,可以点击“添加按钮”,在弹窗中选择要放置的位置,保存后即可添加通道成功 - 选择其他的目录可以看到已经分配在这个目录下的通道,可以对其进行删除后重新在未分配中去分配。 - - 右侧为数据展示以及操作 - 国标通道栏内为来自其他国标设备/平台的通道;直播流通道为来自推流/拉流代理的通道。 \ No newline at end of file + +1. 添加状态选择"未共享"可以将具体的通道共享给上级; +2. 添加状态选择"已共享"可以看到已经共享的通道,并且支持为这个通道在这个平台设备专门的名称和编号; +3. 点击"按设备添加"可以将某个国标设备下的所有通道共享给上级; +4. 点击"按设备移除"可以将某个国标设备下的所有通道取消共享给上级; +5. 点击"全部添加"可以将所有通道共享给上级; +6. 点击"全部移除"可以将所有通道共享给上级; + +## 推送通道 + +WVP会将所有通道信息按照目录订阅消息通知形式,发送ADD事件给上级. \ No newline at end of file diff --git a/doc/_content/ability/channel.md b/doc/_content/ability/channel.md new file mode 100644 index 000000000..adfe0ee76 --- /dev/null +++ b/doc/_content/ability/channel.md @@ -0,0 +1,22 @@ +# 通道管理 + +通道管理为了对已经分配国标编号的通道进行统一的行政区划和业务分组管理,国标中对于组织结构有两种表示方式,一种是按照行政区划,一种是业务分组+虚拟组织的方式. +行政区划结构固定,比如: 北京/市辖区/昌平区, 通道可以挂载道何一级行政区划下. 业务分组比较灵活, 可以按照自己的随意取名, +但是通道只能放在业务分组下的虚拟组织里,不能放在业务分组下. + +## 行政区划 + +左侧树结构为行政区划结构, 通过数据鼠标右键可以操作,包括: 刷新节点,新建节点,编辑节点,删除节点,添加设备( +可以将某个国标设备下的通道全部添加道某一个节点下),移除设备(可以将某个国标设备下的通道全部从这个节点移除) +右侧伪通道列表, 对于非国标接入的设备只有配置了国标编号后才可以在这里进行操作, 添加状态选择"未添加"可以进行添加操作,选择" +已添加"进行移除操作 +![刷新](_media/img_21.png) + +## 业务分组 + +左侧树结构为业务分组结构, 通过数据鼠标右键可以操作,包括: 刷新节点,新建节点,编辑节点,删除节点,添加设备( +可以将某个国标设备下的通道全部添加道某一个节点下),移除设备(可以将某个国标设备下的通道全部从这个节点移除) +业务分组下不能挂载设备,所以没有选择该节点的单选框. +右侧伪通道列表, 对于非国标接入的设备只有配置了国标编号后才可以在这里进行操作, 添加状态选择"未添加"可以进行添加操作,选择" +已添加"进行移除操作 +![刷新](_media/img_22.png) \ No newline at end of file diff --git a/doc/_content/ability/cloud_record.md b/doc/_content/ability/cloud_record.md index 0aa55e1e5..716a614fe 100644 --- a/doc/_content/ability/cloud_record.md +++ b/doc/_content/ability/cloud_record.md @@ -1,8 +1,13 @@ + # 云端录像 + +![云端录像](_media/img_23.png) 云端录像是对录制在zlm服务下的录像文件的管理,录像的文件路径默认在ZLM/www/record下。 -如果你需要24小时的录像,目前有一个这种方案,可以参考[7*24不间断录像](./_content/ability/continuous_recording.md)。 -1. 云段录像支持录像文件的查看,播放(可能因为编码的原因导致无法播放); -2. 支持录像的下载; -3. 支持录像的合并下载; -功能没有太多特殊的地方就不一一介绍了,大家自行体验吧。 + +- 国标设备是否录像: 可以再WVP的配置中user-settings.record-sip设置为true那么每次点播以及录像回放都会录像; +- 推流设备是否录像: 可以再WVP的配置中user-settings.record-push-live设置为true; +- 拉流代理的是否录像: 在添加和编辑拉流代理时可以指定, 每次点播都会进行录像 +- 录像文件存储路径配置: 可以修改media.record-path来修改录像路径,但是如果有旧的录像文件,请不要迁移,因为数据库记录了每一个录像的绝对路径,一旦修改会造成找到文件,无法定时移除以及播放 +- 录像保存时间: 可以修改media.record-day来修改录像保存时间,单位是天; + diff --git a/doc/_content/ability/continuous_broadcast.md b/doc/_content/ability/continuous_broadcast.md index 7af3afeb0..06708e779 100644 --- a/doc/_content/ability/continuous_broadcast.md +++ b/doc/_content/ability/continuous_broadcast.md @@ -1,9 +1,13 @@ # 语音对讲 + ## 流程和原理 + 语音对讲在国标28181-2016中分为broadcast(广播)和talk(对讲)两种模式,broadcast模式是从服务端把音频传送到设备端,是单向的, 需要结合点播视频来实现双向对讲,talk模式支持双向,不过wvp只处理了和broadcast一样的把音频传递设备,这样两种模式可以使用一样的逻辑处理即可。 不同的设备对于两种模式的支持不同且通常差异很大,不同的设备对同一个设备的支持也有一些不同,所以语音对讲中的兼容和适配也是问题最多的。talk模式因为在国标28181-2022中已经移除,所以这里不再讨论它了。 + ### 1. broadcast模式流程 + ```plantuml @startuml "WVP-PRO" -> "设备": 语音广播通知 @@ -16,24 +20,33 @@ "ZLMediaKit" -> "设备": 向设备发送语音流 @enduml ``` + 与点播的流程不同的是,这里的invite消息是由设备发送给wvp的,wvp按照invite协商的方式给设备推送语音流,所有对讲的使用那种方式(UDP/TCP被动/TCP主动)传输语音流由设备决定 + ## 使用条件与限制 -因为invite消息是由设备发送给wvp的,这决定了发送语音流的方式,这也就决定了有的设备不能用于公网对讲,比如大部分的海康设备只支持udp方式收流(目前新版的海康设备已经在着手解决这个问题),那么wvp发流时只能按照sdp中指定的ip端口发流,所以如果wvp在公网,设备在内网中,那么wvp无法连接设备提供的IP,发流也就失败了。 + +因为invite消息是由设备发送给wvp的,这决定了发送语音流的方式,这也就决定了有的设备不能用于公网对讲,比如大部分的海康设备只支持udp方式收流( +目前新版的海康设备已经在着手解决这个问题),那么wvp发流时只能按照sdp中指定的ip端口发流,所以如果wvp在公网,设备在内网中,那么wvp无法连接设备提供的IP,发流也就失败了。 与海康不同的,大华以及很多执法记录仪厂商是支持tcp主动方式取流的,这样是可以实现公网对讲的。 - ## 使用ffmpeg快速测试 + 由于浏览器对于音频的采集需要网页支持https才可以,所以如果想要实现网页音频对讲,那么你必须给wvp和zlm配置证书以使用https。 测试阶段如果只是想测试功能可以用ffmpeg来模拟语音流,推送到wvp后可以实现把音频文件推送到摄像头。 测试命令格式如下: + ```shell ffmpeg -re -i {音频文件} -acodec pcm_alaw -ar 8000 -ac 1 -f rtsp 'rtsp://{zlm的IP}:{zlm的RTSP端口}/broadcast/{设备国标编号}_{通道国标编号}?sign={md5(pushKey)}' ``` + 例如 + ```shell ffmpeg -re -i test.mp3 -acodec pcm_alaw -ar 8000 -ac 1 -f rtsp 'rtsp://192.168.1.3:22554/broadcast/34020000001320000001_34020000001320000001?sign=41db35390ddad33f83944f44b8b75ded' ``` + 测试流程如下: + ```plantuml @startuml "FFMPEG" -> "ZLMediaKit": 推流到zlm @@ -44,33 +57,48 @@ ffmpeg -re -i test.mp3 -acodec pcm_alaw -ar 8000 -ac 1 -f rtsp 'rtsp://192.168.1 "ZLMediaKit" -> "设备": 向设备推流 @enduml ``` + 如果听到设备播放你推送的音频,那么意味着调用成功,此过程推流即可需要调用任何接口 + ## 生产环境网页发起语音对讲 + 生产环境下使用语音对讲,如果是自己的客户端设备那么直接上面的ffmpeg测试方式,按照固定格式推流到zlm即可。 对于WEB程序,主要是局域网和公网的区别,两个原因: + 1. 很多设备不支持公网对讲 2. 公网和局域网获取证书实现https支持的方式不同 + ### 公网使用 + 公网你可以直接使用证书厂商或者云服务器厂商提供的证书,这是很方便的。 + ### 局域网使用 + 局域网你需要为wvp和zlm生成自签名证书,这里我推荐一种生成自签名证书相对方便的方式, 此方式为linux下的一种方式。 下载证书生成工具: [https://github.com/FiloSottile/mkcert/releases/tag/v1.4.4](https://github.com/FiloSottile/mkcert/releases/tag/v1.4.4) 安装此工具, 进入解压的工具目录,执行 + ```shell ./mkcert-v1.4.4-linux-amd64 -install ``` + 生成pem证书 + ```shell ./mkcert-v1.4.4-linux-amd64 局域网IP 局域网IP2 局域网IP3 ``` + 你会得到两文件*-key.pem和*.pem, 此文件配置到wvp后既可实现证书的加载 生成zlm使用的证书 + ```shell cat *.pem *-key.pem> ./zlm.pem ``` + 得到的文件就是可以给zlm使用的证书 zlm下使用证书有两种方式: + 1. 替换zlm下的default.pem, 即删除此文件并把zlm.pem重命名为default.pem 2. 在启动zlm的使用添加 `-s zlm.pem` \ No newline at end of file diff --git a/doc/_content/ability/continuous_recording.md b/doc/_content/ability/continuous_recording.md index 516830fde..78c1ae58c 100644 --- a/doc/_content/ability/continuous_recording.md +++ b/doc/_content/ability/continuous_recording.md @@ -1,14 +1,16 @@ + # 7*24不间断录像 -目前如果要实现不间断录像如果只是关闭无人观看停止推流是不够的,设备可能经历断网,重启,都会导致录像的中断,目前给大家提供一种可用的临时方案。 +目前如果要实现不间断录像如果只是关闭无人观看停止推流是不够的,设备可能经历断网,重启,都会导致录像的中断,目前给大家提供一种可用的临时方案。 **原理:** wvp支持使用流地址自动点播,即你拿到一个流地址直接去播放,即使设备处于未点播状态,wvp会自动帮你点播;ZLM 的拉流代理成功后会无限重试,只要流一恢复就可以拉起来,基于这两个原理。 **方案如下:** + 1. wvp的配置中user-settings->auto-apply-play设置为团true,开启自动点播; 2. 点击你要录像的通道,点击播放页面左下角的“更多地址”,点击rtsp,此时复制了rtsp地址到剪贴板; 3. 在拉流代理中添加一路流,地址填写你复制的地址,启用成功即可。 -**前提:** + **前提:** 1. wvp使用多端口收流,不然你无法得到一个固定的流地址,也就无法实现自动点播。 diff --git a/doc/_content/ability/device.md b/doc/_content/ability/device.md index 134f7add0..cc604576f 100644 --- a/doc/_content/ability/device.md +++ b/doc/_content/ability/device.md @@ -1,33 +1,68 @@ + # 接入设备 -设备接入主要是需要在设备上配置28181上级也就是WVP-PRO的信息,只有信息一致的情况才可以注册成功。设备注册成功后打开WVP->国标设备,可以看到新增加的设备;[设备使用](./_content/ability/device_use.md), -主要有以下字段需要配置: + +## 国标28181设备 + +设备接入主要是需要在设备上配置28181上级也就是WVP-PRO的信息,只有信息一致的情况才可以注册成功。设备注册成功后打开WVP-> +国标设备,可以看到新增加的设备;[设备使用](./_content/ability/device_use.md), +主要有以下字段需要配置: - sip->port -28181服务监听的端口 + 28181服务监听的端口 - sip->domain -domain宜采用ID统一编码的前十位编码。 + domain宜采用ID统一编码的前十位编码。 - sip->id -28181服务ID + 28181服务ID - sip->password -28181服务密码 + 28181服务密码 -- 配置信息在如下位置 +- 配置信息在如下位置 ![_media/img_16.png](_media/img_16.png) *** -## 大华摄像头 + +### 1. 大华摄像头 + ![_media/img_10.png](_media/img_10.png) -## 大华NVR + +### 2. 大华NVR + ![_media/img_11.png](_media/img_11.png) -## 艾科威视摄像头 + +### 3. 艾科威视摄像头 + ![_media/img_15.png](_media/img_15.png) -## 水星摄像头 + +### 4. 水星摄像头 + ![_media/img_12.png](_media/img_12.png) -## 海康摄像头 + +### 5. 海康摄像头 + ![_media/img_9.png](_media/img_9.png) +## 直播推流设备 + +这里以obs推流为例,很多无人机也是一样的,设置下推流地址就可以接入了 + +1. 从wvp获取推流地址, 选择节点管理菜单,查看要推流的节点; + ![_media/img_19.png](_media/img_19.png) +2. 拼接推流地址 + 得到的rtsp地址就是: rtsp://{流IP}:{RTSP PORT}/{app}/{stream} + 得到的rtmp地址就是: rtsp://{流IP}:{RTMP PORT}/{app}/{stream} + 其中流IP是设备可以连接到zlm的IP,端口是对应协议的端口号, app和stream自己定义就可以. +3. 增加推流鉴权信息 + wvp默认开启推流鉴权,拼接好的地址是不能直接推送的,会被返回鉴权失败,参考[推流规则](_content/ability/push?id=推流规则) +4. 推流成功后可以再推流列表中看到推流设备,可以播放 + 此方式只支持设备实时流的播放,无其他功能, 推流信息在推流结束后会自动移除,在列表里就看不到了,如果需要推流信息需要为设备配置国标编号,这样才可以作为wvp的一个永久通道存在. + +## 接入非国标IPC设备或者其他流地址形式的设备 + +这类设备的接入主要通过拉流代理的方式接入,原理就是zlm主动像播放器一样拉取这个流缓存在自己服务器供其他人播放.可以解决源设备并发访问能力差的问题. +在拉流代理/添加代理后可以直接播放, 拉流代理也是同样只支持播放当前配置的流. + [设备使用](_content/ability/device_use.md) diff --git a/doc/_content/ability/device_use.md b/doc/_content/ability/device_use.md index c406b3bbb..6cb54dc10 100644 --- a/doc/_content/ability/device_use.md +++ b/doc/_content/ability/device_use.md @@ -1,21 +1,30 @@ -# 设备使用 -### 更新设备通道 - 点击列表末尾的“刷新”按钮,可以看到一个圆形进度条,等进度结束提示成功后即可更新完成,如果通道数量有变化你可以看点击左上角的![刷新](_media/img_14.png)即可看到通道数量的变化;如果通道数量仍未0,那么可能时对方尚未推送通道给你。 -### 查看设备通道 - 点击列表末尾的“通道”按钮, -### 查看设备定位 - 点击列表末尾的“定位”按钮,即可跳转到地图页面看到设备的位置 -### 编辑设备在WVP中一些功能 + +# 国标设备 + +### 更新设备通道 + +点击列表末尾的“刷新”按钮,可以看到一个圆形进度条,等进度结束提示成功后即可更新完成,如果通道数量有变化你可以看点击左上角的![刷新](_media/img_14.png) +即可看到通道数量的变化;如果通道数量仍未0,那么可能时对方尚未推送通道给你。 + +### 查看设备通道 + +点击列表末尾的“通道”按钮, + +### 编辑设备 + 点击列表末尾的“编辑”按钮,即可在打开的弹窗中对设备功能进行修改 + - 设备名称 如何未能从设备里读取到设备名称或者需要自己重命名,那么可以修改此选项。 +- 密码 + 支持为设备配置独立的密码. +- 收流IP + 如果你需要设备从指定的网络地址接入视频流,那么可以配置此IP,设备将会发流到这个IP,比如多网卡接入的服务器.或者存在网络映射的情况. +- 流媒体ID + 固定设备使用的流媒体ID,默认根据负载自动分配. - 字符集 修改读取设备数据时使用的字符集,默认为GB2312,但是GB2312收录的汉字不全,所以有时候回遇到乱码,可以修改为UTF-8来解决。 -- 地理坐标系 - 展示此设备定位信息时使用的设用什么坐标系来解析经纬度,一般不用修改,如果遇到定位不准,可以修改尝试修改此选项解决。 -- 目录结构 - 展示设备的通道信息时,使用设备作为树形结构的依据,国标28181定义了两种树形结构,详情查看[国标28181的树形结构](./_content/theory/channel_tree.md); - 目录订阅 填写订阅周期即可对设备开启目录订阅,设备如果支持目录订阅那么设备在通道信息发生变化时就会通知WVP哪些通道发生了那些变化,包括通道增加/删除/更新/上线/下线/视频丢失/故障。0为取消订阅。 一般NVR和平台对接可以开启此选项,直接接摄像机开启此选项意义不大。 @@ -23,13 +32,31 @@ 对设备开启移动位置订阅,设备如果支持目录订阅那么设备位置发生变化时会通知到WVP,一般执法记录仪可以开启此选项,对固定位置的设备意义不大。 - SSRC校验 为了解决部分设备出现的串流问题,可以打开此选项。ZLM会严格按照给定的ssrc处理视频流。部分设备流信息不标准,开启可能导致无法点播。 -### 删除设备 - 可以删除WVP中的设备信息,如果设备28181配置未更改,那么设备在下一次注册后仍然会注册上来。 -### 点播视频 - 进入通道列表后,点击列表末尾的“播放”按钮,稍等即可弹出播放页面 -### 设备录像 - 进入通道列表后,点击列表末尾的“设备录像”按钮,也可以在播放页面点击录像查询进入录像查看页面,选择要查看的日期即可对录像进行播放和下载。 -### 云台控制 - 可以对支持云台功能的设备进行上下左右的转动以及拉近拉远的操作。 -### 获取视频的播放器地址 - 视频点播成功后在实时视频页面,点击“更多地址”可以看到所有的播放地址,地址是否可以播放与你是否完整编译启用zlm功能有关,更与网络有关。 \ No newline at end of file +- 作为消息通道 + wvp支持通过报警消息给下级WVP互相推送消息,消息内容由redis消息发送给wvp,wvp编辑成报警消息发送给下级 +- 收到ACK后发流 + 语音对讲策略: 不同的设备对于语音对讲的收流时机要求不一,勾选后会在收到设备发送的ack后再开始发流,不勾选则在回复200OK后开始发流,目前已知大华设备不勾选,海康需要勾选. + +### 删除设备 + +可以删除WVP中的设备信息,如果设备28181配置未更改,那么设备在下一次注册后仍然会注册上来。 + +### 点播视频 + +进入通道列表后,点击列表末尾的“播放”按钮,稍等即可弹出播放页面 + +### 设备录像 + +进入通道列表后,点击列表末尾的“设备录像”按钮,也可以在播放页面点击录像查询进入录像查看页面,选择要查看的日期即可对录像进行播放和下载。 + +### 云台控制 + +可以对支持云台功能的设备进行上下左右的转动以及拉近拉远的操作。 + +### 获取视频的播放器地址 + +视频点播成功后在实时视频页面,点击“更多地址”可以看到所有的播放地址,地址是否可以播放与你是否完整编译启用zlm功能有关,更与网络有关。 + +### 语音对讲 + +[语音对讲](_content/ability/continuous_broadcast.md) \ No newline at end of file diff --git a/doc/_content/ability/gis.md b/doc/_content/ability/gis.md index f77a428c0..5932df923 100644 --- a/doc/_content/ability/gis.md +++ b/doc/_content/ability/gis.md @@ -1,20 +1,26 @@ -# 电子地图 -WVP提供了简单的电子地图用于设备的定位以及移动设备的轨迹信息,电子地图基于开源的地图引擎openlayers开发。 -### 查看设备定位 -1. 可以在设备列表点击“定位”按钮,自动跳转到电子地图页面; -2. 在电子地图页面在设备上右键点击“定位”获取设备/平台下的所有通道位置。 -3. 单击通道信息可以定位到具体的通道 +# 电子地图 + +WVP提供了简单的电子地图用于设备的定位以及移动设备的轨迹信息,电子地图基于开源的地图引擎openlayers开发。 + +### 查看设备定位 + +1. 可以在设备列表点击“定位”按钮,自动跳转到电子地图页面; +2. 在电子地图页面在设备上右键点击“定位”获取设备/平台下的所有通道位置。 +3. 单击通道信息可以定位到具体的通道 ### 查询设备轨迹 + 查询轨迹需要提前配置save-position-history选项开启轨迹信息的保存,目前WVP此处未支持分库分表,对于大数据量的轨迹信息无法胜任,有需求请自行二次开发或者定制开发。 在电子地图页面在设备上右键点击“查询轨迹”获取设备轨迹信息。 PS: 目前的底图仅用用作演示和学习,商用情况请自行购买授权使用。 ### 更换底图以及底图配置 + 目前WVP支持使用了更换底图,配置文件在web_src/static/js/config.js,请修改后重新编译前端文件。 + ```javascript window.mapParam = { // 开启/关闭地图功能 diff --git a/doc/_content/ability/node_manger.md b/doc/_content/ability/node_manger.md index 1b4aef324..ec39a6b2e 100644 --- a/doc/_content/ability/node_manger.md +++ b/doc/_content/ability/node_manger.md @@ -1,9 +1,20 @@ + # 节点管理 + +![节点管理](_media/img_23.png) + WVP支持单个WVP多个ZLM的方案来扩展WVP的视频并发能力,并发点播是因为带宽和性能的原因,单个ZLM节点能支持的路数有限,所以WVP增加了ZLM集群来扩展并发并且保证ZLM的高可用。 + ## 默认节点 + WVP中为了保证功能的完整性,ZLM节点至少要有一个默认节点,这个节点不是在管理页面添加的,而是在WVP的配置文件中配置的,这个节点不可在页面删除。每次启动会自动从配置文件中读取配置写入数据库备用。 + ## 新增节点 -启动你要添加的zlm节点,然后点击“添加节点”按钮输入zlm的ip, http端口,SECRET。点击测试测试完成则开始对节点进行详细的设置,如果你的zlm是使用docker启动的,可能存在zlm使用的端口与宿主机端口不一致的情况,需要在这里一一配置。 + +启动你要添加的zlm节点,然后点击“添加节点”按钮输入zlm的ip, +http端口,SECRET。点击测试测试完成则开始对节点进行详细的设置,如果你的zlm是使用docker启动的,可能存在zlm使用的端口与宿主机端口不一致的情况,需要在这里一一配置。 + ## wvp使用多个节点的原理 + wvp会把连接的节点统一记录在redis中,并记录zlm的负载情况,当新的请求到来时,会取出负载最低的那个zlm进行使用。以此保证节点负载均衡。 diff --git a/doc/_content/ability/online_doc.md b/doc/_content/ability/online_doc.md index 55ecb2680..50d5070e9 100644 --- a/doc/_content/ability/online_doc.md +++ b/doc/_content/ability/online_doc.md @@ -1,2 +1,3 @@ + # 在线文档 diff --git a/doc/_content/ability/proxy.md b/doc/_content/ability/proxy.md index 74e632d81..7080442f4 100644 --- a/doc/_content/ability/proxy.md +++ b/doc/_content/ability/proxy.md @@ -1,12 +1,17 @@ + # 拉流代理 + 不是所有的摄像机都支持国标或者推流的,但是这些设备可以得到一个视频播放地址,通常为rtsp协议, 以大华为例: + ```text rtsp://{user}:{passwd}@{ipc_ip}:{rtsp_port}/cam/realmonitor?channel=1&subtype=0 ``` + 可以得到这样一个流地址,可以直接用vlc进行播放,此时我们可以通过拉流代理功能将这个设备推送给其他国标平台了。 流程如下: + ```plantuml @startuml "摄像机" <- "ZLMediaKit": 1. 流去流信息到ZLM @@ -15,10 +20,13 @@ rtsp://{user}:{passwd}@{ipc_ip}:{rtsp_port}/cam/realmonitor?channel=1&subtype=0 "WVP-PRO" -> "ZLMediaKit": 4. 通知推流到上级国标平台 @enduml ``` + ## 添加代理 + 拉流代理支持两种方式: + 1. ZLM中直接代理流,支持RTSP/RTMP,不支持转码; 2. 借助ffmpeg完成拉转,可以通过修改ffmpeg拉转参数完成转码。 -点击页面的“添加代理”,安装提示操作即可,保存并启用成功后,可以在国标级联中[添加通道推送给上级平台](./_content/ability/cascade?id=_2-%e6%b7%bb%e5%8a%a0%e7%9b%ae%e5%bd%95%e4%b8%8e%e9%80%9a%e9%81%93) + 点击页面的“添加代理”,添加信息后保存即可,如果你需要共享推流信息到其他国标平台,那么你需要编辑/国标通道配置,配置国标编码. -PS: ffmpeg默认模板不需修改,需要修改参数自行去ZLM配置文件中添加一个即可。 +`PS: ffmpeg默认模板不需修改,需要修改`参数自行去ZLM配置文件中添加一个即可。 diff --git a/doc/_content/ability/push.md b/doc/_content/ability/push.md index ef501826e..cc568b3c8 100644 --- a/doc/_content/ability/push.md +++ b/doc/_content/ability/push.md @@ -1,8 +1,11 @@ + # 推流列表 + ## 功能说明 WVP支持三种图像输入方式,直播,[拉流代理](_content/ability/proxy.md),[国标](_content/ability/device.md),直播设备接入流程如下 + ```plantuml @startuml "直播设备" -> "ZLMediaKit": 1. 发起推流 @@ -11,30 +14,41 @@ WVP支持三种图像输入方式,直播,[拉流代理](_content/ability/pro "WVP-PRO" -> "ZLMediaKit": 4. 通知推流到上级国标平台 @enduml ``` -1. 默认情况下WVP收到推流信息后,列表中出现这条推流信息,此时你可以点击“加入国标”按钮为此路推流配置名称以及国标编号,只有有国标编号的推流才可以添加到级联平台,保存成功后可以在国标级联中[添加通道推送给上级平台](_content/ability/cascade?id=_2-%e6%b7%bb%e5%8a%a0%e7%9b%ae%e5%bd%95%e4%b8%8e%e9%80%9a%e9%81%93) -2. WVP也支持推流前导入大量通道直接推送给上级,点击“下载模板”按钮,根据示例修改模板后,点击“通道导入”按钮导入通道数据,保存成功后可以在国标级联中[添加通道推送给上级平台](_content/ability/cascade?id=_2-%e6%b7%bb%e5%8a%a0%e7%9b%ae%e5%bd%95%e4%b8%8e%e9%80%9a%e9%81%93) + +1. 默认情况下WVP收到推流信息后,列表中出现这条推流信息,如果你需要共享推流信息到其他国标平台,那么你需要编辑/国标通道配置,配置国标编码. +2. WVP也支持推流前导入大量通道直接推送给上级,点击“下载模板”按钮,根据示例修改模板后,点击“通道导入”按钮导入通道数据. ## 推拉流鉴权规则 + 为了保护服务器的WVP默认开启推流鉴权(目前不支持关闭此功能) ### 推流规则 + 推流时需要携带推流鉴权的签名sign,sign=md5(pushKey),pushKey来自用户表,每个用户会有一个不同的pushKey. 例如app=test,stream=live,pushKey=1000,ip=192.168.1.4, port=10554 那么推流地址为: + ``` rtsp://192.168.1.4:10554/test/live?sign=a9b7ba70783b617e9998dc4dd82eb3c5 ``` + 支持推流时自定义播放鉴权Id,参数名为callId,此时sign=md5(callId_pushKey) 例如app=test,stream=live,pushKey=1000,callId=12345678, ip=192.168.1.4, port=10554 那么推流地址为: + ``` rtsp://192.168.1.4:10554/test/live?callId=12345678&sign=c8e6e01dde2d60c66dcea8d2498ffef1 ``` + ### 播放规则 + 默认情况播放不需要鉴权,但是如果推流时携带了callId,那么播放时必须携带callId 例如app=test,stream=live,无callId, ip=192.168.1.4, port=10554 那么播放地址为: + ``` rtsp://192.168.1.4:10554/test/live ``` + 例如app=test,stream=live,callId=12345678, ip=192.168.1.4, port=10554 那么播放地址为: + ``` rtsp://192.168.1.4:10554/test/live?callId=12345678 ``` diff --git a/doc/_content/ability/user.md b/doc/_content/ability/user.md index 0c472386a..776fe6b64 100644 --- a/doc/_content/ability/user.md +++ b/doc/_content/ability/user.md @@ -1,2 +1,3 @@ + # 用户管理 diff --git a/doc/_content/about_doc.md b/doc/_content/about_doc.md index f727264e9..02e36137f 100644 --- a/doc/_content/about_doc.md +++ b/doc/_content/about_doc.md @@ -1,5 +1,7 @@ # 关于本文档 -本文档开源在gitee上,[https://gitee.com/pan648540858/wvp-pro-doc.git](https://gitee.com/pan648540858/wvp-pro-doc.git),如果文档出现任何错误或者不易理解的语句,请大家提ISSUE帮助我及时更正。欢迎大家提交PR一起维护这份文档,让更多的人可以使用到这个开源的视频平台。 + +本文档开源在gitee上,[https://gitee.com/pan648540858/wvp-pro-doc.git](https://gitee.com/pan648540858/wvp-pro-doc.git) +,如果文档出现任何错误或者不易理解的语句,请大家提ISSUE帮助我及时更正。欢迎大家提交PR一起维护这份文档,让更多的人可以使用到这个开源的视频平台。 diff --git a/doc/_content/broadcast.md b/doc/_content/broadcast.md index 3a6b20d70..355e76172 100644 --- a/doc/_content/broadcast.md +++ b/doc/_content/broadcast.md @@ -1,6 +1,7 @@ # 原理图 ## 使用ffmpeg测试语音对讲原理 + ```plantuml @startuml "FFMPEG" -> "ZLMediaKit": 推流到zlm @@ -13,6 +14,7 @@ ``` ## 使用网页测试语音对讲原理 + ```plantuml @startuml "前端页面" -> "WVP-PRO": 请求推流地址 diff --git a/doc/_content/disclaimers.md b/doc/_content/disclaimers.md index 94412ecdc..b0b2e64a5 100644 --- a/doc/_content/disclaimers.md +++ b/doc/_content/disclaimers.md @@ -1,2 +1,5 @@ # 免责声明 -WVP-PRO自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; 由于使用本项目而产生的商业纠纷或侵权行为一概与本项目及开发者无关,请自行承担法律风险。 在使用本项目代码时,也应该在授权协议中同时表明本项目依赖的第三方库的协议 \ No newline at end of file + +WVP-PRO自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 +但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; 由于使用本项目而产生的商业纠纷或侵权行为一概与本项目及开发者无关,请自行承担法律风险。 +在使用本项目代码时,也应该在授权协议中同时表明本项目依赖的第三方库的协议 \ No newline at end of file diff --git a/doc/_content/introduction/_media/img_1.png b/doc/_content/introduction/_media/img_1.png index aad1859d0..b4a62afc1 100644 Binary files a/doc/_content/introduction/_media/img_1.png and b/doc/_content/introduction/_media/img_1.png differ diff --git a/doc/_content/introduction/_media/img_2.png b/doc/_content/introduction/_media/img_2.png index e78690000..a5961d6fc 100644 Binary files a/doc/_content/introduction/_media/img_2.png and b/doc/_content/introduction/_media/img_2.png differ diff --git a/doc/_content/introduction/compile.md b/doc/_content/introduction/compile.md index 15f2a9e68..b61d6453b 100644 --- a/doc/_content/introduction/compile.md +++ b/doc/_content/introduction/compile.md @@ -1,46 +1,63 @@ + # 编译 + WVP-PRO不只是实现了国标28181的协议,本身也是一个完整的视频平台。所以对于新手来说,你可能需要一些耐心来完成。遇到问题不要焦躁,你可以 + 1. 百度 2. 加入星球体提问;[知识星球](https://t.zsxq.com/0d8VAD3Dm) 3. 向作者发送邮件648540858@qq.com,寻求技术支持(有偿); WVP-PRO使用Spring boot开发,maven管理依赖。对于熟悉spring开发的朋友是很容易进行编译部署以及运行的。 下面将提供一种通用方法方便大家运行项目。 + ## 1 服务介绍 -| 服务 | 作用 | 是否必须 | -|----------------|------------------------------------------|-------------------------| -| WVP-PRO | 实现国标28181的信令以及视频平台相关的功能 | 是 | -| ZLMediaKit | 为WVP-PRO提供国标28181的媒体部分的实现,以及各种视频流格式的分发支持 | 是 | + +| 服务 | 作用 | 是否必须 | +|------------|------------------------------------------|------| +| WVP-PRO | 实现国标28181的信令以及视频平台相关的功能 | 是 | +| ZLMediaKit | 为WVP-PRO提供国标28181的媒体部分的实现,以及各种视频流格式的分发支持 | 是 | ## 2 安装依赖 -| 依赖 | 版本 | 用途 | 开发环境需要 | 生产环境需要 | -|--------|------------|-------------|--------|--------| -| jdk | >=1.8 | 运行与编译java代码 | 是 | 是 | -| maven | >=3.3 | 管理java代码依赖 | 否 | 否 | -| git || 下载/更新/提交代码 | 否 | 否 | -| nodejs || 编译于运行前端文件 | 否 | 否 | -| npm || 管理前端文件依赖 | 否 | 否 | + +| 依赖 | 版本 | 用途 | 开发环境需要 | 生产环境需要 | +|--------|-------|-------------|--------|--------| +| jdk | >=1.8 | 运行与编译java代码 | 是 | 是 | +| maven | >=3.3 | 管理java代码依赖 | 否 | 否 | +| git | | 下载/更新/提交代码 | 否 | 否 | +| nodejs | | 编译于运行前端文件 | 否 | 否 | +| npm | | 管理前端文件依赖 | 否 | 否 | 如果你是一个新手,建议你使用linux或者macOS平台。windows不推荐。 ubuntu环境,以ubuntu 18为例: + ``` bash apt-get install -y openjdk-11-jre git maven nodejs npm ``` + centos环境,以centos 8为例: + ```bash yum install -y java-1.8.0-openjdk.x86_64 git maven nodejs npm ``` + window环境,以windows10为例: + ```bash 这里不细说了,百度或者谷歌一搜一大把,基本都是下一步下一步,然后配置环境变量。 ``` + ## 3 安装mysql以及redis + 这里依然是参考网上教程,自行安装吧。 ## 4 编译ZLMediaKit -参考ZLMediaKit[WIKI](https://github.com/ZLMediaKit/ZLMediaKit/wiki),如果需要使用语音对讲功能,请参考[zlm启用webrtc编译指南](https://github.com/ZLMediaKit/ZLMediaKit/wiki/zlm%E5%90%AF%E7%94%A8webrtc%E7%BC%96%E8%AF%91%E6%8C%87%E5%8D%97),开启zlm的webrtc功能。截取一下关键步骤: + +参考ZLMediaKit[WIKI](https://github.com/ZLMediaKit/ZLMediaKit/wiki) +,如果需要使用语音对讲功能,请参考[zlm启用webrtc编译指南](https://github.com/ZLMediaKit/ZLMediaKit/wiki/zlm%E5%90%AF%E7%94%A8webrtc%E7%BC%96%E8%AF%91%E6%8C%87%E5%8D%97) +,开启zlm的webrtc功能。截取一下关键步骤: + ```bash # 国内用户推荐从同步镜像网站gitee下载 git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit @@ -48,40 +65,52 @@ cd ZLMediaKit # 千万不要忘记执行这句命令 git submodule update --init ``` + ## 5 编译WVP-PRO + ### 5.1 可以通过git克隆,也可以在项目下载点击下载 + ![点击下载](_media/img_1.png) ![点击下载](_media/img_2.png) 从gitee克隆 + ```bash git clone https://gitee.com/pan648540858/wvp-GB28181-pro.git ``` + 从github克隆 + ```bash git clone https://github.com/648540858/wvp-GB28181-pro.git ``` ### 5.2 编译前端页面 + ```shell script cd wvp-GB28181-pro/web_src/ npm --registry=https://registry.npmmirror.com install npm run build ``` + 编译如果报错, 一般都是网络问题, 导致的依赖包下载失败 编译完成后在src/main/resources下出现static目录 **编译完成一般是这个样子,中间没有报红的错误信息** ![编译成功](_media/img.png) ### 5.3 生成可执行jar + ```bash cd wvp-GB28181-pro mvn package ``` + ### 5.4 生成war + ```bash cd wvp-GB28181-pro mvn package -P war ``` + 编译如果报错, 一般都是网络问题, 导致的依赖包下载失败 编译完成后在target目录下出现wvp-pro-***.jar/wvp-pro-***.war。 接下来[配置服务](./_content/introduction/config.md) diff --git a/doc/_content/introduction/config.md b/doc/_content/introduction/config.md index 359aac9ef..ea4c7c786 100644 --- a/doc/_content/introduction/config.md +++ b/doc/_content/introduction/config.md @@ -1,5 +1,7 @@ + # 配置 + 对于首次测试或者新手同学,我建议在局域网测试,并且关闭服务器与客户机的防火墙测试。建议部署在linux进行测试。 ```plantuml @@ -8,24 +10,38 @@ "WVP-PRO" <-- "ZLMediaKit": Web Hook 接口 @enduml ``` + WVP-PRO通过调用ZLMediaKit的RESTful接口实现对ZLMediaKit行为的控制; ZLMediaKit通过Web Hook 接口把消息通知WVP-PRO。通过这种方式,实现了两者的互通。 对于最简单的配置,你不需要修改ZLMediaKit的任何默认配置。你只需要在WVP-PRO中配置的ZLMediaKit信息即可 + ## 1 WVP配置文件位置 + 基于spring boot的开发方式,配置文件的加载是很灵活的。默认在src/main/resources/application.yml,部分配置项是可选,你不需要全部配置在配置文件中, -完全的配置说明可以参看all-application.yml。 +完全的配置说明可以参看"src/main/resources/配置详情.yml"。 + ### 1.1 默认加载配置文件方式 + 使用maven打包后的target里,已经存在了配置文件,默认加载配置文件为application.yml,查看内容发现其中spring.profiles.active配置的内容,将入配置的值为dev,那么具体加载的配置文件就是application-dev.yml,如果配置的值找不到对应的配置文件,修改值为dev。 + ```shell cd wvp-GB28181-pro/target java -jar wvp-pro-*.jar ``` + ## 2 配置WVP-PRO + wvp支持多种数据库,包括Mysql,Postgresql,金仓等,配置任选一种即可。 + ### 2.1 数据库配置 -#### 2.1.1 初始化数据库 + +#### 2.1.1 初始化数据库 + 首先使用创建数据库,然后使用sql/初始化.sql初始化数据库,如果是从旧版升级上来的,使用升级sql更新。 -#### 2.1.2 Mysql数据库配置 + +#### 2.1.2 Mysql数据库配置 + 数据库名称以wvp为例 + ```yaml spring: dynamic: @@ -38,24 +54,30 @@ spring: username: root password: root123 ``` -#### 2.1.3 Postgresql数据库配置 + +#### 2.1.3 Postgresql数据库配置 + 数据库名称以wvp为例 + ```yaml spring: dynamic: - primary: master - datasource: - type: com.zaxxer.hikari.HikariDataSource - driver-class-name: org.postgresql.Driver - url: jdbc:postgresql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true - username: root - password: 12345678 + primary: master + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true + username: root + password: 12345678 pagehelper: helper-dialect: postgresql ``` -#### 2.1.4 金仓数据库配置 + +#### 2.1.4 金仓数据库配置 + 数据库名称以wvp为例 + ```yaml spring: dynamic: @@ -70,31 +92,39 @@ spring: pagehelper: helper-dialect: postgresql ``` + ### 2.2 Redis数据库配置 + 配置wvp中的redis连接信息,建议wvp自己单独使用一个db。 + ### 2.3 配置服务启动端口(可直接使用默认配置) + ```yaml # [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 server: - port: 18080 + port: 18080 ``` + ### 2.4 配置28181相关信息(可直接使用默认配置) + ```yaml # 作为28181服务器的配置 sip: - # [可选] 28181服务监听的端口 - port: 5060 - # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) - # 后两位为行业编码,定义参照附录D.3 - # 3701020049标识山东济南历下区 信息行业接入 - # [可选] - domain: 3402000000 - # [可选] - id: 34020000002000000001 - # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 - password: 12345678 + # [可选] 28181服务监听的端口 + port: 5060 + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) + # 后两位为行业编码,定义参照附录D.3 + # 3701020049标识山东济南历下区 信息行业接入 + # [可选] + domain: 3402000000 + # [可选] + id: 34020000002000000001 + # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 + password: 12345678 ``` + ### 2.5 配置ZLMediaKit连接信息 + ```yaml #zlm 默认服务器配置 media: @@ -118,7 +148,9 @@ media: # [可选] 国标级联在此范围内选择端口发送媒体流, send-port-range: 40000,40300 # 端口范围 ``` -### 2.4 个性化定制信息配置 + +### 2.4 策略配置 + ```yaml # [根据业务需求配置] user-settings: @@ -133,7 +165,8 @@ user-settings: # 国标点播 按需拉流, true:有人观看拉流,无人观看释放, false:拉起后不自动释放 stream-on-demand: true ``` -更多完整的配置信息参考all-application.yml文件,需要那个配置项,复制到正在使用的配置文件中对应的文件即可。 + +更多完整的配置信息参考"src/main/resources/配置详情.yml"文件,需要那个配置项,复制到正在使用的配置文件中对应的文件即可。 如果配置信息无误,你可以启动zlm,再启动wvp来测试了,启动成功的话,你可以在wvp的日志下看到zlm已连接的提示。 接下来[部署到服务器](./_content/introduction/deployment.md), 如果你只是本地运行直接在本地运行即可。 diff --git a/doc/_content/introduction/deployment.md b/doc/_content/introduction/deployment.md index f11fe7f26..755c4297a 100644 --- a/doc/_content/introduction/deployment.md +++ b/doc/_content/introduction/deployment.md @@ -1,47 +1,63 @@ # 部署 + **请仔细阅读以下内容** + 1. WVP-PRO与ZLM支持分开部署; 2. 需要开放的端口 -| 服务 | 端口 | 类型 | 必选 | -|-----|:-------------------------|-------------|-------| -| wvp | server.port | tcp | 是 | -| wvp | sip.port | udp and tcp | 是 | -| zlm | http.port | tcp | 是 | -| zlm | http.sslport | tcp | 否 | -| zlm | rtmp.port | tcp | 否 | -| zlm | rtmp.sslport | tcp | 否 | -| zlm | rtsp.port | udp and tcp | 否 | -| zlm | rtsp.sslport | udp and tcp | 否 | -| zlm | rtp_proxy.port | udp and tcp | 单端口开放 | -| zlm | rtp.port-range(在wvp中配置) | udp and tcp | 多端口开放 | + | 服务 | 端口 | 类型 | 必选 | + |-----|:-------------------------|-------------|-------| + | wvp | server.port | tcp | 是 | + | wvp | sip.port | udp and tcp | 是 | + | zlm | http.port | tcp | 是 | + | zlm | http.sslport | tcp | 否 | + | zlm | rtmp.port | tcp | 否 | + | zlm | rtmp.sslport | tcp | 否 | + | zlm | rtsp.port | udp and tcp | 否 | + | zlm | rtsp.sslport | udp and tcp | 否 | + | zlm | rtp_proxy.port | udp and tcp | 单端口开放 | + | zlm | rtp.port-range(在wvp中配置) | udp and tcp | 多端口开放 | 3. 测试环境部署建议所有服务部署在一台主机,关闭防火墙,减少因网络出现问题的可能; 4. 生产环境按需开放端口,但是建议修改默认端口,尤其是5060端口,易受到攻击; 5. zlm使用docker部署的情况,请使用host模式,或者端口映射一致,比如映射5060,应将外部端口也映射为5060端口; 6. zlm与wvp会保持高频率的通信,所以不要去将wvp与zlm分属在两个网络,比如wvp在内网,zlm却在公网的情况。 7. 启动服务,以linux为例 -**启动WVP-PRO** + **启动WVP-PRO** + ```shell nohup java -jar wvp-pro-*.jar & ``` + **war包:** 下载Tomcat后将war包放入webapps中,启动Tomcat以解压war包,停止Tomcat后,删除ROOT目录以及war包,将解压后的war包目录重命名为ROOT,将配置文件中的Server.port配置为与Tomcat端口一致 然后启动Tomcat。 **启动ZLM** + ```shell nohup ./MediaServer -d -m 3 & ``` + ### 前后端分离部署 + 前后端部署目前在最新的版本已经支持,请使用3月15日之后的版本部署 前端编译后的文件在`src/main/resources/static`中,将此目录下的文件部署。 WVP默认开启全部接口支持跨域。部署前端文件到WEB容器,并将访问的地址设置为WVP的地址即可。 **配置前端服务器** + 1. 在`src/main/resources/static/static/js/config.js`下配置服务器的地址,也就是wvp服务的地址 + ```javascript window.baseUrl = "http://xxx.com:18080" ``` + `这里的地址是需要客户电脑能访问到的,因为请求是客户端电脑发起,与代理不同` [接入设备](./_content/ability/device.md) +### 默认账号和密码 + +部署完毕后,可以通过访问 ip加端口的方式访问 WVP ,WVP的默认登录账号和密码均为 admin。 + + + diff --git a/doc/_content/qa/bug.md b/doc/_content/qa/bug.md index 81267ffe2..55e4cafa0 100644 --- a/doc/_content/qa/bug.md +++ b/doc/_content/qa/bug.md @@ -1,12 +1,18 @@ + # 反馈bug + 代码是在不断的完善的,不断修改会修复旧的问题也有可能引入新的问题,所以遇到BUG是很正常的一件事。所以遇到问题不要烦燥,咱们就事论事就好了。 + ## 如何反馈 + 1. 在知识星球提问。 2. 更新代码,很可能你遇到问题别人已经更早的遇到了,或者是作者自己发现了,已经解决了,所以你可以更新代码再次进行测试; 3. 可以在github提ISSUE,我几乎每天都会去看issue,你的问题我会尽快给予答复; + > 有偿支持可以给我发邮件, 648540858@qq.com ## 社群 + [![社群](../../_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm) > 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接退款,大家不需要有顾虑,来白嫖三天也不是不可以。 \ No newline at end of file diff --git a/doc/_content/qa/development.md b/doc/_content/qa/development.md index fec7b70cf..ec9df9e36 100644 --- a/doc/_content/qa/development.md +++ b/doc/_content/qa/development.md @@ -1,7 +1,11 @@ + # 参与到开发中来 + 非常欢迎有兴趣的小伙伴一起来维护这个项目 + ## 与开发有关的信息 + - 开发语言:后端java + 前端vue; - jdk版本: 1.8; - 作者自用开发ide: jetbrains intellij idea; @@ -9,7 +13,9 @@ - 后端使用Spring boot框架开发; - 项目大量使用了异步操作; - 跟代码学流程需要参考28181文档,只看代码你会很懵的; -- 必须学会[抓包](_content/skill/tcpdump.md),这是必须的 +- 必须学会[抓包](_content/skill/tcpdump.md),这是必须的 ## 提交代码 -大家可以通过fork项目的方式提交自己的代码,然后提交PR,我来合并到主线。提交代码的过程中我们需要遵循“**阿里编码规约**”,现有代码也有很多代码没有做到,但是我们在朝这个方向努力。 \ No newline at end of file + +大家可以通过fork项目的方式提交自己的代码,然后提交PR,我来合并到主线。提交代码的过程中我们需要遵循“**阿里编码规约** +”,现有代码也有很多代码没有做到,但是我们在朝这个方向努力。 \ No newline at end of file diff --git a/doc/_content/qa/play_error.md b/doc/_content/qa/play_error.md index c8e9c02bb..a474399da 100644 --- a/doc/_content/qa/play_error.md +++ b/doc/_content/qa/play_error.md @@ -1,6 +1,9 @@ + # 点播错误 + 排查点播错误你首先要清楚[点播的基本流程](_content/theory/play.md),一般的流程如下: + ```plantuml @startuml "WEB用户" -> "WVP-PRO": 1. 发起点播请求 @@ -15,11 +18,16 @@ "设备" --> "WVP-PRO": 10 200OK @enduml ``` + 针对几种常见的错误,我们来分析一下,也方便大家对号入座解决常见的问题 + ## 点播收到错误码 + 这个错误一般表现为点击"播放"按钮后很快得到一个错误。 + 1. **400错误码** -出现400错误玛时一般是这样的流程是这样的 + 出现400错误玛时一般是这样的流程是这样的 + ```plantuml @startuml "WEB用户" -> "WVP-PRO": 1. 发起点播请求 @@ -27,30 +35,39 @@ "设备" --> "WVP-PRO": 3. 400错误 @enduml ``` -此时通常是设备认为WVP发送了错误的消息给它,它认为消息不全或者错误所以直接返回400错误,此时我们需要[抓包](_content/skill/tcpdump.md)来分析是否缺失了内容,也可以直接联系对方询问为什么返回了400。 -WVP不能保证兼容所有的设备,有些实现不规范的设备可能在对接时就会出现上述问题,你可以联系作者帮忙对接。 -2. **500错误码** -500或者大于500小于600的错误码一般多是设备内部出了问题,解决方式有两个,第一种直接联系设备/平台客服寻求解决;第二种,如果你有确定可以对接这个设备的平台那么可以把对接这个平台的抓包和对接wvp的抓包同时发送给我,我来尝试解决。 +此时通常是设备认为WVP发送了错误的消息给它,它认为消息不全或者错误所以直接返回400错误,此时我们需要[抓包](_content/skill/tcpdump.md) +来分析是否缺失了内容,也可以直接联系对方询问为什么返回了400。 +WVP不能保证兼容所有的设备,有些实现不规范的设备可能在对接时就会出现上述问题,你可以联系作者帮忙对接。 + +2. **500错误码** + 500或者大于500小于600的错误码一般多是设备内部出了问题,解决方式有两个,第一种直接联系设备/平台客服寻求解决;第二种,如果你有确定可以对接这个设备的平台那么可以把对接这个平台的抓包和对接wvp的抓包同时发送给我,我来尝试解决。 ## 点播超时 + 点播超时的情况大致分为两种:点播超时和收流超时 + 1. **点播超时** -点播超时错误一般为信令的超时,比如长时间为收到对方的回复,可能出现在流程中 “3. 200OK(携带SDP消息体)”这个位置,即我们发送点播消息,但是设备没有回复,可能的原因: + 点播超时错误一般为信令的超时,比如长时间为收到对方的回复,可能出现在流程中 “3. 200OK(携带SDP消息体) + ”这个位置,即我们发送点播消息,但是设备没有回复,可能的原因: + > 1. 设备内部错误,未能回复消息 -> 2. 网络原因消息未到到达设备 +> 2. 网络原因消息未到到达设备 大部分时候是原因2,所以遇到这个错误我们首先要排查我们我的网路,如果你是公网部署,那么也可能时心跳周期太长,导致的路由NAT失效,WVP的消息无法通道原来的IP端口号发送给设备。 2. **收流超时** -收流超时可能发生在流程中的5和6,可能的原因有: -> 1. 设备发送了流但是发送到了错误的ip和端口上,而这个信息是在invite消息的sdp中指定的,就是流程2Invite(携带SDP消息体)中,而这个错误很可能来自你的配置错误,比如你设置了127.0.0.1导致设备网127.0.0.1上发流,或者是你WVP在公网,但是你给设备了一个内网ip,导致设备无法把流发送过来; + 收流超时可能发生在流程中的5和6,可能的原因有: + +> 1. 设备发送了流但是发送到了错误的ip和端口上,而这个信息是在invite消息的sdp中指定的,就是流程2Invite(携带SDP消息体) + 中,而这个错误很可能来自你的配置错误,比如你设置了127.0.0.1导致设备网127.0.0.1上发流,或者是你WVP在公网,但是你给设备了一个内网ip,导致设备无法把流发送过来; > 2. 设备内部错误未发送流; > 2. 设备发送了流,但是流无法识别,可能存在于流不规范和网络很差的情况下; > 3. 设备发送了流,zlm也收到了,但是zlm无法通过hook通知到wvp,此时原因是你可以检查zlm的配置文件中的hook配置,看看是否无法从zlm连接到wvp; > 4. 设备发送了流,但是开启SSRC校验,设备的流不够规范采用错误的ssrc,导致zlm选择丢弃; - -针对这些可能的错误原因我建议的排查顺序: + +针对这些可能的错误原因我建议的排查顺序: + - 关闭ssrc校验; - 查看zlm配置的hook是否可以连接到zlm; - 查看zlm日志是否有流注册; diff --git a/doc/_content/qa/regiser_error.md b/doc/_content/qa/regiser_error.md index d18459c54..7d4e96c17 100644 --- a/doc/_content/qa/regiser_error.md +++ b/doc/_content/qa/regiser_error.md @@ -1,8 +1,10 @@ -# 设备注册不上来的解决办法 -一般的原因有两个 -1. 信息填写错误,比如密码错误; -2. 网络不通导致注册消息无法发送到WVP; +# 设备注册不上来的解决办法 + +一般的原因有两个 + +1. 信息填写错误,比如密码错误; +2. 网络不通导致注册消息无法发送到WVP; 遇到问题首先仔细校验填写信息,例如海康可能需要勾选鉴权才可以输入密码。网络问题请自行测试。 \ No newline at end of file diff --git a/doc/_content/qa/start_error.md b/doc/_content/qa/start_error.md index 1dd533f2c..3e5749dad 100644 --- a/doc/_content/qa/start_error.md +++ b/doc/_content/qa/start_error.md @@ -1,17 +1,19 @@ + # 启动时报错 + 启动时的报错大部分时候是因为你的配置有问题,比如mysql没连接上,redis没连接上,18080/15060端口占用了,这些都会导致启动是报错,修改配置配置之后都可以解决; 下面我整理的一些常见的错误,大家可以先对号入座的简单排查下。 -> **常见错误** +> **常见错误** ![_media/img.png](_media/img.png) -**错误原因:** redis配置错误,可能原因: redis未启动/ip错误/端口错误/网络不通 +**错误原因:** redis配置错误,可能原因: redis未启动/ip错误/端口错误/网络不通 --- ![_media/img_1.png](_media/img_1.png) **错误原因:** redis配置错误,可能原因: 密码错误 --- ![_media/img_2.png](_media/img_2.png) -**错误原因:** mysql配置错误,可能原因: mysql未启动/ip错误/端口错误/网络不通 +**错误原因:** mysql配置错误,可能原因: mysql未启动/ip错误/端口错误/网络不通 --- ![_media/img_3.png](_media/img_3.png) **错误原因:** mysql配置错误,可能原因: 用户名/密码错误 diff --git a/doc/_content/skill/tcpdump.md b/doc/_content/skill/tcpdump.md index 7dd270de8..fa0ee88a0 100644 --- a/doc/_content/skill/tcpdump.md +++ b/doc/_content/skill/tcpdump.md @@ -1,15 +1,25 @@ + # 抓包 -如果说对于网络编程,有什么工具是必会的,我觉得抓包肯定是其中之一了。作为GB/T 28181调试过程中最重要的手段,我觉得如果你真对他有兴趣,或者系统遇到问题可以最快的得到解决,那么抓包你就一定要学会了。 + +如果说对于网络编程,有什么工具是必会的,我觉得抓包肯定是其中之一了。作为GB/T +28181调试过程中最重要的手段,我觉得如果你真对他有兴趣,或者系统遇到问题可以最快的得到解决,那么抓包你就一定要学会了。 ## 抓包工具的选择 + ### 1. Wireshark + 在具备图形界面的系统上,比如windows,linux发行版ubuntu,opensuse等,我一般直接使用Wireshark直接进行抓包,也方便进行内容的查看。 + ### 2. Tcpdump + 在使用命令行的系统,比如linux服务器,我一般使用Tcpdump进行抓包,无需额外安装,系统一般自带,抓包的到的文件,可以使用Wireshark打开,在图形界面下方便查看内容。 ## 工具安装 -Wireshark的安装很简单,根据提示一步步点击就好了,在linux需要解决权限的问题,如果和我一样使用图形界面的linux发行版的话,可以参看如下步骤; windows的小伙伴直接略过即可 + +Wireshark的安装很简单,根据提示一步步点击就好了,在linux需要解决权限的问题,如果和我一样使用图形界面的linux发行版的话,可以参看如下步骤; +windows的小伙伴直接略过即可 + ```shell # 1. 添加wireshark用户组 sudo groupadd wireshark @@ -20,43 +30,65 @@ sudo chmod 4755 /usr/bin/dumpcap # 4. 将需要使用的用户名加入wireshark用户组 sudo gpasswd -a $USER wireshark ``` + tcpdump一般linux都是自带,无需安装,可以这样验证;显示版本信息即是已安装 + ```shell tcpdump --version ``` + ## 开始抓包 + ### 使用Wireshark -在28181中我一般只关注sip包和rtp包,所以我一般是直接过滤sip和rtp,可以输入框输入 `sip or rtp`这样即可,如果设备来源比较多还可以加上ip和端口号的过滤`(sip or rtp )and ip.addr==192.168.1.3 and udp.port==5060` + +在28181中我一般只关注sip包和rtp包,所以我一般是直接过滤sip和rtp,可以输入框输入 `sip or rtp`这样即可,如果设备来源比较多还可以加上ip和端口号的过滤 +`(sip or rtp )and ip.addr==192.168.1.3 and udp.port==5060` 详细的过滤规则可以自行百度,我可以提供一些常用的给大家参考 ![img.png](_media/img.png) **只过滤SIP:** + ```shell sip ``` + **只获取rtp数据:** + ```shell rtp ``` + **默认方式:** + ```shell sip or rtp ``` + **过滤IP:** + ```shell sip and ip.addr==192.168.1.3 ``` + **过滤端口:** + ```shell sip and udp.port==5060 ``` -输入命令开启抓包后,此时可以进行操作,比如点播,录像回访等,操作完成回到Wireshark点击红色的停止即可,需要保存文件可以点击`文件->导出特定分组`导出过滤后的数据,也可以直接`文件->另存为`保存未过滤的数据。 + +输入命令开启抓包后,此时可以进行操作,比如点播,录像回访等,操作完成回到Wireshark点击红色的停止即可,需要保存文件可以点击 +`文件->导出特定分组`导出过滤后的数据,也可以直接`文件->另存为`保存未过滤的数据。 + ### 使用tcpdump + 对于服务器抓包,为了得到足够完整的数据,我一般会要求直接抓取网卡数据而不过滤,如下: 抓取网卡首先需要获取网卡名,在linux我一般使用`ip addr`获取网卡信息,如下所示: ![img_1.png](_media/img_1.png) + ```shell sudo tcpdump -i wlp3s0 -w demo.pcap ``` + ![img_2.png](_media/img_2.png) -命令行会停留在这个位置,此时可以进行操作,比如点播,录像回放等,操作完成回到命令行使用`Ctrl+C`结束命令行,在当前目录下得到demo.pcap,将这个文件下载到图形界面操作系统里,即可使用Wireshark查看了 +命令行会停留在这个位置,此时可以进行操作,比如点播,录像回放等,操作完成回到命令行使用`Ctrl+C` +结束命令行,在当前目录下得到demo.pcap,将这个文件下载到图形界面操作系统里,即可使用Wireshark查看了 更多的操作可以参考: [https://www.cnblogs.com/jiujuan/p/9017495.html](https://www.cnblogs.com/jiujuan/p/9017495.html) diff --git a/doc/_content/theory/broadcast_cascade.md b/doc/_content/theory/broadcast_cascade.md index e59b8c251..1770b117d 100644 --- a/doc/_content/theory/broadcast_cascade.md +++ b/doc/_content/theory/broadcast_cascade.md @@ -1,6 +1,7 @@ # 点播流程 + > 以下为WVP-PRO级联语音喊话流程。 ```plantuml @@ -31,8 +32,8 @@ @enduml ``` +## 注册流程描述如下: -## 注册流程描述如下: 1. 用户从网页或调用接口发起点播请求; 2. WVP-PRO向摄像机发送Invite消息,消息头域中携带 Subject字段,表明点播的视频源ID、发送方媒体流序列号、ZLMediaKit接收流使用的IP、端口号、 接收端媒体流序列号等参数,SDP消息体中 s字段为“Play”代表实时点播,y字段描述SSRC值,f字段描述媒体参数。 diff --git a/doc/_content/theory/channel_tree.md b/doc/_content/theory/channel_tree.md deleted file mode 100644 index c4c1b09ee..000000000 --- a/doc/_content/theory/channel_tree.md +++ /dev/null @@ -1,14 +0,0 @@ - - -# 通道的树形结构 - -国标28181规定了两种组织设备树的方式 -1. **行政区划** - 行政区划模式下主要是以行政区划作为目录节点例如:河北省->邯郸市->广平县 - ![_media/img_8.png](_media/img_8.png) -2. **业务分组** - 业务分组主要自定义的目录树的一种组织形式,但是对定义的目录的国标编号有一定的要求。 - 第一级别需要是业务分组类型,即国标编码中的11、12、13是215,例如:65010200002150000001; - 业务分组下是虚拟组织,即国标编码中的11、12、13是216,例如:65010200002160000002。 - 虚拟组织下不可是业务分组,虚拟组织下可以继续添加虚拟组织。 - ![_media/img_9.png](_media/img_9.png) diff --git a/doc/_content/theory/code.md b/doc/_content/theory/code.md index d5a892e2a..2d9a9be87 100644 --- a/doc/_content/theory/code.md +++ b/doc/_content/theory/code.md @@ -1,25 +1,29 @@ # 统一编码规则 + ## D.1 编码规则 A ->  编码规则 A 由中心编码(8位)、行业编码(2位)、类型编码(3位)和序号(7位)四个码段共20位十 ->进制数字字符构成,即系统编码 =中心编码 + 行业编码 + 类型编码 + 序号。 ->  编码规则 A 的详细说明见表 D.1。其中,中心编码指用户或设备所归属的监控中心的编码,按照监控中心所在地的行政区划代码确定, -> 当不是基层单位时空余位为0。行政区划代码采用 GB/T2260— 2007规定的行政区划代码表示。行业编码是指用户或设备所归属的行业,行业编码对照表见 D.3。 -> 类型编码指定了设备或用户的具体类型,其中的前端设备包含公安系统和非公安系统的前端设备,终端用 户包含公安系统和非公安系统的终端用户。 + +>   编码规则 A 由中心编码(8位)、行业编码(2位)、类型编码(3位)和序号(7位)四个码段共20位十 +> 进制数字字符构成,即系统编码 =中心编码 + 行业编码 + 类型编码 + 序号。 +>   编码规则 A 的详细说明见表 D.1。其中,中心编码指用户或设备所归属的监控中心的编码,按照监控中心所在地的行政区划代码确定, +> 当不是基层单位时空余位为0。行政区划代码采用 GB/T2260— 2007规定的行政区划代码表示。行业编码是指用户或设备所归属的行业,行业编码对照表见 +> D.3。 +> 类型编码指定了设备或用户的具体类型,其中的前端设备包含公安系统和非公安系统的前端设备,终端用 +> 户包含公安系统和非公安系统的终端用户。 ![img_7.png](_media/img_7.png) ![img_1.png](_media/img_1.png) -![img_2.png](_media/img_2.png) - +![img_2.png](_media/img_2.png) ## D.2 编码规则 B ->  编码规则 B由中心编码(8位)、行业编码(2位)、序号(4位)和类型编码(2位)四个码段构成,即系 ->统编码 =中心编码 + 行业编码 +序号+类型编码。编码规则 B的详细说明见表 D.2。 + +>   编码规则 B由中心编码(8位)、行业编码(2位)、序号(4位)和类型编码(2位)四个码段构成,即系 +> 统编码 =中心编码 + 行业编码 +序号+类型编码。编码规则 B的详细说明见表 D.2。 ![img_3.png](_media/img_3.png) ![img_4.png](_media/img_4.png) - ## D.3 行业编码对照表 ->  行业编码对照表见表 D.3。 + +>   行业编码对照表见表 D.3。 ![img_5.png](_media/img_5.png) ![img_6.png](_media/img_6.png) \ No newline at end of file diff --git a/doc/_content/theory/play.md b/doc/_content/theory/play.md index fbfdcc661..5fb75c0c7 100644 --- a/doc/_content/theory/play.md +++ b/doc/_content/theory/play.md @@ -1,6 +1,7 @@ # 点播流程 + > 以下为WVP-PRO点播流程。点播成功前的任何一个环节出现问题都可能出现点播超时,这也是排查点播超时的依据。 ```plantuml @@ -18,11 +19,11 @@ @enduml ``` +## 注册流程描述如下: -## 注册流程描述如下: -1. 用户从网页或调用接口发起点播请求; +1. 用户从网页或调用接口发起点播请求; 2. WVP-PRO向摄像机发送Invite消息,消息头域中携带 Subject字段,表明点播的视频源ID、发送方媒体流序列号、ZLMediaKit接收流使用的IP、端口号、 - 接收端媒体流序列号等参数,SDP消息体中 s字段为“Play”代表实时点播,y字段描述SSRC值,f字段描述媒体参数。 + 接收端媒体流序列号等参数,SDP消息体中 s字段为“Play”代表实时点播,y字段描述SSRC值,f字段描述媒体参数。 3. 摄像机向WVP-PRO回复200OK,消息体中描述了媒体流发送者发送媒体流的IP、端口、媒体格式、SSRC字段等内容。 4. WVP-PRO向设备回复Ack, 会话建立成功。 5. 设备向ZLMediaKit发送实时流。 diff --git a/doc/_content/theory/register.md b/doc/_content/theory/register.md index fad9589fe..2bc839a32 100644 --- a/doc/_content/theory/register.md +++ b/doc/_content/theory/register.md @@ -1,6 +1,7 @@ # 注册流程 + WVP-PRO目前仅支持国标中描述的基本注册流程,也是最常用的, > 基本注册即采用IETFRFC3261规定的基于数字摘要的挑战应答式安全技术进行注册. @@ -13,9 +14,8 @@ WVP-PRO目前仅支持国标中描述的基本注册流程,也是最常用的 @enduml ``` - -> 注册流程描述如下: -> 1. 摄像机向WVP-PRO服务器发送 Register请求; +> 注册流程描述如下: +> 1. 摄像机向WVP-PRO服务器发送 Register请求; > 2. WVP-PRO向摄像机发送响应401,并在响应的消息头 WWW_Authenticate字段中给出适合摄像机的认证体制和参数; > 3. 摄像机重新向WVP-PRO发送 Register请求,在请求的 Authorization字段给出信任书, 包含认证信息; > 4. WVP-PRO对请求进行验证,如果检查出 摄像机身份合法,向摄像机发送成功响应 200OK,如果身份不合法则发送拒绝服务应答。 diff --git a/doc/_coverpage.md b/doc/_coverpage.md index eb3524f9a..4f7695885 100644 --- a/doc/_coverpage.md +++ b/doc/_coverpage.md @@ -5,8 +5,8 @@ > 开箱即用的28181协议视频平台。 -- 基于GB/T28181-2016标准信令实现,兼容GB/T28181-2011。 -- 自带完整前端页面,开箱即用。 +- 基于GB/T28181-2016标准信令实现,兼容GB/T28181-2011。 +- 自带完整前端页面,开箱即用。 - 完全开源,且使用MIT许可协议。可以在保留版权信息的基础上商用。 [GitHub](https://github.com/648540858/wvp-GB28181-pro) diff --git a/doc/_media/2.png b/doc/_media/2.png index dd982e1e5..28d74b2dc 100644 Binary files a/doc/_media/2.png and b/doc/_media/2.png differ diff --git a/doc/_media/3-1.png b/doc/_media/3-1.png index a52620f2f..0e62c36e4 100644 Binary files a/doc/_media/3-1.png and b/doc/_media/3-1.png differ diff --git a/doc/_media/3-2.png b/doc/_media/3-2.png index bef780eb2..c75cef908 100644 Binary files a/doc/_media/3-2.png and b/doc/_media/3-2.png differ diff --git a/doc/_media/index.png b/doc/_media/index.png index 15200e69b..46fe99bb2 100644 Binary files a/doc/_media/index.png and b/doc/_media/index.png differ diff --git a/doc/_sidebar.md b/doc/_sidebar.md index 09a675df0..d1f500c4b 100644 --- a/doc/_sidebar.md +++ b/doc/_sidebar.md @@ -1,34 +1,32 @@ * **编译与部署** - * [编译](_content/introduction/compile.md) - * [配置](_content/introduction/config.md) - * [部署](_content/introduction/deployment.md) + * [编译](_content/introduction/compile.md) + * [配置](_content/introduction/config.md) + * [部署](_content/introduction/deployment.md) * **功能与使用** - * [接入设备](_content/ability/device.md) - * [设备使用](_content/ability/device_use.md) - * [国标级联](_content/ability/cascade2.md) - * [推流列表](_content/ability/push.md) - * [拉流代理](_content/ability/proxy.md) - * [电子地图](_content/ability/gis.md) - * [节点管理](_content/ability/node_manger.md) - * [云端录像](_content/ability/cloud_record.md) - * [不间断录像](_content/ability/continuous_recording.md) - * [语音对讲](_content/ability/continuous_broadcast.md) + * [接入设备](_content/ability/device.md) + * [国标设备](_content/ability/device_use.md) + * [推流列表](_content/ability/push.md) + * [拉流代理](_content/ability/proxy.md) + * [云端录像](_content/ability/cloud_record.md) + * [节点管理](_content/ability/node_manger.md) + * [通道管理](_content/ability/channel.md) + * [国标级联](_content/ability/cascade2.md) * **流程与原理** - * [统一编码规则](_content/theory/code.md) - * [树形结构](_content/theory/channel_tree.md) - * [注册流程](_content/theory/register.md) - * [点播流程](_content/theory/play.md) - * [级联语音喊话流程](_content/theory/broadcast_cascade.md) + * [统一编码规则](_content/theory/code.md) + * [注册流程](_content/theory/register.md) + * [点播流程](_content/theory/play.md) + * [级联语音喊话流程](_content/theory/broadcast_cascade.md) + * [语音对讲](_content/ability/continuous_broadcast.md) * **必备技巧** - * [抓包](_content/skill/tcpdump.md) + * [抓包](_content/skill/tcpdump.md) * **常见问答** - - [如何反馈BUG](_content/qa/bug.md) - - [如何参与开发](_content/qa/development.md) - - [启动报错的解决办法](_content/qa/start_error.md) - - [设备注册不上来的解决办法](_content/qa/regiser_error.md) - - [点播超时/报错的解决办法](_content/qa/play_error.md) + - [如何反馈BUG](_content/qa/bug.md) + - [如何参与开发](_content/qa/development.md) + - [启动报错的解决办法](_content/qa/start_error.md) + - [设备注册不上来的解决办法](_content/qa/regiser_error.md) + - [点播超时/报错的解决办法](_content/qa/play_error.md) * [**免责声明**](_content/disclaimers.md) * [**关于本文档**](_content/about_doc.md) diff --git a/doc/index.html b/doc/index.html index 580603da4..a8c1d7dfd 100644 --- a/doc/index.html +++ b/doc/index.html @@ -1,59 +1,59 @@ - - WVP-PRO文档 - - - - - - - + + WVP-PRO文档 + + + + + + + -
加载中
- - - - - - - + + + + + + + - + diff --git a/pom.xml b/pom.xml index d730641ef..5d818fcda 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ com.genersoft wvp-pro - 2.7.2 + 2.7.3 web video platform 国标28181视频平台 ${project.packaging} @@ -133,6 +133,14 @@ spring-boot-starter-jdbc + + + com.alibaba + druid-spring-boot-starter + 1.2.23 + + + com.mysql @@ -344,13 +352,13 @@ ftpserver-core 1.2.0 + org.apache.ftpserver ftplet-api 1.2.0 - org.projectlombok @@ -359,6 +367,13 @@ provided + + + org.projectlombok + lombok + 1.18.30 + provided + org.springframework.boot @@ -415,7 +430,7 @@ 3.3.0 - **/all-application.yml + **/配置详情.yml **/application.yml **/application-*.yml **/local.jks diff --git a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java index 262910b0b..e838cc8eb 100644 --- a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java +++ b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java @@ -2,8 +2,7 @@ package com.genersoft.iot.vmp; import com.genersoft.iot.vmp.utils.GitUtil; import com.genersoft.iot.vmp.utils.SpringBeanFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @@ -26,19 +25,22 @@ import java.util.Collections; @SpringBootApplication @EnableScheduling @EnableCaching +@Slf4j public class VManageBootstrap extends SpringBootServletInitializer { - private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class); - private static String[] args; private static ConfigurableApplicationContext context; public static void main(String[] args) { VManageBootstrap.args = args; VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args); - GitUtil gitUtil1 = SpringBeanFactory.getBean("gitUtil"); - logger.info("构建版本: {}", gitUtil1.getBuildVersion()); - logger.info("构建时间: {}", gitUtil1.getBuildDate()); - logger.info("GIT最后提交时间: {}", gitUtil1.getCommitTime()); + GitUtil gitUtil = SpringBeanFactory.getBean("gitUtil"); + if (gitUtil == null) { + log.info("获取版本信息失败"); + }else { + log.info("构建版本: {}", gitUtil.getBuildVersion()); + log.info("构建时间: {}", gitUtil.getBuildDate()); + log.info("GIT最后提交时间: {}", gitUtil.getCommitTime()); + } } // 项目重启 public static void restart() { diff --git a/src/main/java/com/genersoft/iot/vmp/common/CivilCodePo.java b/src/main/java/com/genersoft/iot/vmp/common/CivilCodePo.java index 452c12efe..38851977b 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/CivilCodePo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/CivilCodePo.java @@ -1,5 +1,7 @@ package com.genersoft.iot.vmp.common; +import org.springframework.util.ObjectUtils; + public class CivilCodePo { private String code; @@ -12,7 +14,9 @@ public class CivilCodePo { CivilCodePo civilCodePo = new CivilCodePo(); civilCodePo.setCode(infoArray[0]); civilCodePo.setName(infoArray[1]); - civilCodePo.setParentCode(infoArray[2]); + if (!ObjectUtils.isEmpty(infoArray[2])) { + civilCodePo.setParentCode(infoArray[2]); + } return civilCodePo; } diff --git a/src/main/java/com/genersoft/iot/vmp/common/CommonGbChannel.java b/src/main/java/com/genersoft/iot/vmp/common/CommonGbChannel.java deleted file mode 100644 index b289a14a2..000000000 --- a/src/main/java/com/genersoft/iot/vmp/common/CommonGbChannel.java +++ /dev/null @@ -1,191 +0,0 @@ -package com.genersoft.iot.vmp.common; - -import io.swagger.v3.oas.annotations.media.Schema; - -public class CommonGbChannel { - - /** - * 国标字段:归属 - */ - @Schema(description = "归属") - private String owner; - - /** - * 国标字段:行政区划 - */ - @Schema(description = "行政区划") - private String civilCode; - - /** - * 国标字段:安装地址 - */ - @Schema(description = "安装地址") - private String address; - - /** - * 国标字段:经度 - */ - @Schema(description = "经度") - private Double longitude; - - /** - * 国标字段:纬度 - */ - @Schema(description = "纬度") - private Double latitude; - - /** - * 国标字段:摄像机类型: - * 1-球机; - * 2-半球; - * 3-固定枪机; - * 4-遥控枪机 - */ - @Schema(description = "摄像机类型") - private Integer ptzType; - - /** - * 国标字段:摄像机位置类型扩展。 - * 1-省际检查站、 - * 2-党政机关、 - * 3-车站码头、 - * 4-中心广场、 - * 5-体育场馆、 - * 6-商业中心、 - * 7-宗教场所、 - * 8-校园周边、 - * 9-治安复杂区域、 - * 10-交通干线 - */ - @Schema(description = "摄像机位置类型扩展") - private Integer positionType; - - /** - * 国标字段:安装位置室外、室内属性 - * 1-室外、 - * 2-室内 - */ - @Schema(description = "安装位置室外、室内属性") - private Integer roomType; - - /** - * 国标字段:用途 - * 1-治安、 - * 2-交通、 - * 3-重点、 - */ - @Schema(description = "用途") - private Integer useType; - - /** - * 国标字段:补光属性 - * 1-无补光、 - * 2-红外补光、 - * 3-白光补光 - */ - @Schema(description = "补光属性") - private Integer supplyLightType; - - /** - * 摄像机监视方位属性。 - * 1-东、 - * 2-西、 - * 3-南、 - * 4-北、 - * 5-东南、 - * 6-东北、 - * 7-西南、 - * 8-西北 - * - */ - @Schema(description = "方位") - private Integer directionType; - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.owner = owner; - } - - public String getCivilCode() { - return civilCode; - } - - public void setCivilCode(String civilCode) { - this.civilCode = civilCode; - } - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - public Double getLongitude() { - return longitude; - } - - public void setLongitude(Double longitude) { - this.longitude = longitude; - } - - public Double getLatitude() { - return latitude; - } - - public void setLatitude(Double latitude) { - this.latitude = latitude; - } - - public Integer getPtzType() { - return ptzType; - } - - public void setPtzType(Integer ptzType) { - this.ptzType = ptzType; - } - - public Integer getPositionType() { - return positionType; - } - - public void setPositionType(Integer positionType) { - this.positionType = positionType; - } - - public Integer getRoomType() { - return roomType; - } - - public void setRoomType(Integer roomType) { - this.roomType = roomType; - } - - public Integer getUseType() { - return useType; - } - - public void setUseType(Integer useType) { - this.useType = useType; - } - - public Integer getSupplyLightType() { - return supplyLightType; - } - - public void setSupplyLightType(Integer supplyLightType) { - this.supplyLightType = supplyLightType; - } - - public Integer getDirectionType() { - return directionType; - } - - public void setDirectionType(Integer directionType) { - this.directionType = directionType; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java b/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java index dbe9e090a..8c91dc82a 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java @@ -1,15 +1,17 @@ package com.genersoft.iot.vmp.common; import com.genersoft.iot.vmp.service.bean.SSRCInfo; +import lombok.Data; /** * 记录每次发送invite消息的状态 */ +@Data public class InviteInfo { private String deviceId; - private String channelId; + private Integer channelId; private String stream; @@ -28,7 +30,7 @@ public class InviteInfo { private StreamInfo streamInfo; - public static InviteInfo getInviteInfo(String deviceId, String channelId, String stream, SSRCInfo ssrcInfo, + public static InviteInfo getInviteInfo(String deviceId, Integer channelId, String stream, SSRCInfo ssrcInfo, String receiveIp, Integer receivePort, String streamMode, InviteSessionType type, InviteSessionStatus status) { InviteInfo inviteInfo = new InviteInfo(); @@ -44,84 +46,4 @@ public class InviteInfo { return inviteInfo; } - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getChannelId() { - return channelId; - } - - public void setChannelId(String channelId) { - this.channelId = channelId; - } - - public InviteSessionType getType() { - return type; - } - - public void setType(InviteSessionType type) { - this.type = type; - } - - public InviteSessionStatus getStatus() { - return status; - } - - public void setStatus(InviteSessionStatus status) { - this.status = status; - } - - public StreamInfo getStreamInfo() { - return streamInfo; - } - - public void setStreamInfo(StreamInfo streamInfo) { - this.streamInfo = streamInfo; - } - - public String getStream() { - return stream; - } - - public void setStream(String stream) { - this.stream = stream; - } - - public SSRCInfo getSsrcInfo() { - return ssrcInfo; - } - - public void setSsrcInfo(SSRCInfo ssrcInfo) { - this.ssrcInfo = ssrcInfo; - } - - public String getReceiveIp() { - return receiveIp; - } - - public void setReceiveIp(String receiveIp) { - this.receiveIp = receiveIp; - } - - public Integer getReceivePort() { - return receivePort; - } - - public void setReceivePort(Integer receivePort) { - this.receivePort = receivePort; - } - - public String getStreamMode() { - return streamMode; - } - - public void setStreamMode(String streamMode) { - this.streamMode = streamMode; - } - } diff --git a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java index 96a987c94..280c11ba0 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java @@ -1,12 +1,14 @@ package com.genersoft.iot.vmp.common; import com.genersoft.iot.vmp.media.bean.MediaInfo; +import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.service.bean.DownloadFileInfo; import io.swagger.v3.oas.annotations.media.Schema; import java.io.Serializable; import java.util.Objects; + @Schema(description = "流信息") public class StreamInfo implements Serializable, Cloneable{ @@ -15,9 +17,9 @@ public class StreamInfo implements Serializable, Cloneable{ @Schema(description = "流ID") private String stream; @Schema(description = "设备编号") - private String deviceID; - @Schema(description = "通道编号") - private String channelId; + private String deviceId; + @Schema(description = "通道ID") + private Integer channelId; @Schema(description = "IP") private String ip; @@ -68,8 +70,8 @@ public class StreamInfo implements Serializable, Cloneable{ @Schema(description = "RTCS流地址") private StreamURL rtcs; - @Schema(description = "流媒体ID") - private String mediaServerId; + @Schema(description = "流媒体节点") + private MediaServer mediaServer; @Schema(description = "流编码信息") private MediaInfo mediaInfo; @Schema(description = "开始时间") @@ -80,6 +82,8 @@ public class StreamInfo implements Serializable, Cloneable{ private double progress; @Schema(description = "文件下载地址(录像下载使用)") private DownloadFileInfo downLoadFilePath; + @Schema(description = "点播请求的callId") + private String callId; @Schema(description = "是否暂停(录像回放使用)") private boolean pause; @@ -90,6 +94,9 @@ public class StreamInfo implements Serializable, Cloneable{ @Schema(description = "转码后的视频流") private StreamInfo transcodeStream; + @Schema(description = "使用的WVP ID") + private String serverId; + public void setFlv(StreamURL flv) { this.flv = flv; } @@ -352,19 +359,19 @@ public class StreamInfo implements Serializable, Cloneable{ this.app = app; } - public String getDeviceID() { - return deviceID; + public String getDeviceId() { + return deviceId; } - public void setDeviceID(String deviceID) { - this.deviceID = deviceID; + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; } - public String getChannelId() { + public Integer getChannelId() { return channelId; } - public void setChannelId(String channelId) { + public void setChannelId(Integer channelId) { this.channelId = channelId; } @@ -480,12 +487,12 @@ public class StreamInfo implements Serializable, Cloneable{ return rtcs; } - public String getMediaServerId() { - return mediaServerId; + public MediaServer getMediaServer() { + return mediaServer; } - public void setMediaServerId(String mediaServerId) { - this.mediaServerId = mediaServerId; + public void setMediaServer(MediaServer mediaServer) { + this.mediaServer = mediaServer; } public MediaInfo getMediaInfo() { @@ -646,4 +653,20 @@ public class StreamInfo implements Serializable, Cloneable{ public void setOriginType(int originType) { this.originType = originType; } + + public String getServerId() { + return serverId; + } + + public void setServerId(String serverId) { + this.serverId = serverId; + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java index 8be6835fd..3d5597563 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java +++ b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java @@ -26,7 +26,7 @@ public class VideoManagerConstants { public static final String KEEPLIVEKEY_PREFIX = "VMP_KEEPALIVE_"; // TODO 此处多了一个_,暂不修改 - public static final String INVITE_PREFIX = "VMP_INVITE"; + public static final String INVITE_PREFIX = "VMP_INVITE_INFO"; public static final String PLAYER_PREFIX = "VMP_INVITE_PLAY_"; public static final String PLAY_BLACK_PREFIX = "VMP_INVITE_PLAYBACK_"; public static final String DOWNLOAD_PREFIX = "VMP_INVITE_DOWNLOAD_"; @@ -39,7 +39,10 @@ public class VideoManagerConstants { public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_PLATFORM_REGISTER_INFO_"; - public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_PLATFORM_SEND_RTP_INFO_"; + public static final String SEND_RTP_INFO = "VMP_SEND_RTP_INFO:"; + public static final String SEND_RTP_INFO_CALLID = "VMP_SEND_RTP_INFO:CALL_ID"; + public static final String SEND_RTP_INFO_STREAM = "VMP_SEND_RTP_INFO:STREAM"; + public static final String SEND_RTP_INFO_CHANNEL = "VMP_SEND_RTP_INFO:CHANNEL"; public static final String EVENT_ONLINE_REGISTER = "1"; @@ -51,7 +54,9 @@ public class VideoManagerConstants { public static final String MEDIA_SSRC_USED_PREFIX = "VMP_MEDIA_USED_SSRC_"; - public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_MEDIA_TRANSACTION_"; + public static final String SIP_INVITE_SESSION = "VMP_SIP_INVITE_SESSION:"; + public static final String SIP_INVITE_SESSION_CALL_ID = SIP_INVITE_SESSION + "CALL_ID" + ":"; + public static final String SIP_INVITE_SESSION_STREAM = SIP_INVITE_SESSION + "STREAM" + ":"; public static final String MEDIA_STREAM_AUTHORITY = "VMP_MEDIA_STREAM_AUTHORITY_"; diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java b/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java index b449b1b7e..e55c8ecf2 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java @@ -5,9 +5,8 @@ import com.genersoft.iot.vmp.conf.security.SecurityUtils; import com.genersoft.iot.vmp.service.ILogService; import com.genersoft.iot.vmp.storager.dao.dto.LogDto; import com.genersoft.iot.vmp.utils.DateUtil; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; @@ -24,13 +23,11 @@ import java.io.IOException; /** * @author lin */ +@Slf4j @WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/api/*", asyncSupported=true) @Component public class ApiAccessFilter extends OncePerRequestFilter { - private final static Logger logger = LoggerFactory.getLogger(ApiAccessFilter.class); - - @Autowired private UserSetting userSetting; diff --git a/src/main/java/com/genersoft/iot/vmp/conf/CivilCodeFileConf.java b/src/main/java/com/genersoft/iot/vmp/conf/CivilCodeFileConf.java index 8449ebb10..878e01c5d 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/CivilCodeFileConf.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/CivilCodeFileConf.java @@ -2,8 +2,7 @@ package com.genersoft.iot.vmp.conf; import com.genersoft.iot.vmp.common.CivilCodePo; import com.genersoft.iot.vmp.utils.CivilCodeUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.context.annotation.Configuration; @@ -17,18 +16,15 @@ import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; /** * 启动时读取行政区划表 */ +@Slf4j @Configuration @Order(value=14) public class CivilCodeFileConf implements CommandLineRunner { - private final static Logger logger = LoggerFactory.getLogger(CivilCodeFileConf.class); - @Autowired @Lazy private UserSetting userSetting; @@ -36,7 +32,7 @@ public class CivilCodeFileConf implements CommandLineRunner { @Override public void run(String... args) throws Exception { if (ObjectUtils.isEmpty(userSetting.getCivilCodeFile())) { - logger.warn("[行政区划] 文件未设置,可能造成目录刷新结果不完整"); + log.warn("[行政区划] 文件未设置,可能造成目录刷新结果不完整"); return; } InputStream inputStream; @@ -44,7 +40,7 @@ public class CivilCodeFileConf implements CommandLineRunner { String filePath = userSetting.getCivilCodeFile().substring("classpath:".length()); ClassPathResource civilCodeFile = new ClassPathResource(filePath); if (!civilCodeFile.exists()) { - logger.warn("[行政区划] 文件<{}>不存在,可能造成目录刷新结果不完整", userSetting.getCivilCodeFile()); + log.warn("[行政区划] 文件<{}>不存在,可能造成目录刷新结果不完整", userSetting.getCivilCodeFile()); return; } inputStream = civilCodeFile.getInputStream(); @@ -52,7 +48,7 @@ public class CivilCodeFileConf implements CommandLineRunner { }else { File civilCodeFile = new File(userSetting.getCivilCodeFile()); if (!civilCodeFile.exists()) { - logger.warn("[行政区划] 文件<{}>不存在,可能造成目录刷新结果不完整", userSetting.getCivilCodeFile()); + log.warn("[行政区划] 文件<{}>不存在,可能造成目录刷新结果不完整", userSetting.getCivilCodeFile()); return; } inputStream = Files.newInputStream(civilCodeFile.toPath()); @@ -61,7 +57,6 @@ public class CivilCodeFileConf implements CommandLineRunner { BufferedReader inputStreamReader = new BufferedReader(new InputStreamReader(inputStream)); int index = -1; String line; - List civilCodePoList = new ArrayList<>(); while ((line = inputStreamReader.readLine()) != null) { index ++; if (index == 0) { @@ -69,15 +64,14 @@ public class CivilCodeFileConf implements CommandLineRunner { } String[] infoArray = line.split(","); CivilCodePo civilCodePo = CivilCodePo.getInstance(infoArray); - civilCodePoList.add(civilCodePo); + CivilCodeUtil.INSTANCE.add(civilCodePo); } - CivilCodeUtil.INSTANCE.add(civilCodePoList); inputStreamReader.close(); inputStream.close(); - if (civilCodePoList.isEmpty()) { - logger.warn("[行政区划] 文件内容为空,可能造成目录刷新结果不完整"); + if (CivilCodeUtil.INSTANCE.isEmpty()) { + log.warn("[行政区划] 文件内容为空,可能造成目录刷新结果不完整"); }else { - logger.info("[行政区划] 加载成功,共加载数据{}条", civilCodePoList.size()); + log.info("[行政区划] 加载成功,共加载数据{}条", CivilCodeUtil.INSTANCE.size()); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/CloudRecordTimer.java b/src/main/java/com/genersoft/iot/vmp/conf/CloudRecordTimer.java index de87a9c62..652853bb0 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/CloudRecordTimer.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/CloudRecordTimer.java @@ -5,8 +5,7 @@ import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.service.IMediaServerService; import com.genersoft.iot.vmp.service.bean.CloudRecordItem; import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -19,11 +18,10 @@ import java.util.List; /** * 录像文件定时删除 */ +@Slf4j @Component public class CloudRecordTimer { - private final static Logger logger = LoggerFactory.getLogger(CloudRecordTimer.class); - @Autowired private IMediaServerService mediaServerService; @@ -36,7 +34,7 @@ public class CloudRecordTimer { // @Scheduled(fixedRate = 10000) //每五秒执行一次,方便测试 @Scheduled(cron = "0 0 0 * * ?") //每天的0点执行 public void execute(){ - logger.info("[录像文件定时清理] 开始清理过期录像文件"); + log.info("[录像文件定时清理] 开始清理过期录像文件"); // 获取配置了assist的流媒体节点 List mediaServerItemList = mediaServerService.getAllOnline(); if (mediaServerItemList.isEmpty()) { @@ -64,12 +62,12 @@ public class CloudRecordTimer { boolean deleteResult = mediaServerService.deleteRecordDirectory(mediaServerItem, cloudRecordItem.getApp(), cloudRecordItem.getStream(), date, cloudRecordItem.getFileName()); if (deleteResult) { - logger.warn("[录像文件定时清理] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath()); + log.warn("[录像文件定时清理] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath()); } } result += cloudRecordServiceMapper.deleteList(cloudRecordItemList); } } - logger.info("[录像文件定时清理] 共清理{}个过期录像文件", result); + log.info("[录像文件定时清理] 共清理{}个过期录像文件", result); } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java b/src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java index de0e2df57..a56d4090b 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java @@ -1,8 +1,7 @@ package com.genersoft.iot.vmp.conf; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.stereotype.Component; @@ -20,11 +19,10 @@ import java.util.concurrent.TimeUnit; * 动态定时任务 * @author lin */ +@Slf4j @Component public class DynamicTask { - private final Logger logger = LoggerFactory.getLogger(DynamicTask.class); - private ThreadPoolTaskScheduler threadPoolTaskScheduler; private final Map> futureMap = new ConcurrentHashMap<>(); @@ -53,9 +51,9 @@ public class DynamicTask { ScheduledFuture future = futureMap.get(key); if (future != null) { if (future.isCancelled()) { - logger.debug("任务【{}】已存在但是关闭状态!!!", key); + log.debug("任务【{}】已存在但是关闭状态!!!", key); } else { - logger.debug("任务【{}】已存在且已启动!!!", key); + log.debug("任务【{}】已存在且已启动!!!", key); return; } } @@ -65,9 +63,9 @@ public class DynamicTask { if (future != null){ futureMap.put(key, future); runnableMap.put(key, task); - logger.debug("任务【{}】启动成功!!!", key); + log.debug("任务【{}】启动成功!!!", key); }else { - logger.debug("任务【{}】启动失败!!!", key); + log.debug("任务【{}】启动失败!!!", key); } } @@ -90,9 +88,9 @@ public class DynamicTask { ScheduledFuture future = futureMap.get(key); if (future != null) { if (future.isCancelled()) { - logger.debug("任务【{}】已存在但是关闭状态!!!", key); + log.debug("任务【{}】已存在但是关闭状态!!!", key); } else { - logger.debug("任务【{}】已存在且已启动!!!", key); + log.debug("任务【{}】已存在且已启动!!!", key); return; } } @@ -101,9 +99,9 @@ public class DynamicTask { if (future != null){ futureMap.put(key, future); runnableMap.put(key, task); - logger.debug("任务【{}】启动成功!!!", key); + log.debug("任务【{}】启动成功!!!", key); }else { - logger.debug("任务【{}】启动失败!!!", key); + log.debug("任务【{}】启动失败!!!", key); } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java index 0333e0dc3..5e29ff42f 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java @@ -3,8 +3,7 @@ package com.genersoft.iot.vmp.conf; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.BadCredentialsException; @@ -16,11 +15,10 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; /** * 全局异常处理 */ +@Slf4j @RestControllerAdvice public class GlobalExceptionHandler { - private final static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); - /** * 默认异常处理 * @param e 异常 @@ -29,7 +27,7 @@ public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public WVPResult exceptionHandler(Exception e) { - logger.error("[全局异常]: ", e); + log.error("[全局异常]: ", e); return WVPResult.fail(ErrorCode.ERROR500.getCode(), e.getMessage()); } @@ -54,6 +52,16 @@ public class GlobalExceptionHandler { public WVPResult exceptionHandler(HttpRequestMethodNotSupportedException e) { return WVPResult.fail(ErrorCode.ERROR400); } + /** + * 断言异常处理 + * @param e 异常 + * @return 统一返回结果 + */ + @ExceptionHandler(IllegalArgumentException.class) + @ResponseStatus(HttpStatus.OK) + public WVPResult exceptionHandler(IllegalArgumentException e) { + return WVPResult.fail(ErrorCode.ERROR100.getCode(), e.getMessage()); + } /** @@ -62,7 +70,7 @@ public class GlobalExceptionHandler { * @return 统一返回结果 */ @ExceptionHandler(ControllerException.class) - @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ResponseStatus(HttpStatus.OK) public ResponseEntity> exceptionHandler(ControllerException e) { return new ResponseEntity<>(WVPResult.fail(e.getCode(), e.getMsg()), HttpStatus.OK); } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java b/src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java index 33fdd23df..af12ecabd 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java @@ -1,12 +1,9 @@ package com.genersoft.iot.vmp.conf; import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.support.spring.http.converter.FastJsonHttpMessageConverter; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import org.jetbrains.annotations.NotNull; -import org.springframework.boot.autoconfigure.http.HttpMessageConverters; -import org.springframework.context.annotation.Bean; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; @@ -15,6 +12,8 @@ import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; +import java.util.LinkedHashMap; + /** * 全局统一返回结果 * @author lin @@ -52,6 +51,13 @@ public class GlobalResponseAdvice implements ResponseBodyAdvice { return JSON.toJSONString(WVPResult.success(body)); } + if (body instanceof LinkedHashMap) { + LinkedHashMap bodyMap = (LinkedHashMap) body; + if (bodyMap.get("status") != null && (Integer)bodyMap.get("status") != 200) { + return body; + } + } + return WVPResult.success(body); } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java index a9ea67f74..ef9669d5d 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java @@ -2,8 +2,7 @@ package com.genersoft.iot.vmp.conf; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.utils.DateUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; @@ -13,13 +12,11 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.regex.Pattern; - +@Slf4j @Configuration("mediaConfig") @Order(0) public class MediaConfig{ - private final static Logger logger = LoggerFactory.getLogger(MediaConfig.class); - // 修改必须配置,不再支持自动获取 @Value("${media.id}") private String id; @@ -179,7 +176,7 @@ public class MediaConfig{ try { hostAddress = InetAddress.getByName(sdpIp).getHostAddress(); } catch (UnknownHostException e) { - logger.error("[获取SDP IP]: 域名解析失败"); + log.error("[获取SDP IP]: 域名解析失败"); } return hostAddress; } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java index 3948fdb64..ab9c2d458 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java @@ -2,18 +2,17 @@ package com.genersoft.iot.vmp.conf; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.service.IMediaServerService; +import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; -import org.springframework.core.annotation.Order; import org.mitre.dsmiley.httpproxy.ProxyServlet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.util.ObjectUtils; import javax.servlet.ServletException; @@ -28,10 +27,9 @@ import java.net.ConnectException; @SuppressWarnings(value = {"rawtypes", "unchecked"}) @Configuration @Order(1) +@Slf4j public class ProxyServletConfig { - private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class); - @Autowired private IMediaServerService mediaServerService; @@ -44,7 +42,7 @@ public class ProxyServletConfig { servletRegistrationBean.setName("zlm_Proxy"); servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:6080"); servletRegistrationBean.addUrlMappings(); - if (logger.isDebugEnabled()) { + if (log.isDebugEnabled()) { servletRegistrationBean.addInitParameter("log", "true"); } return servletRegistrationBean; @@ -85,15 +83,15 @@ public class ProxyServletConfig { try { super.handleRequestException(proxyRequest, proxyResonse, e); } catch (ServletException servletException) { - logger.error("zlm 代理失败: ", e); + log.error("zlm 代理失败: ", e); } catch (IOException ioException) { if (ioException instanceof ConnectException) { - logger.error("zlm 连接失败"); + log.error("zlm 连接失败"); } else { - logger.error("zlm 代理失败: ", e); + log.error("zlm 代理失败: ", e); } } catch (RuntimeException exception){ - logger.error("zlm 代理失败: ", e); + log.error("zlm 代理失败: ", e); } } @@ -154,7 +152,7 @@ public class ProxyServletConfig { MediaServer mediaInfo = getMediaInfoByUri(requestURI); String url = super.rewriteUrlFromRequest(servletRequest); if (mediaInfo == null) { - logger.error("[ZLM服务访问代理],错误:处理url信息时未找到流媒体信息=>{}", requestURI); + log.error("[ZLM服务访问代理],错误:处理url信息时未找到流媒体信息=>{}", requestURI); return url; } if (!ObjectUtils.isEmpty(mediaInfo.getId())) { @@ -170,7 +168,7 @@ public class ProxyServletConfig { servletRegistrationBean.setName("record_proxy"); servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:18081"); servletRegistrationBean.addUrlMappings(); - if (logger.isDebugEnabled()) { + if (log.isDebugEnabled()) { servletRegistrationBean.addInitParameter("log", "true"); } return servletRegistrationBean; @@ -214,10 +212,10 @@ public class ProxyServletConfig { try { super.handleRequestException(proxyRequest, proxyResponse, e); } catch (ServletException servletException) { - logger.error("录像服务 代理失败: ", e); + log.error("录像服务 代理失败: ", e); } catch (IOException ioException) { if (ioException instanceof ConnectException) { - logger.error("录像服务 连接失败"); + log.error("录像服务 连接失败"); // }else if (ioException instanceof ClientAbortException) { // /** // * TODO 使用这个代理库实现代理在遇到代理视频文件时,如果是206结果,会遇到报错蛋市目前功能正常, @@ -225,10 +223,10 @@ public class ProxyServletConfig { // */ }else { - logger.error("录像服务 代理失败: ", e); + log.error("录像服务 代理失败: ", e); } } catch (RuntimeException exception){ - logger.error("录像服务 代理失败: ", e); + log.error("录像服务 代理失败: ", e); } } @@ -290,7 +288,7 @@ public class ProxyServletConfig { MediaServer mediaInfo = getMediaInfoByUri(requestURI); String url = super.rewriteUrlFromRequest(servletRequest); if (mediaInfo == null) { - logger.error("[录像服务访问代理],错误:处理url信息时未找到流媒体信息=>{}", requestURI); + log.error("[录像服务访问代理],错误:处理url信息时未找到流媒体信息=>{}", requestURI); return url; } if (!ObjectUtils.isEmpty(mediaInfo.getId())) { diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ServiceInfo.java b/src/main/java/com/genersoft/iot/vmp/conf/ServiceInfo.java index 55fbcf48f..3a503a896 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/ServiceInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/ServiceInfo.java @@ -1,16 +1,14 @@ package com.genersoft.iot.vmp.conf; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; +@Slf4j @Component public class ServiceInfo implements ApplicationListener { - private final Logger logger = LoggerFactory.getLogger(ServiceInfo.class); - private static int serverPort; public static int getServerPort() { @@ -21,7 +19,7 @@ public class ServiceInfo implements ApplicationListener parentPlatforms = storager.queryEnableParentPlatformList(true); + List parentPlatforms = platformService.queryEnablePlatformList(); - for (ParentPlatform parentPlatform : parentPlatforms) { + for (Platform platform : parentPlatforms) { - ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); + PlatformCatch platformCatchOld = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId()); // 更新缓存 - ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch(); - parentPlatformCatch.setParentPlatform(parentPlatform); - parentPlatformCatch.setId(parentPlatform.getServerGBId()); - redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); - if (parentPlatformCatchOld != null) { + PlatformCatch platformCatch = new PlatformCatch(); + platformCatch.setPlatform(platform); + platformCatch.setId(platform.getServerGBId()); + redisCatchStorage.updatePlatformCatchInfo(platformCatch); + if (platformCatchOld != null) { // 取消订阅 try { - sipCommanderForPlatform.unregister(parentPlatform, parentPlatformCatchOld.getSipTransactionInfo(), null, (eventResult)->{ - platformService.login(parentPlatform); + log.info("[平台主动注销] {}({})", platform.getName(), platform.getServerGBId()); + sipCommanderForPlatform.unregister(platform, platformCatchOld.getSipTransactionInfo(), null, (eventResult)->{ + platformService.login(platform); }); } catch (Exception e) { - logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); - platformService.offline(parentPlatform, true); + log.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); + platformService.offline(platform, true); continue; } + }else { + platformService.login(platform); } - // 设置所有平台离线 - platformService.offline(parentPlatform, false); + // 设置平台离线 + platformService.offline(platform, false); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java b/src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java index 40803cd1d..0c6eb2359 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java @@ -1,10 +1,8 @@ package com.genersoft.iot.vmp.conf; -import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd.AlarmQueryMessageHandler; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.utils.SystemInfoUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -15,11 +13,10 @@ import java.util.Map; /** * 获取系统信息写入redis */ +@Slf4j @Component public class SystemInfoTimerTask { - private Logger logger = LoggerFactory.getLogger(SystemInfoTimerTask.class); - @Autowired private IRedisCatchStorage redisCatchStorage; @@ -35,7 +32,7 @@ public class SystemInfoTimerTask { List> diskInfo =SystemInfoUtils.getDiskInfo(); redisCatchStorage.addDiskInfo(diskInfo); } catch (InterruptedException e) { - logger.error("[获取系统信息失败] {}", e.getMessage()); + log.error("[获取系统信息失败] {}", e.getMessage()); } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java index 18114c5a4..1d4293ba6 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java @@ -31,7 +31,7 @@ public class UserSetting { private Boolean recordSip = Boolean.TRUE; - private Boolean logInDatabase = Boolean.TRUE; + private Boolean logInDatabase = Boolean.FALSE; private Boolean usePushingAsStatus = Boolean.FALSE; diff --git a/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisMsgListenConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisMsgListenConfig.java index dcf2830c5..f70a9e319 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisMsgListenConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisMsgListenConfig.java @@ -32,7 +32,7 @@ public class RedisMsgListenConfig { private RedisPushStreamStatusMsgListener redisPushStreamStatusMsgListener; @Autowired - private RedisPushStreamStatusListMsgListener redisPushStreamListMsgListener; + private RedisPushStreamListMsgListener pushStreamListMsgListener; @Autowired @@ -61,7 +61,7 @@ public class RedisMsgListenConfig { container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS)); container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE)); container.addMessageListener(redisPushStreamStatusMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_STATUS_CHANGE)); - container.addMessageListener(redisPushStreamListMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_LIST_CHANGE)); + container.addMessageListener(pushStreamListMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_LIST_CHANGE)); container.addMessageListener(redisCloseStreamMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE)); container.addMessageListener(redisRpcConfig, new PatternTopic(RedisRpcConfig.REDIS_REQUEST_CHANNEL_KEY)); container.addMessageListener(redisPushStreamCloseResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_RESPONSE)); diff --git a/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisRpcConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisRpcConfig.java index 3fcc40c0b..b762838c6 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisRpcConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisRpcConfig.java @@ -7,8 +7,7 @@ import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcMessage; import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcRequest; import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcResponse; import com.genersoft.iot.vmp.service.redisMsg.control.RedisRpcController; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.Message; @@ -26,11 +25,10 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; +@Slf4j @Component public class RedisRpcConfig implements MessageListener { - private final static Logger logger = LoggerFactory.getLogger(RedisRpcConfig.class); - public final static String REDIS_REQUEST_CHANNEL_KEY = "WVP_REDIS_REQUEST_CHANNEL_KEY"; private final Random random = new Random(); @@ -65,10 +63,10 @@ public class RedisRpcConfig implements MessageListener { } else if (redisRpcMessage.getResponse() != null){ handlerResponse(redisRpcMessage.getResponse()); } else { - logger.error("[redis rpc 解析失败] {}", JSON.toJSONString(redisRpcMessage)); + log.error("[redis rpc 解析失败] {}", JSON.toJSONString(redisRpcMessage)); } } catch (Exception e) { - logger.error("[redis rpc 解析异常] ", e); + log.error("[redis rpc 解析异常] ", e); } } }); @@ -79,7 +77,7 @@ public class RedisRpcConfig implements MessageListener { if (userSetting.getServerId().equals(response.getToId())) { return; } - logger.info("[redis-rpc] << {}", response); + log.info("[redis-rpc] << {}", response); response(response); } @@ -88,7 +86,7 @@ public class RedisRpcConfig implements MessageListener { if (userSetting.getServerId().equals(request.getFromId())) { return; } - logger.info("[redis-rpc] << {}", request); + log.info("[redis-rpc] << {}", request); Method method = getMethod(request.getUri()); // 没有携带目标ID的可以理解为哪个wvp有结果就哪个回复,携带目标ID,但是如果是不存在的uri则直接回复404 if (userSetting.getServerId().equals(request.getToId())) { @@ -113,7 +111,7 @@ public class RedisRpcConfig implements MessageListener { } } }catch (InvocationTargetException | IllegalAccessException e) { - logger.error("[redis rpc ] 处理请求失败 ", e); + log.error("[redis rpc ] 处理请求失败 ", e); } } @@ -130,7 +128,7 @@ public class RedisRpcConfig implements MessageListener { } private void sendResponse(RedisRpcResponse response){ - logger.info("[redis-rpc] >> {}", response); + log.info("[redis-rpc] >> {}", response); response.setToId(userSetting.getServerId()); RedisRpcMessage message = new RedisRpcMessage(); message.setResponse(response); @@ -138,7 +136,7 @@ public class RedisRpcConfig implements MessageListener { } private void sendRequest(RedisRpcRequest request){ - logger.info("[redis-rpc] >> {}", request); + log.info("[redis-rpc] >> {}", request); RedisRpcMessage message = new RedisRpcMessage(); message.setRequest(request); redisTemplate.convertAndSend(REDIS_REQUEST_CHANNEL_KEY, message); @@ -156,7 +154,7 @@ public class RedisRpcConfig implements MessageListener { sendRequest(request); return subscribe.poll(timeOut, TimeUnit.SECONDS); } catch (InterruptedException e) { - logger.warn("[redis rpc timeout] uri: {}, sn: {}", request.getUri(), request.getSn(), e); + log.warn("[redis rpc timeout] uri: {}, sn: {}", request.getUri(), request.getSn(), e); } finally { this.unsubscribe(request.getSn()); } @@ -176,7 +174,7 @@ public class RedisRpcConfig implements MessageListener { try { return queue.offer(response, 2, TimeUnit.SECONDS); } catch (InterruptedException e) { - logger.error("{}", e.getMessage(), e); + log.error("{}", e.getMessage(), e); } }else if (callback != null) { callback.run(response); diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java index 0cda4a5cd..d21fdaaff 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java @@ -4,8 +4,7 @@ import com.alibaba.excel.util.StringUtils; import com.genersoft.iot.vmp.conf.security.dto.LoginUser; import com.genersoft.iot.vmp.service.IUserService; import com.genersoft.iot.vmp.storager.dao.dto.User; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; @@ -17,25 +16,24 @@ import java.time.LocalDateTime; /** * 用户登录认证逻辑 */ +@Slf4j @Component public class DefaultUserDetailsServiceImpl implements UserDetailsService { - private final static Logger logger = LoggerFactory.getLogger(DefaultUserDetailsServiceImpl.class); - @Autowired private IUserService userService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { if (StringUtils.isBlank(username)) { - logger.info("登录用户:{} 不存在", username); + log.info("登录用户:{} 不存在", username); throw new UsernameNotFoundException("登录用户:" + username + " 不存在"); } // 查出密码 User user = userService.getUserByUsername(username); if (user == null) { - logger.info("登录用户:{} 不存在", username); + log.info("登录用户:{} 不存在", username); throw new UsernameNotFoundException("登录用户:" + username + " 不存在"); } String password = SecurityUtils.encryptPassword(user.getPassword()); diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java index eacff1888..b1eb792ad 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.service.IUserApiKeyService; import com.genersoft.iot.vmp.service.IUserService; import com.genersoft.iot.vmp.storager.dao.dto.User; import com.genersoft.iot.vmp.storager.dao.dto.UserApiKey; +import lombok.extern.slf4j.Slf4j; import org.jose4j.jwk.JsonWebKey; import org.jose4j.jwk.JsonWebKeySet; import org.jose4j.jwk.RsaJsonWebKey; @@ -18,8 +19,6 @@ import org.jose4j.jwt.consumer.InvalidJwtException; import org.jose4j.jwt.consumer.JwtConsumer; import org.jose4j.jwt.consumer.JwtConsumerBuilder; import org.jose4j.lang.JoseException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; @@ -32,11 +31,10 @@ import java.time.ZoneOffset; import java.util.List; import java.util.Map; +@Slf4j @Component public class JwtUtils implements InitializingBean { - private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class); - public static final String HEADER = "access-token"; public static final String API_KEY_HEADER = "api-key"; @@ -75,7 +73,7 @@ public class JwtUtils implements InitializingBean { try { rsaJsonWebKey = generateRsaJsonWebKey(); } catch (JoseException e) { - logger.error("生成RsaJsonWebKey报错。", e); + log.error("生成RsaJsonWebKey报错。", e); } } @@ -145,7 +143,7 @@ public class JwtUtils implements InitializingBean { //get token return jws.getCompactSerialization(); } catch (JoseException e) { - logger.error("[Token生成失败]: {}", e.getMessage()); + log.error("[Token生成失败]: {}", e.getMessage()); } return null; } @@ -217,7 +215,7 @@ public class JwtUtils implements InitializingBean { } return jwtUser; } catch (Exception e) { - logger.error("[Token解析失败]: {}", e.getMessage()); + log.error("[Token解析失败]: {}", e.getMessage()); jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED); return jwtUser; } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/LogoutHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/security/LogoutHandler.java index 790eab842..48b40cbec 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/LogoutHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/LogoutHandler.java @@ -1,7 +1,6 @@ package com.genersoft.iot.vmp.conf.security; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import org.springframework.stereotype.Component; @@ -14,14 +13,13 @@ import java.io.IOException; /** * 退出登录成功 */ +@Slf4j @Component public class LogoutHandler implements LogoutSuccessHandler { - private final static Logger logger = LoggerFactory.getLogger(LogoutHandler.class); - @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { String username = request.getParameter("username"); - logger.info("[退出登录成功] - [{}]", username); + log.info("[退出登录成功] - [{}]", username); } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java index ee45e4d18..d5642d98d 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java @@ -1,8 +1,7 @@ package com.genersoft.iot.vmp.conf.security; import com.genersoft.iot.vmp.conf.UserSetting; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -36,10 +35,9 @@ import java.util.Collections; @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) @Order(1) +@Slf4j public class WebSecurityConfig extends WebSecurityConfigurerAdapter { - private final static Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class); - @Autowired private UserSetting userSetting; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java index d52fbc519..cbbb29596 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java @@ -7,8 +7,7 @@ import com.genersoft.iot.vmp.gb28181.conf.DefaultProperties; import com.genersoft.iot.vmp.gb28181.transmit.ISIPProcessorObserver; import gov.nist.javax.sip.SipProviderImpl; import gov.nist.javax.sip.SipStackImpl; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.core.annotation.Order; @@ -22,12 +21,11 @@ import java.net.NetworkInterface; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +@Slf4j @Component @Order(value=10) public class SipLayer implements CommandLineRunner { - private final static Logger logger = LoggerFactory.getLogger(SipLayer.class); - @Autowired private SipConfig sipConfig; @@ -60,16 +58,16 @@ public class SipLayer implements CommandLineRunner { if (nif.getName().startsWith("docker")) { continue; } - logger.info("[自动配置SIP监听网卡] 网卡接口地址: {}", addr.getHostAddress());// 只关心 IPv4 地址 + log.info("[自动配置SIP监听网卡] 网卡接口地址: {}", addr.getHostAddress());// 只关心 IPv4 地址 monitorIps.add(addr.getHostAddress()); } } } }catch (Exception e) { - logger.error("[读取网卡信息失败]", e); + log.error("[读取网卡信息失败]", e); } if (monitorIps.isEmpty()) { - logger.error("[自动配置SIP监听网卡信息失败], 请手动配置SIP.IP后重新启动"); + log.error("[自动配置SIP监听网卡信息失败], 请手动配置SIP.IP后重新启动"); System.exit(1); } }else { @@ -82,7 +80,9 @@ public class SipLayer implements CommandLineRunner { monitorIps.add(sipConfig.getIp()); } } - sipConfig.setShowIp(String.join(",", monitorIps)); + if (ObjectUtils.isEmpty(sipConfig.getShowIp())){ + sipConfig.setShowIp(String.join(",", monitorIps)); + } SipFactory.getInstance().setPathName("gov.nist"); if (monitorIps.size() > 0) { for (String monitorIp : monitorIps) { @@ -100,7 +100,7 @@ public class SipLayer implements CommandLineRunner { sipStack = (SipStackImpl)SipFactory.getInstance().createSipStack(DefaultProperties.getProperties("GB28181_SIP", userSetting.getSipLog())); sipStack.setMessageParserFactory(new GbStringMsgParserFactory()); } catch (PeerUnavailableException e) { - logger.error("[SIP SERVER] SIP服务启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp); + log.error("[SIP SERVER] SIP服务启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp); return; } @@ -111,12 +111,12 @@ public class SipLayer implements CommandLineRunner { tcpSipProvider.setDialogErrorsAutomaticallyHandled(); tcpSipProvider.addSipListener(sipProcessorObserver); tcpSipProviderMap.put(monitorIp, tcpSipProvider); - logger.info("[SIP SERVER] tcp://{}:{} 启动成功", monitorIp, port); + log.info("[SIP SERVER] tcp://{}:{} 启动成功", monitorIp, port); } catch (TransportNotSupportedException | TooManyListenersException | ObjectInUseException | InvalidArgumentException e) { - logger.error("[SIP SERVER] tcp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" + log.error("[SIP SERVER] tcp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" , monitorIp, port); } @@ -125,15 +125,15 @@ public class SipLayer implements CommandLineRunner { SipProviderImpl udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint); udpSipProvider.addSipListener(sipProcessorObserver); - + udpSipProvider.setDialogErrorsAutomaticallyHandled(); udpSipProviderMap.put(monitorIp, udpSipProvider); - logger.info("[SIP SERVER] udp://{}:{} 启动成功", monitorIp, port); + log.info("[SIP SERVER] udp://{}:{} 启动成功", monitorIp, port); } catch (TransportNotSupportedException | TooManyListenersException | ObjectInUseException | InvalidArgumentException e) { - logger.error("[SIP SERVER] udp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" + log.error("[SIP SERVER] udp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" , monitorIp, port); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java index 05ecb078c..2de1e913c 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java @@ -26,8 +26,7 @@ package com.genersoft.iot.vmp.gb28181.auth; import gov.nist.core.InternalErrorHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import javax.sip.address.URI; import javax.sip.header.AuthorizationHeader; @@ -46,19 +45,14 @@ import java.util.Random; * @author M. Ranganathan * @author Marc Bednarek */ - +@Slf4j public class DigestServerAuthenticationHelper { - private Logger logger = LoggerFactory.getLogger(DigestServerAuthenticationHelper.class); - private MessageDigest messageDigest; public static final String DEFAULT_ALGORITHM = "MD5"; public static final String DEFAULT_SCHEME = "Digest"; - - - /** to hex converter */ private static final char[] toHex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; @@ -205,17 +199,17 @@ public class DigestServerAuthenticationHelper { byte mdbytes[] = messageDigest.digest(A1.getBytes()); String HA1 = toHexString(mdbytes); - logger.debug("A1: " + A1); - logger.debug("A2: " + A2); + log.debug("A1: " + A1); + log.debug("A2: " + A2); mdbytes = messageDigest.digest(A2.getBytes()); String HA2 = toHexString(mdbytes); - logger.debug("HA1: " + HA1); - logger.debug("HA2: " + HA2); + log.debug("HA1: " + HA1); + log.debug("HA2: " + HA2); // String cnonce = authHeader.getCNonce(); - logger.debug("nonce: " + nonce); - logger.debug("nc: " + ncStr); - logger.debug("cnonce: " + cnonce); - logger.debug("qop: " + qop); + log.debug("nonce: " + nonce); + log.debug("nc: " + ncStr); + log.debug("cnonce: " + cnonce); + log.debug("qop: " + qop); String KD = HA1 + ":" + nonce; if (qop != null && qop.equalsIgnoreCase("auth") ) { @@ -228,12 +222,12 @@ public class DigestServerAuthenticationHelper { KD += ":" + qop; } KD += ":" + HA2; - logger.debug("KD: " + KD); + log.debug("KD: " + KD); mdbytes = messageDigest.digest(KD.getBytes()); String mdString = toHexString(mdbytes); - logger.debug("mdString: " + mdString); + log.debug("mdString: " + mdString); String response = authHeader.getResponse(); - logger.debug("response: " + response); + log.debug("response: " + response); return mdString.equals(response); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java index 2b5e16bd1..e1c9c9c73 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java @@ -1,20 +1,22 @@ package com.genersoft.iot.vmp.gb28181.bean; +import com.genersoft.iot.vmp.gb28181.controller.bean.AudioBroadcastEvent; import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; import gov.nist.javax.sip.message.SIPResponse; +import lombok.Data; /** * 缓存语音广播的状态 * @author lin */ +@Data public class AudioBroadcastCatch { public AudioBroadcastCatch( String deviceId, - String channelId, + Integer channelId, MediaServer mediaServerItem, String app, String stream, @@ -43,7 +45,7 @@ public class AudioBroadcastCatch { /** * 通道编号 */ - private String channelId; + private Integer channelId; /** * 流媒体信息 @@ -81,79 +83,7 @@ public class AudioBroadcastCatch { private AudioBroadcastEvent event; - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getChannelId() { - return channelId; - } - - public void setChannelId(String channelId) { - this.channelId = channelId; - } - - public AudioBroadcastCatchStatus getStatus() { - return status; - } - - public void setStatus(AudioBroadcastCatchStatus status) { - this.status = status; - } - - public SipTransactionInfo getSipTransactionInfo() { - return sipTransactionInfo; - } - - public MediaServer getMediaServerItem() { - return mediaServerItem; - } - - public void setMediaServerItem(MediaServer mediaServerItem) { - this.mediaServerItem = mediaServerItem; - } - - 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 boolean isFromPlatform() { - return isFromPlatform; - } - - public void setFromPlatform(boolean fromPlatform) { - isFromPlatform = fromPlatform; - } - - public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { - this.sipTransactionInfo = sipTransactionInfo; - } - - public AudioBroadcastEvent getEvent() { - return event; - } - - public void setEvent(AudioBroadcastEvent event) { - this.event = event; - } - - public void setSipTransactionInfoByRequset(SIPResponse sipResponse) { + public void setSipTransactionInfoByRequest(SIPResponse sipResponse) { this.sipTransactionInfo = new SipTransactionInfo(sipResponse); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogChannelEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogChannelEvent.java new file mode 100644 index 000000000..cf7c9cbda --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogChannelEvent.java @@ -0,0 +1,38 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.dom4j.Element; + +import java.lang.reflect.InvocationTargetException; + +@Data +@Slf4j +@EqualsAndHashCode(callSuper = true) +public class CatalogChannelEvent extends DeviceChannel{ + + private String event; + + private DeviceChannel channel; + + public static CatalogChannelEvent decode(Element element) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { + Element eventElement = element.element("Event"); + CatalogChannelEvent catalogChannelEvent = new CatalogChannelEvent(); + if (eventElement != null) { + catalogChannelEvent.setEvent(eventElement.getText()); + }else { + catalogChannelEvent.setEvent(CatalogEvent.ADD); + } + DeviceChannel deviceChannel; + if (CatalogEvent.ADD.equalsIgnoreCase(catalogChannelEvent.getEvent()) || + CatalogEvent.UPDATE.equalsIgnoreCase(catalogChannelEvent.getEvent()) ){ + deviceChannel = DeviceChannel.decode(element); + }else { + deviceChannel = DeviceChannel.decodeWithOnlyDeviceId(element); + } + catalogChannelEvent.setChannel(deviceChannel); + return catalogChannelEvent; + } +} 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 965d7f2af..4666b7070 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 @@ -1,11 +1,14 @@ package com.genersoft.iot.vmp.gb28181.bean; +import lombok.Data; + import java.time.Instant; import java.util.List; /** * @author lin */ +@Data public class CatalogData { /** * 命令序列号 @@ -13,6 +16,8 @@ public class CatalogData { private int sn; private int total; private List channelList; + private List regionListList; + private List groupListListList; private Instant lastTime; private Device device; private String errorMsg; @@ -21,61 +26,4 @@ public class CatalogData { ready, runIng, end } private CatalogDataStatus status; - - - public int getSn() { - return sn; - } - - public void setSn(int sn) { - this.sn = sn; - } - - public int getTotal() { - return total; - } - - public void setTotal(int total) { - this.total = total; - } - - public List getChannelList() { - return channelList; - } - - public void setChannelList(List channelList) { - this.channelList = channelList; - } - - public Instant getLastTime() { - return lastTime; - } - - public void setLastTime(Instant lastTime) { - this.lastTime = lastTime; - } - - public Device getDevice() { - return device; - } - - public void setDevice(Device device) { - this.device = device; - } - - public String getErrorMsg() { - return errorMsg; - } - - public void setErrorMsg(String errorMsg) { - this.errorMsg = errorMsg; - } - - public CatalogDataStatus getStatus() { - return status; - } - - public void setStatus(CatalogDataStatus status) { - this.status = status; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CommonGBChannel.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CommonGBChannel.java index 4ce14dc3b..6327688b2 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CommonGBChannel.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CommonGBChannel.java @@ -1,5 +1,6 @@ package com.genersoft.iot.vmp.gb28181.bean; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -7,8 +8,8 @@ import lombok.Data; @Schema(description = "国标通道") public class CommonGBChannel { - @Schema(description = "国标-数据库自增Id") - private Integer gbId; + @Schema(description = "国标-数据库自增ID") + private int gbId; @Schema(description = "国标-编码") private String gbDeviceId; @@ -36,7 +37,7 @@ public class CommonGBChannel { private String gbAddress; @Schema(description = "国标-是否有子设备") - private Boolean gbParental; + private Integer gbParental; @Schema(description = "国标-父节点ID") private String gbParentId; @@ -50,7 +51,7 @@ public class CommonGBChannel { // 2016 @Schema(description = "国标-证书序列号") - private Integer gbCertNum; + private String gbCertNum; // 2016 @Schema(description = "国标-证书有效标识") @@ -62,11 +63,7 @@ public class CommonGBChannel { // 2016 @Schema(description = "国标-证书终止有效期(有证书且证书无效的设备必选)") - private Integer gbEndTime; - - // 2022 - @Schema(description = "国标-摄像机安全能力等级代码") - private String gbSecurityLevelCode; + private String gbEndTime; @Schema(description = "国标-保密属性(必选)缺省为0;0-不涉密,1-涉密") private Integer gbSecrecy; @@ -81,12 +78,12 @@ public class CommonGBChannel { private String gbPassword; @Schema(description = "国标-设备状态") - private Boolean gbStatus; + private String gbStatus; @Schema(description = "国标-经度 WGS-84坐标系") private Double gbLongitude; - @Schema(description = "国标-,纬度 WGS-84坐标系") + @Schema(description = "国标-纬度 WGS-84坐标系") private Double gbLatitude; @Schema(description = "国标-虚拟组织所属的业务分组ID") @@ -95,11 +92,10 @@ public class CommonGBChannel { @Schema(description = "国标-摄像机结构类型,标识摄像机类型: 1-球机; 2-半球; 3-固定枪机; 4-遥控枪机;5-遥控半球;6-多目设备的全景/拼接通道;7-多目设备的分割通道") private Integer gbPtzType; - @Schema(description = "国标-摄像机光电成像类型。1-可见光成像;2-热成像;3-雷达成像;4-X光成像;5-深度光场成像;9-其他。可多值,") - private String gbPhotoelectricImagingTyp; - - @Schema(description = "国标-摄像机采集部位类型") - private String gbCapturePositionType; + // 2016 + @Schema(description = "-摄像机位置类型扩展。1-省际检查站、2-党政机关、3-车站码头、4-中心广场、5-体育场馆、6-商业中心、7-宗教场所、" + + "8-校园周边、9-治安复杂区域、10-交通干线。当目录项为摄像机时可选。") + private Integer gbPositionType; @Schema(description = "国标-摄像机安装位置室外、室内属性。1-室外、2-室内。") private Integer gbRoomType; @@ -118,10 +114,6 @@ public class CommonGBChannel { @Schema(description = "国标-摄像机支持的分辨率,可多值") private String gbResolution; - // 2022 - @Schema(description = "国标-摄像机支持的码流编号列表,用于实时点播时指定码流编号(可选)") - private String gbStreamNumberList; - @Schema(description = "国标-下载倍速(可选),可多值") private String gbDownloadSpeed; @@ -131,70 +123,256 @@ public class CommonGBChannel { @Schema(description = "国标-时域编码能力,取值0-不支持;1-1级增强;2-2级增强;3-3级增强(可选)") private Integer gbSvcTimeSupportMode; - // 2022 - @Schema(description = "国标- SSVC增强层与基本层比例能力 ") - private String gbSsvcRatioSupportList; + @Schema(description = "关联的国标设备数据库ID") + private Integer gbDeviceDbId; - // 2022 - @Schema(description = "国标-移动采集设备类型(仅移动采集设备适用,必选);1-移动机器人载摄像机;2-执法记录仪;3-移动单兵设备;" + - "4-车载视频记录设备;5-无人机载摄像机;9-其他") - private Integer gbMobileDeviceType; + @Schema(description = "关联的推流Id(流来源是推流时有效)") + private Integer streamPushId; - // 2022 - @Schema(description = "国标-摄像机水平视场角(可选),取值范围大于0度小于等于360度") - private Double gbHorizontalFieldAngle; + @Schema(description = "关联的拉流代理Id(流来源是拉流代理时有效)") + private Integer streamProxyId; - // 2022 - @Schema(description = "国标-摄像机竖直视场角(可选),取值范围大于0度小于等于360度 ") - private Double gbVerticalFieldAngle; + @Schema(description = "创建时间") + private String createTime; - // 2022 - @Schema(description = "国标-摄像机可视距离(可选),单位:米") - private Double gbMaxViewDistance; + @Schema(description = "更新时间") + private String updateTime; - // 2022 - @Schema(description = "国标-基层组织编码(必选,非基层建设时为“000000”)") - private String gbGrassrootsCode; + public String encode(String serverDeviceId) { + return encode(null, serverDeviceId); + } - // 2022 - @Schema(description = "国标-监控点位类型(当为摄像机时必选),1-一类视频监控点;2-二类视频监控点;3-三类视频监控点;9-其他点位。") - private Integer gbPoType; + public String encode(String event,String serverDeviceId) { + String content; + if (event == null) { + return getFullContent(null, serverDeviceId); + } + switch (event) { + case CatalogEvent.DEL: + case CatalogEvent.DEFECT: + case CatalogEvent.VLOST: + content = "\n" + + "" + this.getGbDeviceId() + "\n" + + "" + event + "\n" + + "\n"; + break; + case CatalogEvent.ON: + case CatalogEvent.OFF: + content = "\n" + + "" + this.getGbDeviceId() + "\n" + + "" + event + "\r\n" + + "\n"; + break; + case CatalogEvent.ADD: + case CatalogEvent.UPDATE: + content = getFullContent(event, serverDeviceId); + break; + default: + content = null; + break; + } + return content; + } - // 2022 - @Schema(description = "国标-点位俗称") - private String gbPoCommonName; + private String getFullContent(String event, String serverDeviceId) { + StringBuilder content = new StringBuilder(); + // 行政区划目录项 + content.append("\n") + .append("" + this.getGbDeviceId() + "\n") + .append("" + this.getGbName() + "\n"); - // 2022 - @Schema(description = "国标-设备MAC地址(可选),用“XX-XX-XX-XX-XX-XX”格式表达") - private String gbMac; - // 2022 - @Schema(description = "国标-摄像机卡口功能类型,01-人脸卡口;02-人员卡口;03-机动车卡口;04-非机动车卡口;05-物品卡口;99-其他") - private String gbFunctionType; + if (this.getGbDeviceId().length() > 8) { - // 2022 - @Schema(description = "国标-摄像机视频编码格式") - private String gbEncodeType; + String type = this.getGbDeviceId().substring(10, 13); + if (type.equals("200")) { + // 业务分组目录项 + if (this.getGbManufacturer() != null) { + content.append("" + this.getGbManufacturer() + "\n"); + } + if (this.getGbModel() != null) { + content.append("" + this.getGbModel() + "\n"); + } + if (this.getGbOwner() != null) { + content.append("" + this.getGbOwner() + "\n"); + } + if (this.getGbCivilCode() != null) { + content.append("" + this.getGbCivilCode() + "\n"); + } + if (this.getGbAddress() != null) { + content.append("
" + this.getGbAddress() + "
\n"); + } + if (this.getGbRegisterWay() != null) { + content.append("" + this.getGbRegisterWay() + "\n"); + } + if (this.getGbSecrecy() != null) { + content.append("" + this.getGbSecrecy() + "\n"); + } + } else if (type.equals("215")) { + // 业务分组 + if (this.getGbCivilCode() != null) { + content.append("" + this.getGbCivilCode() + "\n"); + } + content.append("" + serverDeviceId + "\n"); + } else if (type.equals("216")) { + // 虚拟组织目录项 + if (this.getGbCivilCode() != null) { + content.append("" + this.getGbCivilCode() + "\n"); + } + if (this.getGbParentId() != null) { + content.append("" + this.getGbParentId() + "\n"); + } + content.append("" + this.getGbBusinessGroupId() + "\n"); + } else { + if (this.getGbManufacturer() != null) { + content.append("" + this.getGbManufacturer() + "\n"); + } + if (this.getGbModel() != null) { + content.append("" + this.getGbModel() + "\n"); + } + if (this.getGbOwner() != null) { + content.append("" + this.getGbOwner() + "\n"); + } + if (this.getGbCivilCode() != null) { + content.append("" + this.getGbCivilCode() + "\n"); + } + if (this.getGbAddress() != null) { + content.append("
" + this.getGbAddress() + "
\n"); + } + if (this.getGbRegisterWay() != null) { + content.append("" + this.getGbRegisterWay() + "\n"); + } + if (this.getGbSecrecy() != null) { + content.append("" + this.getGbSecrecy() + "\n"); + } + if (this.getGbParentId() != null) { + content.append("" + this.getGbParentId() + "\n"); + } + if (this.getGbParental() != null) { + content.append("" + this.getGbParental() + "\n"); + } + if (this.getGbSafetyWay() != null) { + content.append("" + this.getGbSafetyWay() + "\n"); + } + if (this.getGbRegisterWay() != null) { + content.append("" + this.getGbRegisterWay() + "\n"); + } + if (this.getGbCertNum() != null) { + content.append("" + this.getGbCertNum() + "\n"); + } + if (this.getGbCertifiable() != null) { + content.append("" + this.getGbCertifiable() + "\n"); + } + if (this.getGbErrCode() != null) { + content.append("" + this.getGbErrCode() + "\n"); + } + if (this.getGbEndTime() != null) { + content.append("" + this.getGbEndTime() + "\n"); + } + if (this.getGbSecrecy() != null) { + content.append("" + this.getGbSecrecy() + "\n"); + } + if (this.getGbIpAddress() != null) { + content.append("" + this.getGbIpAddress() + "\n"); + } + if (this.getGbPort() != null) { + content.append("" + this.getGbPort() + "\n"); + } + if (this.getGbPassword() != null) { + content.append("" + this.getGbPassword() + "\n"); + } + if (this.getGbStatus() != null) { + content.append("" + this.getGbStatus() + "\n"); + } + if (this.getGbLongitude() != null) { + content.append("" + this.getGbLongitude() + "\n"); + } + if (this.getGbLatitude() != null) { + content.append("" + this.getGbLatitude() + "\n"); + } + content.append("\n"); - // 2022 - @Schema(description = "国标-摄像机安装使用时间") - private String gbInstallTime; + if (this.getGbPtzType() != null) { + content.append(" " + this.getGbPtzType() + "\n"); + } + if (this.getGbPositionType() != null) { + content.append(" " + this.getGbPositionType() + "\n"); + } + if (this.getGbRoomType() != null) { + content.append(" " + this.getGbRoomType() + "\n"); + } + if (this.getGbUseType() != null) { + content.append(" " + this.getGbUseType() + "\n"); + } + if (this.getGbSupplyLightType() != null) { + content.append(" " + this.getGbSupplyLightType() + "\n"); + } + if (this.getGbDirectionType() != null) { + content.append(" " + this.getGbDirectionType() + "\n"); + } + if (this.getGbResolution() != null) { + content.append(" " + this.getGbResolution() + "\n"); + } + if (this.getGbBusinessGroupId() != null) { + content.append(" " + this.getGbBusinessGroupId() + "\n"); + } + if (this.getGbDownloadSpeed() != null) { + content.append(" " + this.getGbDownloadSpeed() + "\n"); + } + if (this.getGbSvcSpaceSupportMod() != null) { + content.append(" " + this.getGbSvcSpaceSupportMod() + "\n"); + } + if (this.getGbSvcTimeSupportMode() != null) { + content.append(" " + this.getGbSvcTimeSupportMode() + "\n"); + } + content.append("\n"); + } + } + if (event != null) { + content.append("" + event + "\n"); + } + content.append("
\n"); + return content.toString(); + } - // 2022 - @Schema(description = "国标-摄像机所属管理单位名称") - private String gbManagementUnit; + public static CommonGBChannel build(Group group) { + GbCode gbCode = GbCode.decode(group.getDeviceId()); + CommonGBChannel channel = new CommonGBChannel(); + if (gbCode.getTypeCode().equals("215")) { + // 业务分组 + channel.setGbName(group.getName()); + channel.setGbDeviceId(group.getDeviceId()); + channel.setGbCivilCode(group.getCivilCode()); + } else { + // 虚拟组织 + channel.setGbName(group.getName()); + channel.setGbDeviceId(group.getDeviceId()); + channel.setGbParentId(group.getParentDeviceId()); + channel.setGbBusinessGroupId(group.getBusinessGroup()); + channel.setGbCivilCode(group.getCivilCode()); + } + return channel; + } - // 2022 - @Schema(description = "国标-摄像机所属管理单位联系人的联系方式(电话号码,可多值,用英文半角“/”分割)") - private String gbContactInfo; - - // 2022 - @Schema(description = "国标-录像保存天数(可选)") - private Integer gbRecordSaveDays; - - // 2022 - @Schema(description = "国标-国民经济行业分类代码(可选)") - private String gbIndustrialClassification; + public static CommonGBChannel build(Platform platform) { + CommonGBChannel commonGBChannel = new CommonGBChannel(); + commonGBChannel.setGbDeviceId(platform.getDeviceGBId()); + commonGBChannel.setGbName(platform.getName()); + commonGBChannel.setGbManufacturer(platform.getManufacturer()); + commonGBChannel.setGbModel(platform.getModel()); + commonGBChannel.setGbCivilCode(platform.getCivilCode()); + commonGBChannel.setGbAddress(platform.getAddress()); + commonGBChannel.setGbRegisterWay(platform.getRegisterWay()); + commonGBChannel.setGbSecrecy(platform.getSecrecy()); + commonGBChannel.setGbStatus(platform.isStatus() ? "ON" : "OFF"); + return commonGBChannel; + } + public static CommonGBChannel build(Region region) { + CommonGBChannel commonGBChannel = new CommonGBChannel(); + commonGBChannel.setGbDeviceId(region.getDeviceId()); + commonGBChannel.setGbName(region.getName()); + return commonGBChannel; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java index a9752f127..1e783e867 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java @@ -2,14 +2,19 @@ package com.genersoft.iot.vmp.gb28181.bean; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; /** * 国标设备/平台 * @author lin */ +@Data @Schema(description = "国标设备/平台") public class Device { + @Schema(description = "数据库自增ID") + private int id; + /** * 设备国标编号 */ @@ -168,7 +173,7 @@ public class Device { private boolean ssrcCheck = false; /** - * 地理坐标系, 目前支持 WGS84,GCJ02 + * 地理坐标系, 目前支持 WGS84,GCJ02, 此字段保留,暂无用 */ @Schema(description = "地理坐标系, 目前支持 WGS84,GCJ02") private String geoCoordSys; @@ -190,273 +195,4 @@ public class Device { @Schema(description = "控制语音对讲流程,释放收到ACK后发流") private boolean broadcastPushAfterAck; - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getManufacturer() { - return manufacturer; - } - - public void setManufacturer(String manufacturer) { - this.manufacturer = manufacturer; - } - - public String getModel() { - return model; - } - - public void setModel(String model) { - this.model = model; - } - - public String getFirmware() { - return firmware; - } - - public void setFirmware(String firmware) { - this.firmware = firmware; - } - - public String getTransport() { - return transport; - } - - public void setTransport(String transport) { - this.transport = transport; - } - - public String getStreamMode() { - return streamMode; - } - - public Integer getStreamModeForParam() { - if (streamMode == null) { - return 0; - } - if (streamMode.equalsIgnoreCase("UDP")) { - return 0; - }else if (streamMode.equalsIgnoreCase("TCP-PASSIVE")) { - return 1; - }else if (streamMode.equalsIgnoreCase("TCP-ACTIVE")) { - return 2; - } - return 0; - } - - public void setStreamMode(String streamMode) { - this.streamMode = streamMode; - } - - public String getIp() { - return ip; - } - - public void setIp(String ip) { - this.ip = ip; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public String getHostAddress() { - return hostAddress; - } - - public void setHostAddress(String hostAddress) { - this.hostAddress = hostAddress; - } - - public boolean isOnLine() { - return onLine; - } - - public void setOnLine(boolean onLine) { - this.onLine = onLine; - } - - public int getChannelCount() { - return channelCount; - } - - public void setChannelCount(int channelCount) { - this.channelCount = channelCount; - } - - public String getRegisterTime() { - return registerTime; - } - - public void setRegisterTime(String registerTime) { - this.registerTime = registerTime; - } - - public String getKeepaliveTime() { - return keepaliveTime; - } - - public void setKeepaliveTime(String keepaliveTime) { - this.keepaliveTime = keepaliveTime; - } - - public int getExpires() { - return expires; - } - - public void setExpires(int expires) { - this.expires = expires; - } - - public String getCreateTime() { - return createTime; - } - - public void setCreateTime(String createTime) { - this.createTime = createTime; - } - - public String getUpdateTime() { - return updateTime; - } - - public void setUpdateTime(String updateTime) { - this.updateTime = updateTime; - } - - public String getMediaServerId() { - return mediaServerId; - } - - public void setMediaServerId(String mediaServerId) { - this.mediaServerId = mediaServerId; - } - - public String getCharset() { - return charset; - } - - public void setCharset(String charset) { - this.charset = charset; - } - - public int getSubscribeCycleForCatalog() { - return subscribeCycleForCatalog; - } - - public void setSubscribeCycleForCatalog(int subscribeCycleForCatalog) { - this.subscribeCycleForCatalog = subscribeCycleForCatalog; - } - - public int getSubscribeCycleForMobilePosition() { - return subscribeCycleForMobilePosition; - } - - public void setSubscribeCycleForMobilePosition(int subscribeCycleForMobilePosition) { - this.subscribeCycleForMobilePosition = subscribeCycleForMobilePosition; - } - - public int getMobilePositionSubmissionInterval() { - return mobilePositionSubmissionInterval; - } - - public void setMobilePositionSubmissionInterval(int mobilePositionSubmissionInterval) { - this.mobilePositionSubmissionInterval = mobilePositionSubmissionInterval; - } - - public int getSubscribeCycleForAlarm() { - return subscribeCycleForAlarm; - } - - public void setSubscribeCycleForAlarm(int subscribeCycleForAlarm) { - this.subscribeCycleForAlarm = subscribeCycleForAlarm; - } - - public boolean isSsrcCheck() { - return ssrcCheck; - } - - public void setSsrcCheck(boolean ssrcCheck) { - this.ssrcCheck = ssrcCheck; - } - - public String getGeoCoordSys() { - return geoCoordSys; - } - - public void setGeoCoordSys(String geoCoordSys) { - this.geoCoordSys = geoCoordSys; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getSdpIp() { - return sdpIp; - } - - public void setSdpIp(String sdpIp) { - this.sdpIp = sdpIp; - } - - public String getLocalIp() { - return localIp; - } - - public void setLocalIp(String localIp) { - this.localIp = localIp; - } - - public int getKeepaliveIntervalTime() { - return keepaliveIntervalTime; - } - - public void setKeepaliveIntervalTime(int keepaliveIntervalTime) { - this.keepaliveIntervalTime = keepaliveIntervalTime; - } - - public boolean isAsMessageChannel() { - return asMessageChannel; - } - - public void setAsMessageChannel(boolean asMessageChannel) { - this.asMessageChannel = asMessageChannel; - } - - public SipTransactionInfo getSipTransactionInfo() { - return sipTransactionInfo; - } - - public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { - this.sipTransactionInfo = sipTransactionInfo; - } - public boolean isBroadcastPushAfterAck() { - return broadcastPushAfterAck; - } - - public void setBroadcastPushAfterAck(boolean broadcastPushAfterAck) { - this.broadcastPushAfterAck = broadcastPushAfterAck; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java index 6e230afef..28eb01a8e 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java @@ -1,260 +1,186 @@ package com.genersoft.iot.vmp.gb28181.bean; +import com.genersoft.iot.vmp.gb28181.utils.MessageElementForCatalog; +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.dom4j.Element; +import org.springframework.util.ObjectUtils; +import java.lang.reflect.InvocationTargetException; + +@Data +@Slf4j @Schema(description = "通道信息") -public class DeviceChannel { +@EqualsAndHashCode(callSuper = true) +public class DeviceChannel extends CommonGBChannel { - - /** - * 数据库自增ID - */ @Schema(description = "数据库自增ID") private int id; - /** - * 通道国标编号 - */ - @Schema(description = "通道国标编号") - private String channelId; + @Schema(description = "设备的数据库自增ID") + private Integer deviceDbId; - /** - * 设备国标编号 - */ - @Schema(description = "设备国标编号") + @MessageElementForCatalog("DeviceID") + @Schema(description = "编码") private String deviceId; - - /** - * 通道名 - */ + + @MessageElementForCatalog("Name") @Schema(description = "名称") private String name; - - /** - * 生产厂商 - */ - @Schema(description = "生产厂商") - private String manufacture; - - /** - * 型号 - */ - @Schema(description = "型号") + + @MessageElementForCatalog("Manufacturer") + @Schema(description = "设备厂商") + private String manufacturer; + + @MessageElementForCatalog("Model") + @Schema(description = "设备型号") private String model; - - /** - * 设备归属 - */ + + // 2016 + @MessageElementForCatalog("Owner") @Schema(description = "设备归属") private String owner; - - /** - * 行政区域 - */ + + @MessageElementForCatalog("CivilCode") @Schema(description = "行政区域") private String civilCode; - - /** - * 警区 - */ + + @MessageElementForCatalog("Block") @Schema(description = "警区") private String block; - /** - * 安装地址 - */ + @MessageElementForCatalog("Address") @Schema(description = "安装地址") private String address; - - /** - * 是否有子设备 1有, 0没有 - */ - @Schema(description = "是否有子设备 1有, 0没有") - private int parental; - - /** - * 父级id - */ - @Schema(description = "父级id") + + @MessageElementForCatalog("Parental") + @Schema(description = "是否有子设备(必选)1有,0没有") + private Integer parental; + + + @MessageElementForCatalog("ParentID") + @Schema(description = "父节点ID") private String parentId; - - /** - * 信令安全模式 缺省为0; 0:不采用; 2: S/MIME签名方式; 3: S/ MIME加密签名同时采用方式; 4:数字摘要方式 - */ - @Schema(description = "信令安全模式 缺省为0; 0:不采用; 2: S/MIME签名方式; 3: S/ MIME加密签名同时采用方式; 4:数字摘要方式") - private int safetyWay; - - /** - * 注册方式 缺省为1;1:符合IETFRFC3261标准的认证注册模 式; 2:基于口令的双向认证注册模式; 3:基于数字证书的双向认证注册模式 - */ - @Schema(description = "注册方式 缺省为1;1:符合IETFRFC3261标准的认证注册模 式; 2:基于口令的双向认证注册模式; 3:基于数字证书的双向认证注册模式") - private int registerWay; - - /** - * 证书序列号 - */ + + // 2016 + @MessageElementForCatalog("SafetyWay") + @Schema(description = "信令安全模式") + private Integer safetyWay; + + @MessageElementForCatalog("RegisterWay") + @Schema(description = "注册方式") + private Integer registerWay; + + // 2016 + @MessageElementForCatalog("CertNum") @Schema(description = "证书序列号") private String certNum; - - /** - * 证书有效标识 缺省为0;证书有效标识:0:无效1: 有效 - */ - @Schema(description = "证书有效标识 缺省为0;证书有效标识:0:无效1: 有效") - private int certifiable; - - /** - * 证书无效原因码 - */ - @Schema(description = "证书无效原因码") - private int errCode; - - /** - * 证书终止有效期 - */ - @Schema(description = "证书终止有效期") + + // 2016 + @MessageElementForCatalog("Certifiable") + @Schema(description = "证书有效标识, 缺省为0;证书有效标识:0:无效 1:有效") + private Integer certifiable; + + // 2016 + @MessageElementForCatalog("ErrCode") + @Schema(description = "无效原因码(有证书且证书无效的设备必选)") + private Integer errCode; + + // 2016 + @MessageElementForCatalog("EndTime") + @Schema(description = "证书终止有效期(有证书且证书无效的设备必选)") private String endTime; - - /** - * 保密属性 缺省为0; 0:不涉密, 1:涉密 - */ - @Schema(description = "保密属性 缺省为0; 0:不涉密, 1:涉密") - private String secrecy; - - /** - * IP地址 - */ - @Schema(description = "IP地址") + + @MessageElementForCatalog("Secrecy") + @Schema(description = "保密属性(必选)缺省为0;0-不涉密,1-涉密") + private Integer secrecy; + + @MessageElementForCatalog("IPAddress") + @Schema(description = "设备/系统IPv4/IPv6地址") private String ipAddress; - - /** - * 端口号 - */ - @Schema(description = "端口号") - private int port; - - /** - * 密码 - */ - @Schema(description = "密码") + + @MessageElementForCatalog("Port") + @Schema(description = "设备/系统端口") + private Integer port; + + @MessageElementForCatalog("Password") + @Schema(description = "设备口令") private String password; - /** - * 云台类型 - */ - @Schema(description = "云台类型") - private int ptzType; + @MessageElementForCatalog("Status") + @Schema(description = "设备状态") + private String status; + + @MessageElementForCatalog("Longitude") + @Schema(description = "经度 WGS-84坐标系") + private Double longitude; + + + @MessageElementForCatalog("Latitude") + @Schema(description = ",纬度 WGS-84坐标系") + private Double latitude; + + @MessageElementForCatalog("Info.PTZType") + @Schema(description = "摄像机结构类型,标识摄像机类型: 1-球机; 2-半球; 3-固定枪机; 4-遥控枪机;5-遥控半球;6-多目设备的全景/拼接通道;7-多目设备的分割通道") + private Integer ptzType; + + @MessageElementForCatalog("Info.PositionType") + @Schema(description = "摄像机位置类型扩展。1-省际检查站、2-党政机关、3-车站码头、4-中心广场、5-体育场馆、" + + "6-商业中心、7-宗教场所、8-校园周边、9-治安复杂区域、10-交通干线") + private Integer positionType; + + @MessageElementForCatalog("Info.RoomType") + @Schema(description = "摄像机安装位置室外、室内属性。1-室外、2-室内。") + private Integer roomType; + + @MessageElementForCatalog("Info.UseType") + @Schema(description = "用途属性, 1-治安、2-交通、3-重点。") + private Integer useType; + + @MessageElementForCatalog("Info.SupplyLightType") + @Schema(description = "摄像机补光属性。1-无补光;2-红外补光;3-白光补光;4-激光补光;9-其他") + private Integer supplyLightType; + + @MessageElementForCatalog("Info.DirectionType") + @Schema(description = "摄像机监视方位(光轴方向)属性。1-东(西向东)、2-西(东向西)、3-南(北向南)、4-北(南向北)、" + + "5-东南(西北到东南)、6-东北(西南到东北)、7-西南(东北到西南)、8-西北(东南到西北)") + private Integer directionType; + + @MessageElementForCatalog("Info.Resolution") + @Schema(description = "摄像机支持的分辨率,可多值") + private String resolution; + + @MessageElementForCatalog({"BusinessGroupID","Info.BusinessGroupID"}) + @Schema(description = "虚拟组织所属的业务分组ID") + private String businessGroupId; + + @MessageElementForCatalog("Info.DownloadSpeed") + @Schema(description = "下载倍速(可选),可多值") + private String downloadSpeed; + + @MessageElementForCatalog("Info.SVCSpaceSupportMode") + @Schema(description = "空域编码能力,取值0-不支持;1-1级增强(1个增强层);2-2级增强(2个增强层);3-3级增强(3个增强层)") + private Integer svcSpaceSupportMod; + + @MessageElementForCatalog("Info.SVCTimeSupportMode") + @Schema(description = "时域编码能力,取值0-不支持;1-1级增强;2-2级增强;3-3级增强(可选)") + private Integer svcTimeSupportMode; - /** - * 云台类型描述字符串 - */ @Schema(description = "云台类型描述字符串") private String ptzTypeText; - /** - * 创建时间 - */ - @Schema(description = "创建时间") - private String createTime; - - /** - * 更新时间 - */ - @Schema(description = "更新时间") - private String updateTime; - - /** - * 在线/离线 - * 1在线,0离线 - * 默认在线 - * 信令: - * ON - * OFF - * 遇到过NVR下的IPC下发信令可以推流, 但是 Status 响应 OFF - */ - @Schema(description = "在线/离线, 1在线,0离线") - private boolean status; - - /** - * 经度 - */ - @Schema(description = "经度") - private double longitude; - - /** - * 纬度 - */ - @Schema(description = "纬度") - private double latitude; - - /** - * 经度 - */ - @Schema(description = "自定义经度") - private double customLongitude; - - /** - * 纬度 - */ - @Schema(description = "自定义纬度") - private double customLatitude; - - /** - * 经度 GCJ02 - */ - @Schema(description = "GCJ02坐标系经度") - private double longitudeGcj02; - - /** - * 纬度 GCJ02 - */ - @Schema(description = "GCJ02坐标系纬度") - private double latitudeGcj02; - - /** - * 经度 WGS84 - */ - @Schema(description = "WGS84坐标系经度") - private double longitudeWgs84; - - /** - * 纬度 WGS84 - */ - @Schema(description = "WGS84坐标系纬度") - private double latitudeWgs84; - - /** - * 子设备数 - */ @Schema(description = "子设备数") private int subCount; - /** - * 流唯一编号,存在表示正在直播 - */ @Schema(description = "流唯一编号,存在表示正在直播") private String streamId; - /** - * 是否含有音频 - */ @Schema(description = "是否含有音频") - private Boolean hasAudio; + private boolean hasAudio; - /** - * 标记通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划 - */ - @Schema(description = "标记通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划") - private int channelType; - - /** - * 业务分组 - */ - @Schema(description = "业务分组") - private String businessGroupId; - - /** - * GPS的更新时间 - */ @Schema(description = "GPS的更新时间") private String gpsTime; @@ -262,21 +188,8 @@ public class DeviceChannel { "用于选择码流时组成码流标识。默认为null,不设置。可选值: stream/streamnumber/streamprofile/streamMode") private String streamIdentification; - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } + @Schema(description = "通道类型, 默认0, 0: 普通通道,1 行政区划 2 业务分组/虚拟组织") + private int channelType; public void setPtzType(int ptzType) { this.ptzType = ptzType; @@ -296,322 +209,40 @@ public class DeviceChannel { case 4: this.ptzTypeText = "遥控枪机"; break; + case 5: + this.ptzTypeText = "遥控半球"; + break; + case 6: + this.ptzTypeText = "多目设备的全景/拼接通道"; + break; + case 7: + this.ptzTypeText = "多目设备的分割通道"; + break; } } - public String getChannelId() { - return channelId; + public static DeviceChannel decode(Element element) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { + DeviceChannel deviceChannel = XmlUtil.elementDecode(element, DeviceChannel.class); + if(deviceChannel.getCivilCode() != null ) { + if (ObjectUtils.isEmpty(deviceChannel.getCivilCode()) + || deviceChannel.getCivilCode().length() > 8 ){ + deviceChannel.setCivilCode(null); + } + // 此处对于不在wvp缓存中的行政区划,默认直接存储.保证即使出现wvp的行政区划缓存过老,也可以通过用户自主创建的方式正常使用系统 + } + GbCode gbCode = GbCode.decode(deviceChannel.getDeviceId()); + if (gbCode != null && "138".equals(gbCode.getTypeCode())) { + deviceChannel.setHasAudio(true); + } + return deviceChannel; + } + + public static DeviceChannel decodeWithOnlyDeviceId(Element element) { + Element deviceElement = element.element("DeviceID"); + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setDeviceId(deviceElement.getText()); + return deviceChannel; } - public void setChannelId(String channelId) { - this.channelId = channelId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getManufacture() { - return manufacture; - } - - public void setManufacture(String manufacture) { - this.manufacture = manufacture; - } - - public String getModel() { - return model; - } - - public void setModel(String model) { - this.model = model; - } - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.owner = owner; - } - - public String getCivilCode() { - return civilCode; - } - - public void setCivilCode(String civilCode) { - this.civilCode = civilCode; - } - - public String getBlock() { - return block; - } - - public void setBlock(String block) { - this.block = block; - } - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - public int getParental() { - return parental; - } - - public void setParental(int parental) { - this.parental = parental; - } - - public String getParentId() { - return parentId; - } - - public void setParentId(String parentId) { - this.parentId = parentId; - } - - public int getSafetyWay() { - return safetyWay; - } - - public void setSafetyWay(int safetyWay) { - this.safetyWay = safetyWay; - } - - public int getRegisterWay() { - return registerWay; - } - - public void setRegisterWay(int registerWay) { - this.registerWay = registerWay; - } - - public String getCertNum() { - return certNum; - } - - public void setCertNum(String certNum) { - this.certNum = certNum; - } - - public int getCertifiable() { - return certifiable; - } - - public void setCertifiable(int certifiable) { - this.certifiable = certifiable; - } - - public int getErrCode() { - return errCode; - } - - public void setErrCode(int errCode) { - this.errCode = errCode; - } - - public String getEndTime() { - return endTime; - } - - public void setEndTime(String endTime) { - this.endTime = endTime; - } - - public String getSecrecy() { - return secrecy; - } - - public void setSecrecy(String secrecy) { - this.secrecy = secrecy; - } - - public String getIpAddress() { - return ipAddress; - } - - public void setIpAddress(String ipAddress) { - this.ipAddress = ipAddress; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - public int getPtzType() { - return ptzType; - } - - public String getPtzTypeText() { - return ptzTypeText; - } - - public void setPtzTypeText(String ptzTypeText) { - this.ptzTypeText = ptzTypeText; - } - - public boolean isStatus() { - return status; - } - - public void setStatus(boolean status) { - this.status = status; - } - - public double getLongitude() { - return longitude; - } - - public void setLongitude(double longitude) { - this.longitude = longitude; - } - - public double getLatitude() { - return latitude; - } - - public void setLatitude(double latitude) { - this.latitude = latitude; - } - - public double getLongitudeGcj02() { - return longitudeGcj02; - } - - public void setLongitudeGcj02(double longitudeGcj02) { - this.longitudeGcj02 = longitudeGcj02; - } - - public double getLatitudeGcj02() { - return latitudeGcj02; - } - - public void setLatitudeGcj02(double latitudeGcj02) { - this.latitudeGcj02 = latitudeGcj02; - } - - public double getLongitudeWgs84() { - return longitudeWgs84; - } - - public void setLongitudeWgs84(double longitudeWgs84) { - this.longitudeWgs84 = longitudeWgs84; - } - - public double getLatitudeWgs84() { - return latitudeWgs84; - } - - public void setLatitudeWgs84(double latitudeWgs84) { - this.latitudeWgs84 = latitudeWgs84; - } - - public int getSubCount() { - return subCount; - } - - public void setSubCount(int subCount) { - this.subCount = subCount; - } - - public Boolean getHasAudio() { - return hasAudio; - } - - public void setHasAudio(Boolean hasAudio) { - this.hasAudio = hasAudio; - } - - public String getStreamId() { - return streamId; - } - - public void setStreamId(String streamId) { - this.streamId = streamId; - } - - public String getCreateTime() { - return createTime; - } - - public void setCreateTime(String createTime) { - this.createTime = createTime; - } - - public String getUpdateTime() { - return updateTime; - } - - public void setUpdateTime(String updateTime) { - this.updateTime = updateTime; - } - - public int getChannelType() { - return channelType; - } - - public void setChannelType(int channelType) { - this.channelType = channelType; - } - - public String getBusinessGroupId() { - return businessGroupId; - } - - public void setBusinessGroupId(String businessGroupId) { - this.businessGroupId = businessGroupId; - } - - public String getGpsTime() { - return gpsTime; - } - - public void setGpsTime(String gpsTime) { - this.gpsTime = gpsTime; - } - - public String getStreamIdentification() { - return streamIdentification; - } - - public void setStreamIdentification(String streamIdentification) { - this.streamIdentification = streamIdentification; - } - - public double getCustomLongitude() { - return customLongitude; - } - - public void setCustomLongitude(double customLongitude) { - this.customLongitude = customLongitude; - } - - public double getCustomLatitude() { - return customLatitude; - } - - public void setCustomLatitude(double customLatitude) { - this.customLatitude = customLatitude; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceType.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceType.java new file mode 100644 index 000000000..5a82526da --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceType.java @@ -0,0 +1,58 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import org.jetbrains.annotations.NotNull; + +public class DeviceType implements Comparable{ + + /** + * 编号 + */ + private String name; + + /** + * 名称 + */ + private String code; + + /** + * 归属名称 + */ + private String ownerName; + public static DeviceType getInstance(DeviceTypeEnum typeEnum) { + DeviceType deviceType = new DeviceType(); + deviceType.setName(typeEnum.getName()); + deviceType.setCode(typeEnum.getCode()); + deviceType.setOwnerName(typeEnum.getOwnerName()); + return deviceType; + } + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getOwnerName() { + return ownerName; + } + + public void setOwnerName(String ownerName) { + this.ownerName = ownerName; + } + + @Override + public int compareTo(@NotNull DeviceType deviceType) { + return Integer.compare(Integer.parseInt(this.code), Integer.parseInt(deviceType.getCode())); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceTypeEnum.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceTypeEnum.java new file mode 100644 index 000000000..8a7c07a5b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceTypeEnum.java @@ -0,0 +1,98 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +/** + * 收录行业编码 + */ +public enum DeviceTypeEnum { + DVR("111", "DVR编码", "前端主设备"), + VIDEO_SERVER("112", "视频服务器编码", "前端主设备"), + ENCODER("113", "编码器编码", "前端主设备"), + DECODER("114", "解码器编码", "前端主设备"), + VIDEO_SWITCHING_MATRIX("115", "视频切换矩阵编码", "前端主设备"), + AUDIO_SWITCHING_MATRIX("116", "音频切换矩阵编码", "前端主设备"), + ALARM_CONTROLLER("117", "报警控制器编码", "前端主设备"), + NVR("118", "网络视频录像机(NVR)编码", "前端主设备"), + RESERVE("119", "预留", "前端主设备"), + ONLINE_VIDEO_IMAGE_INFORMATION_ACQUISITION_SYSTEM("120", "在线视频图像信息采集系统编码", "前端主设备"), + VIDEO_CHECKPOINT("121", "视频卡口编码", "前端主设备"), + MULTI_CAMERA_DEVICE("122", "多目设备编码", "前端主设备"), + PARKING_LOT_ENTRANCE_AND_EXIT_CONTROL_EQUIPMENT("123", "停车场出入口控制设备编码", "前端主设备"), + PERSONNEL_ACCESS_CONTROL_EQUIPMENT("124", "人员出入口控制设备编码", "前端主设备"), + SECURITY_INSPECTION_EQUIPMENT("125", "安检设备编码", "前端主设备"), + HVR("130", "混合硬盘录像机(HVR)编码", "前端主设备"), + CAMERA("131", "摄像机编码", "前端外围设备"), + IPC("132", "网络摄像机(IPC)/在线视频图像信息采集设备编码", "前端外围设备"), + MONITOR("133", "显示器编码", "前端外围设备"), + ALARM_INPUT_DEVICE("134", "报警输入设备编码(如红外、烟感、门禁等报警设备)", "前端外围设备"), + ALARM_OUTPUT_DEVICE("135", "报警输出设备编码(如警灯、警铃等设备)", "前端外围设备"), + VOICE_INPUT_DEVICE("136", "语音输入设备编码", "前端外围设备"), + VOICE_OUTPUT_DEVICE("137", "语音输出设备", "前端外围设备"), + MOBILE_TRANSMISSION_EQUIPMENT("138", "移动传输设备编码", "前端外围设备"), + OTHER_PERIPHERAL_DEVICES("139", "其他外围设备编码", "前端外围设备"), + ALARM_OUTPUT_DEVICE2("140", "报警输出设备编码(如继电器或触发器控制的设备)", "前端外围设备"), + BARRIER_GATE("141", "道闸(控制车辆通行)", "前端外围设备"), + SMART_DOOR("142", "智能门(控制人员通行)", "前端外围设备"), + VOUCHER_RECOGNITION_UNIT("143", "凭证识别单元", "前端外围设备"), + CENTRAL_SIGNALING_CONTROL_SERVER("200", "中心信令控制服务器编码", "平台设备"), + WEB_APPLICATION_SERVER("201", "Web应用服务器编码", "平台设备"), + PROXY_SERVER("203", "代理服务器编码", "平台设备"), + SECURITY_SERVER("204", "安全服务器编码", "平台设备"), + ALARM_SERVER("205", "报警服务器编码", "平台设备"), + DATABASE_SERVER("206", "数据库服务器编码", "平台设备"), + GIS_SERVER("207", "GIS服务器编码", "平台设备"), + MANAGER_SERVER("208", "管理服务器编码", "平台设备"), + ACCESS_GATEWAY("209", "接入网关编码", "平台设备"), + MEDIA_STORAGE_SERVER("210", "媒体存储服务器编码", "平台设备"), + SIGNALING_SECURITY_ROUTING_GATEWAY("211", "信令安全路由网关编码", "平台设备"), + BUSINESS_GROUP("215", "业务分组编码", "平台设备"), + VIRTUAL_ORGANIZATION("216", "虚拟组织编码", "平台设备"), + CENTRAL_USER("300", "中心用户", "中心用户"), + END_USER("400", "终端用户", "终端用户"), + VIDEO_IMAGE_INFORMATION_SYNTHESIS("500", "视频图像信息综合应用平台", "平台外接服务器"), + VIDEO_IMAGE_INFORMATION_OPERATION_AND_MAINTENANCE_MANAGEMENT("501", "视频图像信息运维管理平台", "平台外接服务器"), + VIDEO_IMAGE_ANALYSIS("502", "视频图像分析系统", "平台外接服务器"), + VIDEO_IMAGE_INFORMATION_DATABASE("503", "视频图像信息数据库", "平台外接服务器"), + VIDEO_IMAGE_ANALYSIS_EQUIPMENT("505", "视频图像分析设备", "平台外接服务器"), + ; + + /** + * 编号 + */ + private final String name; + + /** + * 名称 + */ + private String code; + + /** + * 归属名称 + */ + private String ownerName; + + DeviceTypeEnum(String code, String name, String ownerName) { + this.name = name; + this.code = code; + this.ownerName = ownerName; + } + + public String getName() { + return name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getOwnerName() { + return ownerName; + } + + public void setOwnerName(String ownerName) { + this.ownerName = ownerName; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GBStringMsgParser.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GBStringMsgParser.java index 2abc3569a..d4dab0786 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GBStringMsgParser.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GBStringMsgParser.java @@ -1,9 +1,7 @@ package com.genersoft.iot.vmp.gb28181.bean; -import gov.nist.core.CommonLogger; import gov.nist.core.Host; import gov.nist.core.HostNameParser; -import gov.nist.core.StackLogger; import gov.nist.javax.sip.SIPConstants; import gov.nist.javax.sip.address.AddressImpl; import gov.nist.javax.sip.address.GenericURI; @@ -14,16 +12,16 @@ import gov.nist.javax.sip.message.SIPMessage; import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPResponse; import gov.nist.javax.sip.parser.*; +import lombok.extern.slf4j.Slf4j; import java.io.UnsupportedEncodingException; import java.text.ParseException; +@Slf4j public class GBStringMsgParser implements MessageParser { protected static boolean computeContentLengthFromMessage = false; - private static StackLogger logger = CommonLogger.getLogger(StringMsgParser.class); - /** * @since v0.9 */ @@ -60,8 +58,8 @@ public class GBStringMsgParser implements MessageParser { } catch (ArrayIndexOutOfBoundsException e) { // Array contains only control char, return null. - if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) { - logger.logDebug("handled only control char so returning null"); + if (log.isDebugEnabled()) { + log.debug("handled only control char so returning null"); } return null; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbCode.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbCode.java new file mode 100644 index 000000000..bc5e508ef --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbCode.java @@ -0,0 +1,48 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 国标编码对象 + */ +@Data +@Schema(description = "国标编码对象") +public class GbCode { + + @Schema(description = "中心编码,由监控中心所在地的行政区划代码确定,符合GB/T2260—2007的要求") + private String centerCode; + + @Schema(description = "行业编码") + private String industryCode; + + @Schema(description = "类型编码") + private String typeCode; + + @Schema(description = "网络标识") + private String netCode; + + @Schema(description = "序号") + private String sn; + + /** + * 解析国标编号 + */ + public static GbCode decode(String code){ + if (code == null || code.trim().length() != 20) { + return null; + } + code = code.trim(); + GbCode gbCode = new GbCode(); + gbCode.setCenterCode(code.substring(0, 8)); + gbCode.setIndustryCode(code.substring(8, 10)); + gbCode.setTypeCode(code.substring(10, 13)); + gbCode.setNetCode(code.substring(13, 14)); + gbCode.setSn(code.substring(14)); + return gbCode; + } + + public String ecode(){ + return centerCode + industryCode + typeCode + netCode + sn; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Group.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Group.java new file mode 100644 index 000000000..e50c1ad50 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Group.java @@ -0,0 +1,93 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import com.genersoft.iot.vmp.utils.DateUtil; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.jetbrains.annotations.NotNull; + +/** + * 业务分组 + */ +@Data +@Schema(description = "业务分组") +public class Group implements Comparable{ + /** + * 数据库自增ID + */ + @Schema(description = "数据库自增ID") + private int id; + + /** + * 区域国标编号 + */ + @Schema(description = "区域国标编号") + private String deviceId; + + /** + * 区域名称 + */ + @Schema(description = "区域名称") + private String name; + + /** + * 父分组ID + */ + @Schema(description = "父分组ID") + private Integer parentId; + + /** + * 父区域国标ID + */ + @Schema(description = "父区域国标ID") + private String parentDeviceId; + + /** + * 所属的业务分组国标编号 + */ + @Schema(description = "所属的业务分组国标编号") + private String businessGroup; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + private String createTime; + + /** + * 更新时间 + */ + @Schema(description = "更新时间") + private String updateTime; + /** + * 行政区划 + */ + @Schema(description = "行政区划") + private String civilCode; + + public static Group getInstance(DeviceChannel channel) { + GbCode gbCode = GbCode.decode(channel.getDeviceId()); + if (gbCode == null || (!gbCode.getTypeCode().equals("215") && !gbCode.getTypeCode().equals("216"))) { + return null; + } + Group group = new Group(); + group.setName(channel.getName()); + group.setDeviceId(channel.getDeviceId()); + group.setCreateTime(DateUtil.getNow()); + group.setUpdateTime(DateUtil.getNow()); + if (gbCode.getTypeCode().equals("215")) { + group.setBusinessGroup(channel.getDeviceId()); + }else if (gbCode.getTypeCode().equals("216")) { + group.setBusinessGroup(channel.getBusinessGroupId()); + group.setParentDeviceId(channel.getParentId()); + } + if (group.getBusinessGroup() == null) { + return null; + } + return group; + } + + @Override + public int compareTo(@NotNull Group region) { + return Integer.compare(Integer.parseInt(this.deviceId), Integer.parseInt(region.getDeviceId())); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GroupTree.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GroupTree.java new file mode 100644 index 000000000..8ac1278e1 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GroupTree.java @@ -0,0 +1,22 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 业务分组 + */ +@Data +@Schema(description = "业务分组树") +public class GroupTree extends Group{ + + @Schema(description = "树节点ID") + private String treeId; + + @Schema(description = "是否有子节点") + private boolean isLeaf; + + @Schema(description = "类型, 行政区划:0 摄像头: 1") + private int type; + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/IndustryCodeType.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/IndustryCodeType.java new file mode 100644 index 000000000..d3414a220 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/IndustryCodeType.java @@ -0,0 +1,59 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import org.jetbrains.annotations.NotNull; + +public class IndustryCodeType implements Comparable{ + + /** + * 接入类型码 + */ + private String name; + + /** + * 名称 + */ + private String code; + + /** + * 备注 + */ + private String notes; + + public static IndustryCodeType getInstance(IndustryCodeTypeEnum typeEnum) { + IndustryCodeType industryCodeType = new IndustryCodeType(); + industryCodeType.setName(typeEnum.getName()); + industryCodeType.setCode(typeEnum.getCode()); + industryCodeType.setNotes(typeEnum.getNotes()); + return industryCodeType; + } + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + @Override + public int compareTo(@NotNull IndustryCodeType industryCodeType) { + return Integer.compare(Integer.parseInt(this.code), Integer.parseInt(industryCodeType.getCode())); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/IndustryCodeTypeEnum.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/IndustryCodeTypeEnum.java new file mode 100644 index 000000000..8d25fe993 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/IndustryCodeTypeEnum.java @@ -0,0 +1,55 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import lombok.Getter; + +/** + * 收录行业编码 + */ +public enum IndustryCodeTypeEnum { + SOCIAL_SECURITY_ROAD("00", "社会治安路面接入", "包括城市路面、商业街、公共区域、重点区域"), + SOCIAL_SECURITY_COMMUNITY("01", "社会治安社区接入", "包括社区、楼宇、网吧等"), + SOCIAL_SECURITY__INTERNAL("02", "社会治安内部接入 ", "包括公安办公楼、留置室等"), + SOCIAL_SECURITY_OTHER("03", "社会治安其他接入", ""), + TRAFFIC_ROAD("04", "交通路面接入 ", "包括城市主要干道、国道、高速交通状况监视"), + TRAFFIC_BAYONET("05", "交通卡口接入", "包括交叉路口、“电子警察”、关口、收费站等"), + TRAFFIC_INTERNAL("06", "交通内部接入", "包括交管办公楼等"), + TRAFFIC_OTHER("07", "交通其他接入", ""), + CITY_MANAGEMENT("08", "城市管理接入", ""), + HEALTH_ENVIRONMENTAL_PROTECTION("09", "卫生环保接入", ""), + COMMODITY_INSPECTION_CUSTOMHOUSE("10", "商检海关接入", ""), + EDUCATION_SECTOR("11", "教育部门接入", ""), + CIVIL_AVIATION("12", "民航接入", ""), + RAILWAY("13", "铁路接入", ""), + SHIPPING("14", "航运接入", ""), + AGRICULTURE_FORESTRY_ANIMAL_HUSBANDRY_FISHING("40", "农、林、牧、渔业接入", ""), + MINING("41", "采矿业接入", ""), + MANUFACTURING_INDUSTRY("42", "制造业接入", ""), + ELECTRICITY_HEAT_GAS_AND_WATER_PRODUCTION_AND_SUPPLY("43", "电力、热力、燃气及水生产和供应业接入", ""), + CONSTRUCTION("44", "建筑业接入", ""), + WHOLESALE_AND_RETAIL("45", "批发和零售业接入", ""), + ; + + /** + * 接入类型码 + */ + @Getter + private String name; + + /** + * 名称 + */ + @Getter + private String code; + + /** + * 备注 + */ + @Getter + private String notes; + + IndustryCodeTypeEnum(String code, String name, String notes) { + this.name = name; + this.code = code; + this.notes = notes; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteDecodeException.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteDecodeException.java new file mode 100644 index 000000000..e9943f15f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteDecodeException.java @@ -0,0 +1,14 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import lombok.Data; + +@Data +public class InviteDecodeException extends RuntimeException{ + private int code; + private String msg; + + public InviteDecodeException(int code, String msg) { + this.code = code; + this.msg = msg; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteInfo.java new file mode 100644 index 000000000..57e83bd10 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteInfo.java @@ -0,0 +1,22 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import lombok.Data; + +// 从INVITE消息中解析需要的信息 +@Data +public class InviteInfo { + private String requesterId; + private String targetChannelId; + private String sourceChannelId; + private String sessionName; + private String ssrc; + private boolean tcp; + private boolean tcpActive; + private String callId; + private Long startTime; + private Long stopTime; + private String downloadSpeed; + private String ip; + private int port; + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java index 19000930c..52b9d798f 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java @@ -1,11 +1,14 @@ package com.genersoft.iot.vmp.gb28181.bean; +import lombok.Data; + /** * @description: 移动位置bean * @author: lawrencehj * @date: 2021年1月23日 */ +@Data public class MobilePosition { /** * 设备Id @@ -15,7 +18,7 @@ public class MobilePosition { /** * 通道Id */ - private String channelId; + private Integer channelId; /** * 设备名称 @@ -56,150 +59,8 @@ public class MobilePosition { * 位置信息上报来源(Mobile Position、GPS Alarm) */ private String reportSource; - - /** - * 国内坐标系:经度坐标 - */ - private double longitudeGcj02; - - /** - * 国内坐标系:纬度坐标 - */ - private double latitudeGcj02; - - /** - * 国内坐标系:经度坐标 - */ - private double longitudeWgs84; - - /** - * 国内坐标系:纬度坐标 - */ - private double latitudeWgs84; - /** * 创建时间 */ private String createTime; - - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getDeviceName() { - return deviceName; - } - - public void setDeviceName(String deviceName) { - this.deviceName = deviceName; - } - - public String getTime() { - return time; - } - - public void setTime(String time) { - this.time = time; - } - - public double getLongitude() { - return longitude; - } - - public void setLongitude(double longitude) { - this.longitude = longitude; - } - - public double getLatitude() { - return latitude; - } - - public void setLatitude(double latitude) { - this.latitude = latitude; - } - - public double getAltitude() { - return altitude; - } - - public void setAltitude(double altitude) { - this.altitude = altitude; - } - - public double getSpeed() { - return speed; - } - - public void setSpeed(double speed) { - this.speed = speed; - } - - public double getDirection() { - return direction; - } - - public void setDirection(double direction) { - this.direction = direction; - } - - public String getReportSource() { - return reportSource; - } - - public void setReportSource(String reportSource) { - this.reportSource = reportSource; - } - - public String getChannelId() { - return channelId; - } - - public void setChannelId(String channelId) { - this.channelId = channelId; - } - - public double getLongitudeGcj02() { - return longitudeGcj02; - } - - public void setLongitudeGcj02(double longitudeGcj02) { - this.longitudeGcj02 = longitudeGcj02; - } - - public double getLatitudeGcj02() { - return latitudeGcj02; - } - - public void setLatitudeGcj02(double latitudeGcj02) { - this.latitudeGcj02 = latitudeGcj02; - } - - public double getLongitudeWgs84() { - return longitudeWgs84; - } - - public void setLongitudeWgs84(double longitudeWgs84) { - this.longitudeWgs84 = longitudeWgs84; - } - - public double getLatitudeWgs84() { - return latitudeWgs84; - } - - public void setLatitudeWgs84(double latitudeWgs84) { - this.latitudeWgs84 = latitudeWgs84; - } - - public String getCreateTime() { - return createTime; - } - - public void setCreateTime(String createTime) { - this.createTime = createTime; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/NetworkIdentificationType.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/NetworkIdentificationType.java new file mode 100644 index 000000000..ed469e495 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/NetworkIdentificationType.java @@ -0,0 +1,45 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import org.jetbrains.annotations.NotNull; + +public class NetworkIdentificationType implements Comparable{ + + /** + * 接入类型码 + */ + private String name; + + /** + * 名称 + */ + private String code; + + public static NetworkIdentificationType getInstance(NetworkIdentificationTypeEnum typeEnum) { + NetworkIdentificationType industryCodeType = new NetworkIdentificationType(); + industryCodeType.setName(typeEnum.getName()); + industryCodeType.setCode(typeEnum.getCode()); + return industryCodeType; + } + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + @Override + public int compareTo(@NotNull NetworkIdentificationType networkIdentificationType) { + return Integer.compare(Integer.parseInt(this.code), Integer.parseInt(networkIdentificationType.getCode())); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/NetworkIdentificationTypeEnum.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/NetworkIdentificationTypeEnum.java new file mode 100644 index 000000000..1b0ed2a5c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/NetworkIdentificationTypeEnum.java @@ -0,0 +1,51 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +/** + * 收录行业编码 + */ +public enum NetworkIdentificationTypeEnum { + PUBLIC_SECURITY_VIDEO_TRANSMISSION_NETWORK("0", "公安视频传输网"), + PUBLIC_SECURITY_VIDEO_TRANSMISSION_NETWORK2("1", "公安视频传输网"), + INDUSTRY_SPECIFIC_NETWORK("2", "行业专网"), + POLITICAL_AND_LEGAL_INFORMATION_NETWORK("3", "政法信息网"), + PUBLIC_SECURITY_MOBILE_INFORMATION_NETWORK("4", "公安移动信息网"), + PUBLIC_SECURITY_INFORMATION_NETWORK("5", "公安信息网"), + ELECTRONIC_GOVERNMENT_EXTRANET("6", "电子政务外网"), + PUBLIC_NETWORKS_SUCH_AS_THE_INTERNET("7", "互联网等公共网络"), + Dedicated_Line("8", "专线"), + RESERVE("9", "预留"), + ; + + /** + * 接入类型码 + */ + private String name; + + /** + * 名称 + */ + private String code; + + + NetworkIdentificationTypeEnum(String code, String name) { + this.name = name; + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/NotifyCatalogChannel.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/NotifyCatalogChannel.java new file mode 100644 index 000000000..8961677d9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/NotifyCatalogChannel.java @@ -0,0 +1,38 @@ +package com.genersoft.iot.vmp.gb28181.bean; + + +public class NotifyCatalogChannel { + + private Type type; + + private DeviceChannel channel; + + + public enum Type { + ADD, DELETE, UPDATE, STATUS_CHANGED + } + + + public static NotifyCatalogChannel getInstance(Type type, DeviceChannel channel) { + NotifyCatalogChannel notifyCatalogChannel = new NotifyCatalogChannel(); + notifyCatalogChannel.setType(type); + notifyCatalogChannel.setChannel(channel); + return notifyCatalogChannel; + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public DeviceChannel getChannel() { + return channel; + } + + public void setChannel(DeviceChannel channel) { + this.channel = channel; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/OpenRTPServerResult.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/OpenRTPServerResult.java new file mode 100644 index 000000000..aa6044443 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/OpenRTPServerResult.java @@ -0,0 +1,12 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import com.genersoft.iot.vmp.media.event.hook.HookData; +import com.genersoft.iot.vmp.service.bean.SSRCInfo; +import lombok.Data; + +@Data +public class OpenRTPServerResult { + + private SSRCInfo ssrcInfo; + private HookData hookData; +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java deleted file mode 100755 index 5de9761e8..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java +++ /dev/null @@ -1,450 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.bean; - -import io.swagger.v3.oas.annotations.media.Schema; - -/** - * @author lin - */ -@Schema(description = "平台信息") -public class ParentPlatform { - - /** - * id - */ - @Schema(description = "ID(数据库中)") - private Integer id; - - /** - * 是否启用 - */ - @Schema(description = "是否启用") - private boolean enable; - - /** - * 名称 - */ - @Schema(description = "名称") - private String name; - - /** - * SIP服务国标编码 - */ - @Schema(description = "SIP服务国标编码") - private String serverGBId; - - /** - * SIP服务国标域 - */ - @Schema(description = "SIP服务国标域") - private String serverGBDomain; - - /** - * SIP服务IP - */ - @Schema(description = "SIP服务IP") - private String serverIP; - - /** - * SIP服务端口 - */ - @Schema(description = "SIP服务端口") - private int serverPort; - - /** - * 设备国标编号 - */ - @Schema(description = "设备国标编号") - private String deviceGBId; - - /** - * 设备ip - */ - @Schema(description = "设备ip") - private String deviceIp; - - /** - * 设备端口 - */ - @Schema(description = "设备端口") - private int devicePort; - - /** - * SIP认证用户名(默认使用设备国标编号) - */ - @Schema(description = "SIP认证用户名(默认使用设备国标编号)") - private String username; - - /** - * SIP认证密码 - */ - @Schema(description = "SIP认证密码") - private String password; - - /** - * 注册周期 (秒) - */ - @Schema(description = "注册周期 (秒)") - private int expires; - - /** - * 心跳周期(秒) - */ - @Schema(description = "心跳周期(秒)") - private int keepTimeout; - - /** - * 传输协议 - * UDP/TCP - */ - @Schema(description = "传输协议") - private String transport; - - /** - * 字符集 - */ - @Schema(description = "字符集") - private String characterSet; - - /** - * 允许云台控制 - */ - @Schema(description = "允许云台控制") - private boolean ptz; - - /** - * RTCP流保活 - */ - @Schema(description = "RTCP流保活") - private boolean rtcp; - - /** - * 在线状态 - */ - @Schema(description = "在线状态") - private boolean status; - - /** - * 在线状态 - */ - @Schema(description = "在线状态") - private int channelCount; - - /** - * 默认目录Id,自动添加的通道多放在这个目录下 - */ - @Schema(description = "默认目录Id,自动添加的通道多放在这个目录下") - private String catalogId; - - /** - * 已被订阅目录信息 - */ - @Schema(description = "已被订阅目录信息") - private boolean catalogSubscribe; - - /** - * 已被订阅报警信息 - */ - @Schema(description = "已被订阅报警信息") - private boolean alarmSubscribe; - - /** - * 已被订阅移动位置信息 - */ - @Schema(description = "已被订阅移动位置信息") - private boolean mobilePositionSubscribe; - - /** - * 点播未推流的设备时是否使用redis通知拉起 - */ - @Schema(description = "点播未推流的设备时是否使用redis通知拉起") - private boolean startOfflinePush; - - /** - * 目录分组-每次向上级发送通道信息时单个包携带的通道数量,取值1,2,4,8 - */ - @Schema(description = "目录分组-每次向上级发送通道信息时单个包携带的通道数量,取值1,2,4,8") - private int catalogGroup; - - /** - * 行政区划 - */ - @Schema(description = "行政区划") - private String administrativeDivision; - - /** - * 更新时间 - */ - @Schema(description = "更新时间") - private String updateTime; - - /** - * 创建时间 - */ - @Schema(description = "创建时间") - private String createTime; - - @Schema(description = "是否作为消息通道") - private boolean asMessageChannel; - - @Schema(description = "是否作为消息通道") - private boolean autoPushChannel; - - @Schema(description = "点播回复200OK使用次IP") - private String sendStreamIp; - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public boolean isEnable() { - return enable; - } - - public void setEnable(boolean enable) { - this.enable = enable; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getServerGBId() { - return serverGBId; - } - - public void setServerGBId(String serverGBId) { - this.serverGBId = serverGBId; - } - - public String getServerGBDomain() { - return serverGBDomain; - } - - public void setServerGBDomain(String serverGBDomain) { - this.serverGBDomain = serverGBDomain; - } - - public String getServerIP() { - return serverIP; - } - - public void setServerIP(String serverIP) { - this.serverIP = serverIP; - } - - public int getServerPort() { - return serverPort; - } - - public void setServerPort(int serverPort) { - this.serverPort = serverPort; - } - - public String getDeviceGBId() { - return deviceGBId; - } - - public void setDeviceGBId(String deviceGBId) { - this.deviceGBId = deviceGBId; - } - - public String getDeviceIp() { - return deviceIp; - } - - public void setDeviceIp(String deviceIp) { - this.deviceIp = deviceIp; - } - - public int getDevicePort() { - return devicePort; - } - - public void setDevicePort(int devicePort) { - this.devicePort = devicePort; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public int getExpires() { - return expires; - } - - public void setExpires(int expires) { - this.expires = expires; - } - - public int getKeepTimeout() { - return keepTimeout; - } - - public void setKeepTimeout(int keepTimeout) { - this.keepTimeout = keepTimeout; - } - - public String getTransport() { - return transport; - } - - public void setTransport(String transport) { - this.transport = transport; - } - - public String getCharacterSet() { - return characterSet; - } - - public void setCharacterSet(String characterSet) { - this.characterSet = characterSet; - } - - public boolean isPtz() { - return ptz; - } - - public void setPtz(boolean ptz) { - this.ptz = ptz; - } - - public boolean isRtcp() { - return rtcp; - } - - public void setRtcp(boolean rtcp) { - this.rtcp = rtcp; - } - - public boolean isStatus() { - return status; - } - - public void setStatus(boolean status) { - this.status = status; - } - - public int getChannelCount() { - return channelCount; - } - - public void setChannelCount(int channelCount) { - this.channelCount = channelCount; - } - - public String getCatalogId() { - return catalogId; - } - - public void setCatalogId(String catalogId) { - this.catalogId = catalogId; - } - - public boolean isCatalogSubscribe() { - return catalogSubscribe; - } - - public void setCatalogSubscribe(boolean catalogSubscribe) { - this.catalogSubscribe = catalogSubscribe; - } - - public boolean isAlarmSubscribe() { - return alarmSubscribe; - } - - public void setAlarmSubscribe(boolean alarmSubscribe) { - this.alarmSubscribe = alarmSubscribe; - } - - public boolean isMobilePositionSubscribe() { - return mobilePositionSubscribe; - } - - public void setMobilePositionSubscribe(boolean mobilePositionSubscribe) { - this.mobilePositionSubscribe = mobilePositionSubscribe; - } - - public boolean isStartOfflinePush() { - return startOfflinePush; - } - - public void setStartOfflinePush(boolean startOfflinePush) { - this.startOfflinePush = startOfflinePush; - } - - public int getCatalogGroup() { - return catalogGroup; - } - - public void setCatalogGroup(int catalogGroup) { - this.catalogGroup = catalogGroup; - } - - public String getAdministrativeDivision() { - return administrativeDivision; - } - - public void setAdministrativeDivision(String administrativeDivision) { - this.administrativeDivision = administrativeDivision; - } - - public String getUpdateTime() { - return updateTime; - } - - public void setUpdateTime(String updateTime) { - this.updateTime = updateTime; - } - - public String getCreateTime() { - return createTime; - } - - public void setCreateTime(String createTime) { - this.createTime = createTime; - } - - public boolean isAsMessageChannel() { - return asMessageChannel; - } - - public void setAsMessageChannel(boolean asMessageChannel) { - this.asMessageChannel = asMessageChannel; - } - - public boolean isAutoPushChannel() { - return autoPushChannel; - } - - public void setAutoPushChannel(boolean autoPushChannel) { - this.autoPushChannel = autoPushChannel; - } - - public String getSendStreamIp() { - return sendStreamIp; - } - - public void setSendStreamIp(String sendStreamIp) { - this.sendStreamIp = sendStreamIp; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java deleted file mode 100755 index 6ff2fe32e..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.bean; - -public class ParentPlatformCatch { - - private String id; - - /** - * 心跳未回复次数 - */ - private int keepAliveReply; - - // 注册未回复次数 - private int registerAliveReply; - - private String callId; - - private ParentPlatform parentPlatform; - - private SipTransactionInfo sipTransactionInfo; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public int getKeepAliveReply() { - return keepAliveReply; - } - - public void setKeepAliveReply(int keepAliveReply) { - this.keepAliveReply = keepAliveReply; - } - - public int getRegisterAliveReply() { - return registerAliveReply; - } - - public void setRegisterAliveReply(int registerAliveReply) { - this.registerAliveReply = registerAliveReply; - } - - public ParentPlatform getParentPlatform() { - return parentPlatform; - } - - public void setParentPlatform(ParentPlatform parentPlatform) { - this.parentPlatform = parentPlatform; - } - - public String getCallId() { - return callId; - } - - public void setCallId(String callId) { - this.callId = callId; - } - - public SipTransactionInfo getSipTransactionInfo() { - return sipTransactionInfo; - } - - public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { - this.sipTransactionInfo = sipTransactionInfo; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Platform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Platform.java new file mode 100755 index 000000000..74afd9ff7 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Platform.java @@ -0,0 +1,130 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * @author lin + */ +@Data +@Schema(description = "平台信息") +public class Platform { + + @Schema(description = "ID(数据库中)") + private Integer id; + + @Schema(description = "是否启用") + private boolean enable; + + @Schema(description = "名称") + private String name; + + @Schema(description = "SIP服务国标编码") + private String serverGBId; + + @Schema(description = "SIP服务国标域") + private String serverGBDomain; + + @Schema(description = "SIP服务IP") + private String serverIp; + + @Schema(description = "SIP服务端口") + private int serverPort; + + @Schema(description = "设备国标编号") + private String deviceGBId; + + @Schema(description = "设备ip") + private String deviceIp; + + @Schema(description = "设备端口") + private int devicePort; + + @Schema(description = "SIP认证用户名(默认使用设备国标编号)") + private String username; + + @Schema(description = "SIP认证密码") + private String password; + + @Schema(description = "注册周期 (秒)") + private int expires; + + @Schema(description = "心跳周期(秒)") + private int keepTimeout; + + @Schema(description = "传输协议") + private String transport; + + @Schema(description = "字符集") + private String characterSet; + + @Schema(description = "允许云台控制") + private boolean ptz; + + @Schema(description = "RTCP流保活") + private boolean rtcp; + + @Schema(description = "在线状态") + private boolean status; + + @Schema(description = "通道数量") + private int channelCount; + + @Schema(description = "已被订阅目录信息") + private boolean catalogSubscribe; + + @Schema(description = "已被订阅报警信息") + private boolean alarmSubscribe; + + @Schema(description = "已被订阅移动位置信息") + private boolean mobilePositionSubscribe; + + @Schema(description = "目录分组-每次向上级发送通道信息时单个包携带的通道数量,取值1,2,4,8") + private int catalogGroup; + + @Schema(description = "更新时间") + private String updateTime; + + @Schema(description = "创建时间") + private String createTime; + + @Schema(description = "是否作为消息通道") + private boolean asMessageChannel; + + @Schema(description = "点播回复200OK使用的IP") + private String sendStreamIp; + + @Schema(description = "是否自动推送通道变化") + private Boolean autoPushChannel; + + @Schema(description = "目录信息包含平台信息, 0:关闭,1:打开") + private int catalogWithPlatform; + + @Schema(description = "目录信息包含分组信息, 0:关闭,1:打开") + private int catalogWithGroup; + + @Schema(description = "目录信息包含行政区划, 0:关闭,1:打开") + private int catalogWithRegion; + + @Schema(description = "行政区划") + private String civilCode; + + @Schema(description = "平台厂商") + private String manufacturer; + + @Schema(description = "平台型号") + private String model; + + @Schema(description = "平台安装地址") + private String address; + + @Schema(description = "注册方式(必选)缺省为1; " + + "1-符合IETF RFC 3261标准的认证注册模式;" + + "2-基于口令的双向认证注册模式;" + + "3-基于数字证书的双向认证注册模式(高安全级别要求);" + + "4-基于数字证书的单向认证注册模式(高安全级别要求)") + private int registerWay = 1; + + @Schema(description = "保密属性(必选)缺省为0;0-不涉密,1-涉密") + private int secrecy = 0; +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatch.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatch.java new file mode 100755 index 000000000..db7ab9805 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatch.java @@ -0,0 +1,24 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import lombok.Data; + +@Data +public class PlatformCatch { + + private String id; + + /** + * 心跳未回复次数 + */ + private int keepAliveReply; + + // 注册未回复次数 + private int registerAliveReply; + + private String callId; + + private Platform platform; + + private SipTransactionInfo sipTransactionInfo; + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformChannel.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformChannel.java new file mode 100644 index 000000000..a9517b345 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformChannel.java @@ -0,0 +1,208 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class PlatformChannel extends CommonGBChannel { + + @Schema(description = "Id") + private int id; + + @Schema(description = "平台ID") + private int platformId; + + @Schema(description = "国标-编码") + private String customDeviceId; + + @Schema(description = "国标-名称") + private String customName; + + @Schema(description = "国标-设备厂商") + private String customManufacturer; + + @Schema(description = "国标-设备型号") + private String customModel; + + // 2016 + @Schema(description = "国标-设备归属") + private String customOwner; + + @Schema(description = "国标-行政区域") + private String customCivilCode; + + @Schema(description = "国标-警区") + private String customBlock; + + @Schema(description = "国标-安装地址") + private String customAddress; + + @Schema(description = "国标-是否有子设备") + private Integer customParental; + + @Schema(description = "国标-父节点ID") + private String customParentId; + + // 2016 + @Schema(description = "国标-信令安全模式") + private Integer customSafetyWay; + + @Schema(description = "国标-注册方式") + private Integer customRegisterWay; + + // 2016 + @Schema(description = "国标-证书序列号") + private Integer customCertNum; + + // 2016 + @Schema(description = "国标-证书有效标识") + private Integer customCertifiable; + + // 2016 + @Schema(description = "国标-无效原因码(有证书且证书无效的设备必选)") + private Integer customErrCode; + + // 2016 + @Schema(description = "国标-证书终止有效期(有证书且证书无效的设备必选)") + private Integer customEndTime; + + // 2022 + @Schema(description = "国标-摄像机安全能力等级代码") + private String customSecurityLevelCode; + + @Schema(description = "国标-保密属性(必选)缺省为0;0-不涉密,1-涉密") + private Integer customSecrecy; + + @Schema(description = "国标-设备/系统IPv4/IPv6地址") + private String customIpAddress; + + @Schema(description = "国标-设备/系统端口") + private Integer customPort; + + @Schema(description = "国标-设备口令") + private String customPassword; + + @Schema(description = "国标-设备状态") + private String customStatus; + + @Schema(description = "国标-经度 WGS-84坐标系") + private Double customLongitude; + + @Schema(description = "国标-纬度 WGS-84坐标系") + private Double customLatitude; + + @Schema(description = "国标-虚拟组织所属的业务分组ID") + private String customBusinessGroupId; + + @Schema(description = "国标-摄像机结构类型,标识摄像机类型: 1-球机; 2-半球; 3-固定枪机; 4-遥控枪机;5-遥控半球;6-多目设备的全景/拼接通道;7-多目设备的分割通道") + private Integer customPtzType; + + // 2016 + @Schema(description = "-摄像机位置类型扩展。1-省际检查站、2-党政机关、3-车站码头、4-中心广场、5-体育场馆、6-商业中心、7-宗教场所、" + + "8-校园周边、9-治安复杂区域、10-交通干线。当目录项为摄像机时可选。") + private Integer customPositionType; + + @Schema(description = "国标-摄像机光电成像类型。1-可见光成像;2-热成像;3-雷达成像;4-X光成像;5-深度光场成像;9-其他。可多值,") + private String customPhotoelectricImagingTyp; + + @Schema(description = "国标-摄像机采集部位类型") + private String customCapturePositionType; + + @Schema(description = "国标-摄像机安装位置室外、室内属性。1-室外、2-室内。") + private Integer customRoomType; + + // 2016 + @Schema(description = "国标-用途属性") + private Integer customUseType; + + @Schema(description = "国标-摄像机补光属性。1-无补光;2-红外补光;3-白光补光;4-激光补光;9-其他") + private Integer customSupplyLightType; + + @Schema(description = "国标-摄像机监视方位(光轴方向)属性。1-东(西向东)、2-西(东向西)、3-南(北向南)、4-北(南向北)、" + + "5-东南(西北到东南)、6-东北(西南到东北)、7-西南(东北到西南)、8-西北(东南到西北)") + private Integer customDirectionType; + + @Schema(description = "国标-摄像机支持的分辨率,可多值") + private String customResolution; + + // 2022 + @Schema(description = "国标-摄像机支持的码流编号列表,用于实时点播时指定码流编号(可选)") + private String customStreamNumberList; + + @Schema(description = "国标-下载倍速(可选),可多值") + private String customDownloadSpeed; + + @Schema(description = "国标-空域编码能力,取值0-不支持;1-1级增强(1个增强层);2-2级增强(2个增强层);3-3级增强(3个增强层)") + private Integer customSvcSpaceSupportMod; + + @Schema(description = "国标-时域编码能力,取值0-不支持;1-1级增强;2-2级增强;3-3级增强(可选)") + private Integer customSvcTimeSupportMode; + + // 2022 + @Schema(description = "国标- SSVC增强层与基本层比例能力 ") + private String customSsvcRatioSupportList; + + // 2022 + @Schema(description = "国标-移动采集设备类型(仅移动采集设备适用,必选);1-移动机器人载摄像机;2-执法记录仪;3-移动单兵设备;" + + "4-车载视频记录设备;5-无人机载摄像机;9-其他") + private Integer customMobileDeviceType; + + // 2022 + @Schema(description = "国标-摄像机水平视场角(可选),取值范围大于0度小于等于360度") + private Double customHorizontalFieldAngle; + + // 2022 + @Schema(description = "国标-摄像机竖直视场角(可选),取值范围大于0度小于等于360度 ") + private Double customVerticalFieldAngle; + + // 2022 + @Schema(description = "国标-摄像机可视距离(可选),单位:米") + private Double customMaxViewDistance; + + // 2022 + @Schema(description = "国标-基层组织编码(必选,非基层建设时为“000000”)") + private String customGrassrootsCode; + + // 2022 + @Schema(description = "国标-监控点位类型(当为摄像机时必选),1-一类视频监控点;2-二类视频监控点;3-三类视频监控点;9-其他点位。") + private Integer customPoType; + + // 2022 + @Schema(description = "国标-点位俗称") + private String customPoCommonName; + + // 2022 + @Schema(description = "国标-设备MAC地址(可选),用“XX-XX-XX-XX-XX-XX”格式表达") + private String customMac; + + // 2022 + @Schema(description = "国标-摄像机卡口功能类型,01-人脸卡口;02-人员卡口;03-机动车卡口;04-非机动车卡口;05-物品卡口;99-其他") + private String customFunctionType; + + // 2022 + @Schema(description = "国标-摄像机视频编码格式") + private String customEncodeType; + + // 2022 + @Schema(description = "国标-摄像机安装使用时间") + private String customInstallTime; + + // 2022 + @Schema(description = "国标-摄像机所属管理单位名称") + private String customManagementUnit; + + // 2022 + @Schema(description = "国标-摄像机所属管理单位联系人的联系方式(电话号码,可多值,用英文半角“/”分割)") + private String customContactInfo; + + // 2022 + @Schema(description = "国标-录像保存天数(可选)") + private Integer customRecordSaveDays; + + // 2022 + @Schema(description = "国标-国民经济行业分类代码(可选)") + private String customIndustrialClassification; + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlayException.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlayException.java new file mode 100644 index 000000000..89343940a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlayException.java @@ -0,0 +1,14 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import lombok.Data; + +@Data +public class PlayException extends RuntimeException{ + private int code; + private String msg; + + public PlayException(int code, String msg) { + this.code = code; + this.msg = msg; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Region.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Region.java new file mode 100644 index 000000000..bb4e1fdd3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Region.java @@ -0,0 +1,122 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import com.genersoft.iot.vmp.common.CivilCodePo; +import com.genersoft.iot.vmp.utils.CivilCodeUtil; +import com.genersoft.iot.vmp.utils.DateUtil; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.jetbrains.annotations.NotNull; + +/** + * 区域 + */ +@Data +@Schema(description = "区域") +public class Region implements Comparable{ + /** + * 数据库自增ID + */ + @Schema(description = "数据库自增ID") + private int id; + + /** + * 区域国标编号 + */ + @Schema(description = "区域国标编号") + private String deviceId; + + /** + * 区域名称 + */ + @Schema(description = "区域名称") + private String name; + + /** + * 父区域国标ID + */ + @Schema(description = "父区域ID") + private Integer parentId; + + /** + * 父区域国标ID + */ + @Schema(description = "父区域国标ID") + private String parentDeviceId; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + private String createTime; + + /** + * 更新时间 + */ + @Schema(description = "更新时间") + private String updateTime; + + public static Region getInstance(String commonRegionDeviceId, String commonRegionName, String commonRegionParentId) { + Region region = new Region(); + region.setDeviceId(commonRegionDeviceId); + region.setName(commonRegionName); + region.setParentDeviceId(commonRegionParentId); + region.setCreateTime(DateUtil.getNow()); + region.setUpdateTime(DateUtil.getNow()); + return region; + } + + public static Region getInstance(CivilCodePo civilCodePo) { + Region region = new Region(); + region.setName(civilCodePo.getName()); + region.setDeviceId(civilCodePo.getCode()); + if (civilCodePo.getCode().length() > 2) { + region.setParentDeviceId(civilCodePo.getParentCode()); + } + region.setCreateTime(DateUtil.getNow()); + region.setUpdateTime(DateUtil.getNow()); + return region; + } + + public static Region getInstance(DeviceChannel channel) { + Region region = new Region(); + region.setName(channel.getName()); + region.setDeviceId(channel.getDeviceId()); + CivilCodePo parentCode = CivilCodeUtil.INSTANCE.getParentCode(channel.getDeviceId()); + if (parentCode != null) { + region.setParentDeviceId(parentCode.getCode()); + } + region.setCreateTime(DateUtil.getNow()); + region.setUpdateTime(DateUtil.getNow()); + return region; + } + + @Override + public int compareTo(@NotNull Region region) { + return Integer.compare(Integer.parseInt(this.deviceId), Integer.parseInt(region.getDeviceId())); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) + return false; + if (this == obj) + return true; + if (obj instanceof Region) { + Region region = (Region) obj; + + // 比较每个属性的值一致时才返回true + if (region.getId() == this.id) { + return true; + } + } + return false; + } + + /** + * 重写hashcode方法,返回的hashCode一样才再去比较每个属性的值 + */ + @Override + public int hashCode() { + return id; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RegionTree.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RegionTree.java new file mode 100644 index 000000000..b771a603c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RegionTree.java @@ -0,0 +1,21 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 区域 + */ +@Data +@Schema(description = "区域树") +public class RegionTree extends Region { + + @Schema(description = "树节点ID") + private String treeId; + + @Schema(description = "是否有子节点") + private boolean isLeaf; + + @Schema(description = "类型, 行政区划:0 摄像头: 1") + private int type; +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpInfo.java new file mode 100755 index 000000000..988c2d441 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpInfo.java @@ -0,0 +1,236 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg; +import lombok.Data; + +@Data +public class SendRtpInfo { + + /** + * 推流ip + */ + private String ip; + + /** + * 推流端口 + */ + private int port; + + /** + * 推流标识 + */ + private String ssrc; + + /** + * 目标平台或设备的编号 + */ + private String targetId; + + /** + * 目标平台或设备的名称 + */ + private String targetName; + + /** + * 是否是发送给上级平台 + */ + private boolean sendToPlatform; + + /** + * 直播流的应用名 + */ + private String app; + + /** + * 通道id + */ + private Integer channelId; + + /** + * 推流状态 + * 0 等待设备推流上来 + * 1 等待上级平台回复ack + * 2 推流中 + */ + private int status = 0; + + + /** + * 设备推流的streamId + */ + private String stream; + + /** + * 是否为tcp + */ + private boolean tcp; + + /** + * 是否为tcp主动模式 + */ + private boolean tcpActive; + + /** + * 自己推流使用的IP + */ + private String localIp; + + /** + * 自己推流使用的端口 + */ + private int localPort; + + /** + * 使用的流媒体 + */ + private String mediaServerId; + + /** + * 使用的服务的ID + */ + private String serverId; + + /** + * invite 的 callId + */ + private String callId; + + /** + * invite 的 fromTag + */ + private String fromTag; + + /** + * invite 的 toTag + */ + private String toTag; + + /** + * 发送时,rtp的pt(uint8_t),不传时默认为96 + */ + private int pt = 96; + + /** + * 发送时,rtp的负载类型。为true时,负载为ps;为false时,为es; + */ + private boolean usePs = true; + + /** + * 当usePs 为false时,有效。为1时,发送音频;为0时,发送视频;不传时默认为0 + */ + private boolean onlyAudio = false; + + /** + * 是否开启rtcp保活 + */ + private boolean rtcp = false; + + + /** + * 播放类型 + */ + private InviteStreamType playType; + + /** + * 发流的同时收流 + */ + private String receiveStream; + + /** + * 上级的点播类型 + */ + private String sessionName; + + public static SendRtpInfo getInstance(RequestPushStreamMsg requestPushStreamMsg) { + SendRtpInfo sendRtpItem = new SendRtpInfo(); + sendRtpItem.setMediaServerId(requestPushStreamMsg.getMediaServerId()); + sendRtpItem.setApp(requestPushStreamMsg.getApp()); + sendRtpItem.setStream(requestPushStreamMsg.getStream()); + sendRtpItem.setIp(requestPushStreamMsg.getIp()); + sendRtpItem.setPort(requestPushStreamMsg.getPort()); + sendRtpItem.setSsrc(requestPushStreamMsg.getSsrc()); + sendRtpItem.setTcp(requestPushStreamMsg.isTcp()); + sendRtpItem.setLocalPort(requestPushStreamMsg.getSrcPort()); + sendRtpItem.setPt(requestPushStreamMsg.getPt()); + sendRtpItem.setUsePs(requestPushStreamMsg.isPs()); + sendRtpItem.setOnlyAudio(requestPushStreamMsg.isOnlyAudio()); + return sendRtpItem; + + } + + public static SendRtpInfo getInstance(String app, String stream, String ssrc, String dstIp, Integer dstPort, boolean tcp, int sendLocalPort, Integer pt) { + SendRtpInfo sendRtpItem = new SendRtpInfo(); + sendRtpItem.setApp(app); + sendRtpItem.setStream(stream); + sendRtpItem.setSsrc(ssrc); + sendRtpItem.setTcp(tcp); + sendRtpItem.setLocalPort(sendLocalPort); + sendRtpItem.setIp(dstIp); + sendRtpItem.setPort(dstPort); + if (pt != null) { + sendRtpItem.setPt(pt); + } + + return sendRtpItem; + } + + public static SendRtpInfo getInstance(Integer localPort, MediaServer mediaServer, String ip, int port, String ssrc, + String deviceId, String platformId, Integer channelId, boolean isTcp, boolean rtcp, + String serverId) { + if (localPort == 0) { + return null; + } + SendRtpInfo sendRtpItem = new SendRtpInfo(); + sendRtpItem.setIp(ip); + sendRtpItem.setPort(port); + sendRtpItem.setSsrc(ssrc); + if (deviceId != null) { + sendRtpItem.setTargetId(deviceId); + sendRtpItem.setSendToPlatform(false); + }else { + sendRtpItem.setTargetId(platformId); + sendRtpItem.setSendToPlatform(true); + } + sendRtpItem.setChannelId(channelId); + sendRtpItem.setTcp(isTcp); + sendRtpItem.setRtcp(rtcp); + sendRtpItem.setApp("rtp"); + sendRtpItem.setLocalPort(localPort); + sendRtpItem.setServerId(serverId); + sendRtpItem.setMediaServerId(mediaServer.getId()); + return sendRtpItem; + } + + @Override + public String toString() { + return "SendRtpItem{" + + "ip='" + ip + '\'' + + ", port=" + port + + ", ssrc='" + ssrc + '\'' + + ", targetId='" + targetId + '\'' + + ", app='" + app + '\'' + + ", channelId='" + channelId + '\'' + + ", status=" + status + + ", stream='" + stream + '\'' + + ", tcp=" + tcp + + ", tcpActive=" + tcpActive + + ", localIp='" + localIp + '\'' + + ", localPort=" + localPort + + ", mediaServerId='" + mediaServerId + '\'' + + ", serverId='" + serverId + '\'' + + ", CallId='" + callId + '\'' + + ", fromTag='" + fromTag + '\'' + + ", toTag='" + toTag + '\'' + + ", pt=" + pt + + ", usePs=" + usePs + + ", onlyAudio=" + onlyAudio + + ", rtcp=" + rtcp + + ", playType=" + playType + + ", receiveStream='" + receiveStream + '\'' + + ", sessionName='" + sessionName + '\'' + + '}'; + } + + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java deleted file mode 100755 index 55f09df3e..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java +++ /dev/null @@ -1,427 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.bean; - -import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg; - -import com.genersoft.iot.vmp.common.VideoManagerConstants; - -public class SendRtpItem { - - /** - * 推流ip - */ - private String ip; - - /** - * 推流端口 - */ - private int port; - - /** - * 推流标识 - */ - private String ssrc; - - /** - * 平台id - */ - private String platformId; - - /** - * 平台名称 - */ - private String platformName; - - /** - * 对应设备id - */ - private String deviceId; - - /** - * 直播流的应用名 - */ - private String app; - - /** - * 通道id - */ - private String channelId; - - /** - * 推流状态 - * 0 等待设备推流上来 - * 1 等待上级平台回复ack - * 2 推流中 - */ - private int status = 0; - - - /** - * 设备推流的streamId - */ - private String stream; - - /** - * 是否为tcp - */ - private boolean tcp; - - /** - * 是否为tcp主动模式 - */ - private boolean tcpActive; - - /** - * 自己推流使用的IP - */ - private String localIp; - - /** - * 自己推流使用的端口 - */ - private int localPort; - - /** - * 使用的流媒体 - */ - private String mediaServerId; - - /** - * 使用的服务的ID - */ - private String serverId; - - /** - * invite 的 callId - */ - private String callId; - - /** - * invite 的 fromTag - */ - private String fromTag; - - /** - * invite 的 toTag - */ - private String toTag; - - /** - * 发送时,rtp的pt(uint8_t),不传时默认为96 - */ - private int pt = 96; - - /** - * 发送时,rtp的负载类型。为true时,负载为ps;为false时,为es; - */ - private boolean usePs = true; - - /** - * 当usePs 为false时,有效。为1时,发送音频;为0时,发送视频;不传时默认为0 - */ - private boolean onlyAudio = false; - - /** - * 是否开启rtcp保活 - */ - private boolean rtcp = false; - - - /** - * 播放类型 - */ - private InviteStreamType playType; - - /** - * 发流的同时收流 - */ - private String receiveStream; - - /** - * 上级的点播类型 - */ - private String sessionName; - - public static SendRtpItem getInstance(RequestPushStreamMsg requestPushStreamMsg) { - SendRtpItem sendRtpItem = new SendRtpItem(); - sendRtpItem.setMediaServerId(requestPushStreamMsg.getMediaServerId()); - sendRtpItem.setApp(requestPushStreamMsg.getApp()); - sendRtpItem.setStream(requestPushStreamMsg.getStream()); - sendRtpItem.setIp(requestPushStreamMsg.getIp()); - sendRtpItem.setPort(requestPushStreamMsg.getPort()); - sendRtpItem.setSsrc(requestPushStreamMsg.getSsrc()); - sendRtpItem.setTcp(requestPushStreamMsg.isTcp()); - sendRtpItem.setLocalPort(requestPushStreamMsg.getSrcPort()); - sendRtpItem.setPt(requestPushStreamMsg.getPt()); - sendRtpItem.setUsePs(requestPushStreamMsg.isPs()); - sendRtpItem.setOnlyAudio(requestPushStreamMsg.isOnlyAudio()); - return sendRtpItem; - - } - - public static SendRtpItem getInstance(String app, String stream, String ssrc, String dstIp, Integer dstPort, boolean tcp, int sendLocalPort, Integer pt) { - SendRtpItem sendRtpItem = new SendRtpItem(); - sendRtpItem.setApp(app); - sendRtpItem.setStream(stream); - sendRtpItem.setSsrc(ssrc); - sendRtpItem.setTcp(tcp); - sendRtpItem.setLocalPort(sendLocalPort); - sendRtpItem.setIp(dstIp); - sendRtpItem.setPort(dstPort); - if (pt != null) { - sendRtpItem.setPt(pt); - } - - return sendRtpItem; - } - - public String getIp() { - return ip; - } - - public void setIp(String ip) { - this.ip = ip; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public String getSsrc() { - return ssrc; - } - - public void setSsrc(String ssrc) { - this.ssrc = ssrc; - } - - public String getPlatformId() { - return platformId; - } - - public void setPlatformId(String platformId) { - this.platformId = platformId; - } - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getChannelId() { - return channelId; - } - - public void setChannelId(String channelId) { - this.channelId = channelId; - } - - public int getStatus() { - return status; - } - - public void setStatus(int status) { - this.status = status; - } - - 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 boolean isTcp() { - return tcp; - } - - public void setTcp(boolean tcp) { - this.tcp = tcp; - } - - public int getLocalPort() { - return localPort; - } - - public void setLocalPort(int localPort) { - this.localPort = localPort; - } - - public boolean isTcpActive() { - return tcpActive; - } - - public void setTcpActive(boolean tcpActive) { - this.tcpActive = tcpActive; - } - - public String getMediaServerId() { - return mediaServerId; - } - - public void setMediaServerId(String mediaServerId) { - this.mediaServerId = mediaServerId; - } - - public String getCallId() { - return callId; - } - - public void setCallId(String callId) { - this.callId = callId; - } - - public InviteStreamType getPlayType() { - return playType; - } - - public void setPlayType(InviteStreamType playType) { - this.playType = playType; - } - - public int getPt() { - return pt; - } - - public void setPt(int pt) { - this.pt = pt; - } - - public boolean isUsePs() { - return usePs; - } - - public void setUsePs(boolean usePs) { - this.usePs = usePs; - } - - public boolean isOnlyAudio() { - return onlyAudio; - } - - public void setOnlyAudio(boolean onlyAudio) { - this.onlyAudio = onlyAudio; - } - - public String getServerId() { - return serverId; - } - - public void setServerId(String serverId) { - this.serverId = serverId; - } - - public String getFromTag() { - return fromTag; - } - - public void setFromTag(String fromTag) { - this.fromTag = fromTag; - } - - public String getToTag() { - return toTag; - } - - public void setToTag(String toTag) { - this.toTag = toTag; - } - - public boolean isRtcp() { - return rtcp; - } - - public void setRtcp(boolean rtcp) { - this.rtcp = rtcp; - } - - public String getReceiveStream() { - return receiveStream; - } - - public void setReceiveStream(String receiveStream) { - this.receiveStream = receiveStream; - } - - public String getPlatformName() { - return platformName; - } - - public void setPlatformName(String platformName) { - this.platformName = platformName; - } - - public String getLocalIp() { - return localIp; - } - - public void setLocalIp(String localIp) { - this.localIp = localIp; - } - - public String getSessionName() { - return sessionName; - } - - public void setSessionName(String sessionName) { - this.sessionName = sessionName; - } - - @Override - public String toString() { - return "SendRtpItem{" + - "ip='" + ip + '\'' + - ", port=" + port + - ", ssrc='" + ssrc + '\'' + - ", platformId='" + platformId + '\'' + - ", platformName='" + platformName + '\'' + - ", deviceId='" + deviceId + '\'' + - ", app='" + app + '\'' + - ", channelId='" + channelId + '\'' + - ", status=" + status + - ", stream='" + stream + '\'' + - ", tcp=" + tcp + - ", tcpActive=" + tcpActive + - ", localIp='" + localIp + '\'' + - ", localPort=" + localPort + - ", mediaServerId='" + mediaServerId + '\'' + - ", serverId='" + serverId + '\'' + - ", CallId='" + callId + '\'' + - ", fromTag='" + fromTag + '\'' + - ", toTag='" + toTag + '\'' + - ", pt=" + pt + - ", usePs=" + usePs + - ", onlyAudio=" + onlyAudio + - ", rtcp=" + rtcp + - ", playType=" + playType + - ", receiveStream='" + receiveStream + '\'' + - ", sessionName='" + sessionName + '\'' + - '}'; - } - - public String getRedisKey() { - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + - serverId + "_" - + mediaServerId + "_" - + platformId + "_" - + channelId + "_" - + stream + "_" - + callId; - return key; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SipMsgInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SipMsgInfo.java index 302539b6a..06d43c8da 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SipMsgInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SipMsgInfo.java @@ -7,7 +7,7 @@ import javax.sip.RequestEvent; public class SipMsgInfo { private RequestEvent evt; private Device device; - private ParentPlatform platform; + private Platform platform; private Element rootElement; public SipMsgInfo(RequestEvent evt, Device device, Element rootElement) { @@ -16,7 +16,7 @@ public class SipMsgInfo { this.rootElement = rootElement; } - public SipMsgInfo(RequestEvent evt, ParentPlatform platform, Element rootElement) { + public SipMsgInfo(RequestEvent evt, Platform platform, Element rootElement) { this.evt = evt; this.platform = platform; this.rootElement = rootElement; @@ -38,11 +38,11 @@ public class SipMsgInfo { this.device = device; } - public ParentPlatform getPlatform() { + public Platform getPlatform() { return platform; } - public void setPlatform(ParentPlatform platform) { + public void setPlatform(Platform platform) { this.platform = platform; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java index 6ed8d1441..893db116e 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java @@ -1,81 +1,84 @@ package com.genersoft.iot.vmp.gb28181.bean; import com.genersoft.iot.vmp.common.InviteSessionType; +import gov.nist.javax.sip.message.SIPResponse; +import lombok.Data; +@Data public class SsrcTransaction { + /** + * 设备编号 + */ private String deviceId; - private String channelId; + + /** + * 上级平台的编号 + */ + private String platformId; + + /** + * 通道的数据库ID + */ + private Integer channelId; + + /** + * 会话的CALL ID + */ private String callId; + + /** + * 关联的流ID + */ private String stream; + + /** + * 使用的流媒体 + */ private String mediaServerId; + + /** + * 使用的SSRC + */ private String ssrc; + /** + * 事务信息 + */ private SipTransactionInfo sipTransactionInfo; + /** + * 类型 + */ private InviteSessionType type; - public String getDeviceId() { - return deviceId; + public static SsrcTransaction buildForDevice(String deviceId, Integer channelId, String callId, String stream, + String ssrc, String mediaServerId, SIPResponse response, InviteSessionType type) { + SsrcTransaction ssrcTransaction = new SsrcTransaction(); + ssrcTransaction.setDeviceId(deviceId); + ssrcTransaction.setChannelId(channelId); + ssrcTransaction.setCallId(callId); + ssrcTransaction.setStream(stream); + ssrcTransaction.setMediaServerId(mediaServerId); + ssrcTransaction.setSsrc(ssrc); + ssrcTransaction.setSipTransactionInfo(new SipTransactionInfo(response)); + ssrcTransaction.setType(type); + return ssrcTransaction; + } + public static SsrcTransaction buildForPlatform(String platformId, Integer channelId, String callId, String stream, + String ssrc, String mediaServerId, SIPResponse response, InviteSessionType type) { + SsrcTransaction ssrcTransaction = new SsrcTransaction(); + ssrcTransaction.setPlatformId(platformId); + ssrcTransaction.setChannelId(channelId); + ssrcTransaction.setCallId(callId); + ssrcTransaction.setStream(stream); + ssrcTransaction.setMediaServerId(mediaServerId); + ssrcTransaction.setSsrc(ssrc); + ssrcTransaction.setSipTransactionInfo(new SipTransactionInfo(response)); + ssrcTransaction.setType(type); + return ssrcTransaction; } - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getChannelId() { - return channelId; - } - - public void setChannelId(String channelId) { - this.channelId = channelId; - } - - public String getCallId() { - return callId; - } - - public void setCallId(String callId) { - this.callId = callId; - } - - public String getStream() { - return stream; - } - - public void setStream(String stream) { - this.stream = stream; - } - - public String getMediaServerId() { - return mediaServerId; - } - - public void setMediaServerId(String mediaServerId) { - this.mediaServerId = mediaServerId; - } - - public String getSsrc() { - return ssrc; - } - - public void setSsrc(String ssrc) { - this.ssrc = ssrc; - } - - public InviteSessionType getType() { - return type; - } - - public void setType(InviteSessionType type) { - this.type = type; - } - - public SipTransactionInfo getSipTransactionInfo() { - return sipTransactionInfo; - } - - public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { - this.sipTransactionInfo = sipTransactionInfo; + public SsrcTransaction() { } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeInfo.java index e5c504559..a131ccb43 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeInfo.java @@ -2,9 +2,12 @@ package com.genersoft.iot.vmp.gb28181.bean; import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPResponse; +import lombok.Data; import javax.sip.header.*; +import java.util.UUID; +@Data public class SubscribeInfo { @@ -31,9 +34,9 @@ public class SubscribeInfo { /** * 以下为可选字段 - * @return */ private String sn; + private int gpsInterval; /** @@ -51,91 +54,18 @@ public class SubscribeInfo { */ private String simulatedCallId; - public String getId() { - return id; + + public static SubscribeInfo buildSimulated(String platFormServerId, String platFormServerIp){ + SubscribeInfo subscribeInfo = new SubscribeInfo(); + subscribeInfo.setId(platFormServerId); + subscribeInfo.setExpires(-1); + subscribeInfo.setEventType("Catalog"); + int random = (int) Math.floor(Math.random() * 10000); + subscribeInfo.setEventId(random + ""); + subscribeInfo.setSimulatedCallId(UUID.randomUUID().toString().replace("-", "") + "@" + platFormServerIp); + subscribeInfo.setSimulatedFromTag(UUID.randomUUID().toString().replace("-", "")); + subscribeInfo.setSimulatedToTag(UUID.randomUUID().toString().replace("-", "")); + return subscribeInfo; } - public void setId(String id) { - this.id = id; - } - - public SIPRequest getRequest() { - return request; - } - - public void setRequest(SIPRequest request) { - this.request = request; - } - - public int getExpires() { - return expires; - } - - public void setExpires(int expires) { - this.expires = expires; - } - - public String getEventId() { - return eventId; - } - - public void setEventId(String eventId) { - this.eventId = eventId; - } - - public String getEventType() { - return eventType; - } - - public void setEventType(String eventType) { - this.eventType = eventType; - } - - public SIPResponse getResponse() { - return response; - } - - public void setResponse(SIPResponse response) { - this.response = response; - } - - public String getSn() { - return sn; - } - - public void setSn(String sn) { - this.sn = sn; - } - - public int getGpsInterval() { - return gpsInterval; - } - - public void setGpsInterval(int gpsInterval) { - this.gpsInterval = gpsInterval; - } - - public String getSimulatedFromTag() { - return simulatedFromTag; - } - - public void setSimulatedFromTag(String simulatedFromTag) { - this.simulatedFromTag = simulatedFromTag; - } - - public String getSimulatedCallId() { - return simulatedCallId; - } - - public void setSimulatedCallId(String simulatedCallId) { - this.simulatedCallId = simulatedCallId; - } - - public String getSimulatedToTag() { - return simulatedToTag; - } - - public void setSimulatedToTag(String simulatedToTag) { - this.simulatedToTag = simulatedToTag; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/conf/DefaultProperties.java b/src/main/java/com/genersoft/iot/vmp/gb28181/conf/DefaultProperties.java index e2c9ef58b..0afe80a1f 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/conf/DefaultProperties.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/conf/DefaultProperties.java @@ -44,19 +44,19 @@ public class DefaultProperties { // 定义应用程序打算多久审计一次 SIP 堆栈,了解其内部线程的健康状况(该属性指定连续审计之间的时间(以毫秒为单位)) properties.setProperty("gov.nist.javax.sip.THREAD_AUDIT_INTERVAL_IN_MILLISECS", "30000"); -// properties.setProperty("gov.nist.javax.sip.MESSAGE_PROCESSOR_FACTORY", "gov.nist.javax.sip.stack.NioMessageProcessorFactory"); + properties.setProperty("gov.nist.javax.sip.MESSAGE_PROCESSOR_FACTORY", "gov.nist.javax.sip.stack.NioMessageProcessorFactory"); /** * sip_server_log.log 和 sip_debug_log.log ERROR, INFO, WARNING, OFF, DEBUG, TRACE */ - Logger logger = LoggerFactory.getLogger(AlarmNotifyMessageHandler.class); + Logger log = LoggerFactory.getLogger(AlarmNotifyMessageHandler.class); if (sipLog) { properties.setProperty("gov.nist.javax.sip.STACK_LOGGER", "com.genersoft.iot.vmp.gb28181.conf.StackLoggerImpl"); properties.setProperty("gov.nist.javax.sip.SERVER_LOGGER", "com.genersoft.iot.vmp.gb28181.conf.ServerLoggerImpl"); properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "true"); - logger.info("[SIP日志]已开启"); + log.info("[SIP日志]已开启"); }else { - logger.info("[SIP日志]已关闭"); + log.info("[SIP日志]已关闭"); } return properties; } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/AlarmController.java similarity index 91% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/controller/AlarmController.java index 10c99a5aa..fdde8bf57 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/AlarmController.java @@ -1,14 +1,15 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.alarm; +package com.genersoft.iot.vmp.gb28181.controller; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.security.JwtUtils; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.service.IDeviceAlarmService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IPlatformService; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; -import com.genersoft.iot.vmp.service.IDeviceAlarmService; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.github.pagehelper.PageInfo; @@ -16,8 +17,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; @@ -29,13 +29,11 @@ import java.util.Arrays; import java.util.List; @Tag(name = "报警信息管理") - +@Slf4j @RestController @RequestMapping("/api/alarm") public class AlarmController { - private final static Logger logger = LoggerFactory.getLogger(AlarmController.class); - @Autowired private IDeviceAlarmService deviceAlarmService; @@ -46,7 +44,10 @@ public class AlarmController { private ISIPCommanderForPlatform commanderForPlatform; @Autowired - private IVideoManagerStorage storage; + private IPlatformService platformService; + + @Autowired + private IDeviceService deviceService; /** @@ -98,8 +99,8 @@ public class AlarmController { @Operation(summary = "测试向上级/设备发送模拟报警通知", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Parameter(name = "deviceId", description = "设备国标编号") public void delete(@RequestParam String deviceId) { - Device device = storage.queryVideoDevice(deviceId); - ParentPlatform platform = storage.queryParentPlatByServerGBId(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); + Platform platform = platformService.queryPlatformByServerGBId(deviceId); DeviceAlarm deviceAlarm = new DeviceAlarm(); deviceAlarm.setChannelId(deviceId); deviceAlarm.setAlarmDescription("test"); @@ -121,7 +122,7 @@ public class AlarmController { try { commanderForPlatform.sendAlarmMessage(platform, deviceAlarm); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } }else { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/CommonChannelController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/CommonChannelController.java new file mode 100755 index 000000000..398b107dc --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/CommonChannelController.java @@ -0,0 +1,216 @@ +package com.genersoft.iot.vmp.gb28181.controller; + +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.security.JwtUtils; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelToGroupByGbDeviceParam; +import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelToGroupParam; +import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelToRegionByGbDeviceParam; +import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelToRegionParam; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelPlayService; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; +import com.genersoft.iot.vmp.media.service.IMediaServerService; +import com.genersoft.iot.vmp.service.bean.ErrorCallback; +import com.genersoft.iot.vmp.service.bean.InviteErrorCode; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.vmanager.bean.StreamContent; +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +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; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.sip.message.Response; +import java.util.List; + + +@Tag(name = "全局通道管理") +@RestController +@Slf4j +@RequestMapping(value = "/api/common/channel") +public class CommonChannelController { + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IGbChannelService channelService; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private IGbChannelPlayService channelPlayService; + + @Autowired + private UserSetting userSetting; + + + @Operation(summary = "查询通道信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "id", description = "通道的数据库自增Id", required = true) + @GetMapping(value = "/one") + public CommonGBChannel getOne(int id){ + return channelService.getOne(id); + } + + @Operation(summary = "获取行业编码列表", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @GetMapping("/industry/list") + public List getIndustryCodeList(){ + return channelService.getIndustryCodeList(); + } + + @Operation(summary = "获取编码列表", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @GetMapping("/type/list") + public List getDeviceTypeList(){ + return channelService.getDeviceTypeList(); + } + + @Operation(summary = "获取编码列表", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @GetMapping("/network/identification/list") + public List getNetworkIdentificationTypeList(){ + return channelService.getNetworkIdentificationTypeList(); + } + + @Operation(summary = "更新通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/update") + public void update(@RequestBody CommonGBChannel channel){ + channelService.update(channel); + } + + @Operation(summary = "重置国标通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/reset") + public void reset(Integer id){ + channelService.reset(id); + } + + @Operation(summary = "增加通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/add") + public CommonGBChannel add(@RequestBody CommonGBChannel channel){ + channelService.add(channel); + return channel; + } + + @Operation(summary = "获取通道列表", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页查询数量", required = true) + @Parameter(name = "query", description = "查询内容") + @Parameter(name = "online", description = "是否在线") + @Parameter(name = "hasCivilCode", description = "是否分配行政区划") + @GetMapping("/list") + public PageInfo queryList(int page, int count, + @RequestParam(required = false) String query, + @RequestParam(required = false) Boolean online, + @RequestParam(required = false) Boolean hasCivilCode, + @RequestParam(required = false) Boolean hasGroup){ + if (ObjectUtils.isEmpty(query)){ + query = null; + } + return channelService.queryList(page, count, query, online, hasCivilCode, hasGroup); + } + + @Operation(summary = "通道设置行政区划", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/region/add") + public void addChannelToRegion(@RequestBody ChannelToRegionParam param){ + Assert.notEmpty(param.getChannelIds(),"通道ID不可为空"); + Assert.hasLength(param.getCivilCode(),"未添加行政区划"); + channelService.addChannelToRegion(param.getCivilCode(), param.getChannelIds()); + } + + @Operation(summary = "通道删除行政区划", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/region/delete") + public void deleteChannelToRegion(@RequestBody ChannelToRegionParam param){ + Assert.isTrue(!param.getChannelIds().isEmpty() || !ObjectUtils.isEmpty(param.getCivilCode()),"参数异常"); + channelService.deleteChannelToRegion(param.getCivilCode(), param.getChannelIds()); + } + + @Operation(summary = "通道设置行政区划-根据国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/region/device/add") + public void addChannelToRegionByGbDevice(@RequestBody ChannelToRegionByGbDeviceParam param){ + Assert.notEmpty(param.getDeviceIds(),"参数异常"); + Assert.hasLength(param.getCivilCode(),"未添加行政区划"); + channelService.addChannelToRegionByGbDevice(param.getCivilCode(), param.getDeviceIds()); + } + + @Operation(summary = "通道删除行政区划-根据国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/region/device/delete") + public void deleteChannelToRegionByGbDevice(@RequestBody ChannelToRegionByGbDeviceParam param){ + Assert.notEmpty(param.getDeviceIds(),"参数异常"); + channelService.deleteChannelToRegionByGbDevice(param.getDeviceIds()); + } + + @Operation(summary = "通道设置业务分组", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/group/add") + public void addChannelToGroup(@RequestBody ChannelToGroupParam param){ + Assert.notEmpty(param.getChannelIds(),"通道ID不可为空"); + Assert.hasLength(param.getParentId(),"未添加上级分组编号"); + Assert.hasLength(param.getBusinessGroup(),"未添加业务分组"); + channelService.addChannelToGroup(param.getParentId(), param.getBusinessGroup(), param.getChannelIds()); + } + + @Operation(summary = "通道删除业务分组", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/group/delete") + public void deleteChannelToGroup(@RequestBody ChannelToGroupParam param){ + Assert.isTrue(!param.getChannelIds().isEmpty() + || (!ObjectUtils.isEmpty(param.getParentId()) && !ObjectUtils.isEmpty(param.getBusinessGroup())), + "参数异常"); + channelService.deleteChannelToGroup(param.getParentId(), param.getBusinessGroup(), param.getChannelIds()); + } + + @Operation(summary = "通道设置业务分组-根据国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/group/device/add") + public void addChannelToGroupByGbDevice(@RequestBody ChannelToGroupByGbDeviceParam param){ + Assert.notEmpty(param.getDeviceIds(),"参数异常"); + Assert.hasLength(param.getParentId(),"未添加上级分组编号"); + Assert.hasLength(param.getBusinessGroup(),"未添加业务分组"); + channelService.addChannelToGroupByGbDevice(param.getParentId(), param.getBusinessGroup(), param.getDeviceIds()); + } + + @Operation(summary = "通道删除业务分组-根据国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/group/device/delete") + public void deleteChannelToGroupByGbDevice(@RequestBody ChannelToGroupByGbDeviceParam param){ + Assert.notEmpty(param.getDeviceIds(),"参数异常"); + channelService.deleteChannelToGroupByGbDevice(param.getDeviceIds()); + } + + @Operation(summary = "播放通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @GetMapping("/play") + public DeferredResult> deleteChannelToGroupByGbDevice(Integer channelId){ + Assert.notNull(channelId,"参数异常"); + CommonGBChannel channel = channelService.getOne(channelId); + Assert.notNull(channel, "通道不存在"); + + DeferredResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); + + ErrorCallback callback = (code, msg, data) -> { + if (code == InviteErrorCode.SUCCESS.getCode()) { + result.setResult(WVPResult.success(new StreamContent(data))); + }else { + result.setResult(WVPResult.fail(code, msg)); + } + }; + + if (channel.getGbDeviceDbId() != null) { + // 国标通道 + channelPlayService.playGbDeviceChannel(channel, callback); + } else if (channel.getStreamProxyId() != null) { + // 拉流代理 + channelPlayService.playProxy(channel, callback); + } else if (channel.getStreamPushId() != null) { + // 推流 + channelPlayService.playPush(channel, null, null, callback); + } else { + // 通道数据异常 + log.error("[点播通用通道] 通道数据异常,无法识别通道来源: {}({})", channel.getGbName(), channel.getGbDeviceId()); + throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); + } + return result; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceConfig.java similarity index 87% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceConfig.java index 2f323619f..f6e7ddcb8 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceConfig.java @@ -5,23 +5,22 @@ * @date 2021年2月2日 */ -package com.genersoft.iot.vmp.vmanager.gb28181.device; +package com.genersoft.iot.vmp.gb28181.controller; import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.security.JwtUtils; import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; 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.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; @@ -32,15 +31,14 @@ import javax.sip.SipException; import java.text.ParseException; import java.util.UUID; +@Slf4j @Tag(name = "国标设备配置") @RestController @RequestMapping("/api/device/config") public class DeviceConfig { - private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); - @Autowired - private IVideoManagerStorage storager; + private IDeviceService deviceService; @Autowired private SIPCommander cmder; @@ -72,10 +70,10 @@ public class DeviceConfig { @RequestParam(required = false) String expiration, @RequestParam(required = false) String heartBeatInterval, @RequestParam(required = false) String heartBeatCount) { - if (logger.isDebugEnabled()) { - logger.debug("报警复位API调用"); + if (log.isDebugEnabled()) { + log.debug("报警复位API调用"); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); String uuid = UUID.randomUUID().toString(); String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + deviceId + channelId; try { @@ -87,12 +85,12 @@ public class DeviceConfig { resultHolder.invokeResult(msg); }); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 设备配置: {}", e.getMessage()); + log.error("[命令发送失败] 设备配置: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } DeferredResult result = new DeferredResult(3 * 1000L); result.onTimeout(() -> { - logger.warn(String.format("设备配置操作超时, 设备未返回应答指令")); + log.warn(String.format("设备配置操作超时, 设备未返回应答指令")); // 释放rtpserver RequestMessage msg = new RequestMessage(); msg.setId(uuid); @@ -123,12 +121,12 @@ public class DeviceConfig { public DeferredResult configDownloadApi(@PathVariable String deviceId, @PathVariable String configType, @RequestParam(required = false) String channelId) { - if (logger.isDebugEnabled()) { - logger.debug("设备状态查询API调用"); + if (log.isDebugEnabled()) { + log.debug("设备状态查询API调用"); } String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId); String uuid = UUID.randomUUID().toString(); - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); try { cmder.deviceConfigQuery(device, channelId, configType, event -> { RequestMessage msg = new RequestMessage(); @@ -138,12 +136,12 @@ public class DeviceConfig { resultHolder.invokeResult(msg); }); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 获取设备配置: {}", e.getMessage()); + log.error("[命令发送失败] 获取设备配置: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } DeferredResult result = new DeferredResult (3 * 1000L); result.onTimeout(()->{ - logger.warn(String.format("获取设备配置超时")); + log.warn(String.format("获取设备配置超时")); // 释放rtpserver RequestMessage msg = new RequestMessage(); msg.setId(uuid); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceControl.java similarity index 84% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceControl.java index 2f5a5894c..06d5fa85f 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceControl.java @@ -5,7 +5,7 @@ * @date 2021年2月1日 */ -package com.genersoft.iot.vmp.vmanager.gb28181.device; +package com.genersoft.iot.vmp.gb28181.controller; import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.conf.exception.ControllerException; @@ -14,14 +14,13 @@ import com.genersoft.iot.vmp.gb28181.bean.Device; 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.ISIPCommander; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.util.ObjectUtils; @@ -34,15 +33,13 @@ import java.text.ParseException; import java.util.UUID; @Tag(name = "国标设备控制") - +@Slf4j @RestController @RequestMapping("/api/device/control") public class DeviceControl { - private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); - @Autowired - private IVideoManagerStorage storager; + private IDeviceService deviceService; @Autowired private ISIPCommander cmder; @@ -59,14 +56,14 @@ public class DeviceControl { @Parameter(name = "deviceId", description = "设备国标编号", required = true) @GetMapping("/teleboot/{deviceId}") public void teleBootApi(@PathVariable String deviceId) { - if (logger.isDebugEnabled()) { - logger.debug("设备远程启动API调用"); + if (log.isDebugEnabled()) { + log.debug("设备远程启动API调用"); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); try { cmder.teleBootCmd(device); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 远程启动: {}", e.getMessage()); + log.error("[命令发送失败] 远程启动: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } } @@ -85,15 +82,15 @@ public class DeviceControl { @GetMapping("/record/{deviceId}/{recordCmdStr}") public DeferredResult> recordApi(@PathVariable String deviceId, @PathVariable String recordCmdStr, String channelId) { - if (logger.isDebugEnabled()) { - logger.debug("开始/停止录像API调用"); + if (log.isDebugEnabled()) { + log.debug("开始/停止录像API调用"); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); String uuid = UUID.randomUUID().toString(); String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId; DeferredResult> result = new DeferredResult>(3 * 1000L); result.onTimeout(() -> { - logger.warn(String.format("开始/停止录像操作超时, 设备未返回应答指令")); + log.warn(String.format("开始/停止录像操作超时, 设备未返回应答指令")); // 释放rtpserver RequestMessage msg = new RequestMessage(); msg.setKey(key); @@ -114,7 +111,7 @@ public class DeviceControl { resultHolder.invokeAllResult(msg); },null); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 开始/停止录像: {}", e.getMessage()); + log.error("[命令发送失败] 开始/停止录像: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } @@ -132,10 +129,10 @@ public class DeviceControl { @Parameter(name = "guardCmdStr", description = "命令, 可选值:SetGuard(布防),ResetGuard(撤防)", required = true) @GetMapping("/guard/{deviceId}/{guardCmdStr}") public DeferredResult guardApi(@PathVariable String deviceId, @PathVariable String guardCmdStr) { - if (logger.isDebugEnabled()) { - logger.debug("布防/撤防API调用"); + if (log.isDebugEnabled()) { + log.debug("布防/撤防API调用"); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + deviceId; String uuid =UUID.randomUUID().toString(); try { @@ -147,13 +144,13 @@ public class DeviceControl { resultHolder.invokeResult(msg); },null); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 布防/撤防操作: {}", e.getMessage()); + log.error("[命令发送失败] 布防/撤防操作: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage()); } DeferredResult result = new DeferredResult<>(3 * 1000L); resultHolder.put(key, uuid, result); result.onTimeout(() -> { - logger.warn(String.format("布防/撤防操作超时, 设备未返回应答指令")); + log.warn(String.format("布防/撤防操作超时, 设备未返回应答指令")); // 释放rtpserver RequestMessage msg = new RequestMessage(); msg.setKey(key); @@ -181,10 +178,10 @@ public class DeviceControl { public DeferredResult> resetAlarmApi(@PathVariable String deviceId, String channelId, @RequestParam(required = false) String alarmMethod, @RequestParam(required = false) String alarmType) { - if (logger.isDebugEnabled()) { - logger.debug("报警复位API调用"); + if (log.isDebugEnabled()) { + log.debug("报警复位API调用"); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); String uuid = UUID.randomUUID().toString(); String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId; try { @@ -196,12 +193,12 @@ public class DeviceControl { resultHolder.invokeResult(msg); },null); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 报警复位: {}", e.getMessage()); + log.error("[命令发送失败] 报警复位: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } DeferredResult> result = new DeferredResult>(3 * 1000L); result.onTimeout(() -> { - logger.warn(String.format("报警复位操作超时, 设备未返回应答指令")); + log.warn(String.format("报警复位操作超时, 设备未返回应答指令")); // 释放rtpserver RequestMessage msg = new RequestMessage(); msg.setId(uuid); @@ -225,14 +222,14 @@ public class DeviceControl { @GetMapping("/i_frame/{deviceId}") public JSONObject iFrame(@PathVariable String deviceId, @RequestParam(required = false) String channelId) { - if (logger.isDebugEnabled()) { - logger.debug("强制关键帧API调用"); + if (log.isDebugEnabled()) { + log.debug("强制关键帧API调用"); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); try { cmder.iFrameCmd(device, channelId); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 强制关键帧: {}", e.getMessage()); + log.error("[命令发送失败] 强制关键帧: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } JSONObject json = new JSONObject(); @@ -261,12 +258,12 @@ public class DeviceControl { public DeferredResult homePositionApi(String deviceId, String channelId, Boolean enabled, @RequestParam(required = false) Integer resetTime, @RequestParam(required = false) Integer presetIndex) { - if (logger.isDebugEnabled()) { - logger.debug("报警复位API调用"); + if (log.isDebugEnabled()) { + log.debug("报警复位API调用"); } String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId); String uuid = UUID.randomUUID().toString(); - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); try { cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> { RequestMessage msg = new RequestMessage(); @@ -276,12 +273,12 @@ public class DeviceControl { resultHolder.invokeResult(msg); },null); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 看守位控制: {}", e.getMessage()); + log.error("[命令发送失败] 看守位控制: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } DeferredResult result = new DeferredResult<>(3 * 1000L); result.onTimeout(() -> { - logger.warn(String.format("看守位控制操作超时, 设备未返回应答指令")); + log.warn(String.format("看守位控制操作超时, 设备未返回应答指令")); // 释放rtpserver RequestMessage msg = new RequestMessage(); msg.setId(uuid); @@ -326,10 +323,10 @@ public class DeviceControl { @RequestParam int midpointy, @RequestParam int lengthx, @RequestParam int lengthy) throws RuntimeException { - if (logger.isDebugEnabled()) { - logger.debug(String.format("设备拉框放大 API调用,deviceId:%s ,channelId:%s ,length:%d ,width:%d ,midpointx:%d ,midpointy:%d ,lengthx:%d ,lengthy:%d",deviceId, channelId, length, width, midpointx, midpointy,lengthx, lengthy)); + if (log.isDebugEnabled()) { + log.debug(String.format("设备拉框放大 API调用,deviceId:%s ,channelId:%s ,length:%d ,width:%d ,midpointx:%d ,midpointy:%d ,lengthx:%d ,lengthy:%d",deviceId, channelId, length, width, midpointx, midpointy,lengthx, lengthy)); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); StringBuffer cmdXml = new StringBuffer(200); cmdXml.append("\r\n"); cmdXml.append("" + length+ "\r\n"); @@ -342,7 +339,7 @@ public class DeviceControl { try { cmder.dragZoomCmd(device, channelId, cmdXml.toString()); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 拉框放大: {}", e.getMessage()); + log.error("[命令发送失败] 拉框放大: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } } @@ -378,10 +375,10 @@ public class DeviceControl { @RequestParam int lengthx, @RequestParam int lengthy){ - if (logger.isDebugEnabled()) { - logger.debug(String.format("设备拉框缩小 API调用,deviceId:%s ,channelId:%s ,length:%d ,width:%d ,midpointx:%d ,midpointy:%d ,lengthx:%d ,lengthy:%d",deviceId, channelId, length, width, midpointx, midpointy,lengthx, lengthy)); + if (log.isDebugEnabled()) { + log.debug(String.format("设备拉框缩小 API调用,deviceId:%s ,channelId:%s ,length:%d ,width:%d ,midpointx:%d ,midpointy:%d ,lengthx:%d ,lengthy:%d",deviceId, channelId, length, width, midpointx, midpointy,lengthx, lengthy)); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); StringBuffer cmdXml = new StringBuffer(200); cmdXml.append("\r\n"); cmdXml.append("" + length+ "\r\n"); @@ -394,7 +391,7 @@ public class DeviceControl { try { cmder.dragZoomCmd(device, channelId, cmdXml.toString()); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 拉框缩小: {}", e.getMessage()); + log.error("[命令发送失败] 拉框缩小: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceQuery.java similarity index 72% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceQuery.java index 8a2618bbd..c3ba87a48 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceQuery.java @@ -1,4 +1,4 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.device; +package com.genersoft.iot.vmp.gb28181.controller; import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.conf.DynamicTask; @@ -7,18 +7,15 @@ import com.genersoft.iot.vmp.conf.security.JwtUtils; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.SyncStatus; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask; import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask; import com.genersoft.iot.vmp.gb28181.transmit.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.IDeviceChannelService; -import com.genersoft.iot.vmp.service.IDeviceService; -import com.genersoft.iot.vmp.service.IInviteStreamService; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; -import com.genersoft.iot.vmp.vmanager.bean.BaseTree; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import com.github.pagehelper.PageInfo; @@ -26,14 +23,14 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.compress.utils.IOUtils; import org.apache.ibatis.annotations.Options; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; @@ -47,26 +44,21 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.text.ParseException; -import java.util.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; @Tag(name = "国标设备查询", description = "国标设备查询") @SuppressWarnings("rawtypes") - +@Slf4j @RestController @RequestMapping("/api/device/query") public class DeviceQuery { - - private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); - - @Autowired - private IVideoManagerStorage storager; @Autowired private IDeviceChannelService deviceChannelService; - @Autowired - private IRedisCatchStorage redisCatchStorage; - @Autowired private IInviteStreamService inviteStreamService; @@ -92,7 +84,7 @@ public class DeviceQuery { @GetMapping("/devices/{deviceId}") public Device devices(@PathVariable String deviceId){ - return storager.queryVideoDevice(deviceId); + return deviceService.getDeviceByDeviceId(deviceId); } /** @@ -104,25 +96,19 @@ public class DeviceQuery { @Operation(summary = "分页查询国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Parameter(name = "page", description = "当前页", required = true) @Parameter(name = "count", description = "每页查询数量", required = true) + @Parameter(name = "query", description = "搜索", required = false) + @Parameter(name = "status", description = "状态", required = false) @GetMapping("/devices") @Options() - public PageInfo devices(int page, int count){ -// if (page == null) page = 0; -// if (count == null) count = 20; - return storager.queryVideoDeviceList(page, count,null); + public PageInfo devices(int page, int count, String query, Boolean status){ + if (ObjectUtils.isEmpty(query)){ + query = null; + } + return deviceService.getAll(page, count, query, status); } /** * 分页查询通道数 - * - * @param deviceId 设备id - * @param page 当前页 - * @param count 每页条数 - * @param query 查询内容 - * @param online 是否在线 在线 true / 离线 false - * @param channelType 设备 false/子目录 true - * @param catalogUnderDevice 是否直属与设备的目录 - * @return 通道列表 */ @GetMapping("/devices/{deviceId}/channels") @Operation(summary = "分页查询通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) @@ -132,34 +118,30 @@ public class DeviceQuery { @Parameter(name = "query", description = "查询内容") @Parameter(name = "online", description = "是否在线") @Parameter(name = "channelType", description = "设备/子目录-> false/true") - @Parameter(name = "catalogUnderDevice", description = "是否直属与设备的目录") public PageInfo channels(@PathVariable String deviceId, int page, int count, @RequestParam(required = false) String query, @RequestParam(required = false) Boolean online, - @RequestParam(required = false) Boolean channelType, - @RequestParam(required = false) Boolean catalogUnderDevice) { + @RequestParam(required = false) Boolean channelType) { if (ObjectUtils.isEmpty(query)) { query = null; } - return storager.queryChannelsByDeviceId(deviceId, query, channelType, online, catalogUnderDevice, page, count); + return deviceChannelService.queryChannelsByDeviceId(deviceId, query, channelType, online, page, count); } /** * 同步设备通道 - * @param deviceId 设备id - * @return */ @Operation(summary = "同步设备通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Parameter(name = "deviceId", description = "设备国标编号", required = true) @GetMapping("/devices/{deviceId}/sync") public WVPResult devicesSync(@PathVariable String deviceId){ - - if (logger.isDebugEnabled()) { - logger.debug("设备通道信息同步API调用,deviceId:" + deviceId); + + if (log.isDebugEnabled()) { + log.debug("设备通道信息同步API调用,deviceId:" + deviceId); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); boolean status = deviceService.isSyncRunning(deviceId); // 已存在则返回进度 if (status) { @@ -183,9 +165,9 @@ public class DeviceQuery { @Parameter(name = "deviceId", description = "设备国标编号", required = true) @DeleteMapping("/devices/{deviceId}/delete") public String delete(@PathVariable String deviceId){ - - if (logger.isDebugEnabled()) { - logger.debug("设备信息删除API调用,deviceId:" + deviceId); + + if (log.isDebugEnabled()) { + log.debug("设备信息删除API调用,deviceId:" + deviceId); } // 清除redis记录 @@ -208,7 +190,7 @@ public class DeviceQuery { json.put("deviceId", deviceId); return json.toString(); } else { - logger.warn("设备信息删除API调用失败!"); + log.warn("设备信息删除API调用失败!"); throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备信息删除API调用失败!"); } } @@ -241,27 +223,23 @@ public class DeviceQuery { @RequestParam(required = false) Boolean online, @RequestParam(required = false) Boolean channelType){ - DeviceChannel deviceChannel = storager.queryChannel(deviceId,channelId); + DeviceChannel deviceChannel = deviceChannelService.getOne(deviceId,channelId); if (deviceChannel == null) { PageInfo deviceChannelPageResult = new PageInfo<>(); return deviceChannelPageResult; } - return storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count); + return deviceChannelService.getSubChannels(deviceChannel.getDeviceDbId(), channelId, query, channelType, online, page, count); } - /** - * 更新通道信息 - * @param deviceId 设备id - * @param channel 通道 - * @return - */ - @Operation(summary = "更新通道信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "deviceId", description = "设备国标编号", required = true) - @Parameter(name = "channel", description = "通道信息", required = true) - @PostMapping("/channel/update/{deviceId}") - public void updateChannel(@PathVariable String deviceId,DeviceChannel channel){ - deviceChannelService.updateChannel(deviceId, channel); + @Operation(summary = "开启/关闭通道的音频", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "channelId", description = "通道的数据库ID", required = true) + @Parameter(name = "audio", description = "开启/关闭音频", required = true) + @PostMapping("/channel/audio") + public void changeAudio(Integer channelId, Boolean audio){ + Assert.notNull(channelId, "通道的数据库ID不可为NULL"); + Assert.notNull(audio, "开启/关闭音频不可为NULL"); + deviceChannelService.changeAudio(channelId, audio); } @Operation(summary = "修改通道的码流类型", security = @SecurityRequirement(name = JwtUtils.HEADER)) @@ -284,7 +262,7 @@ public class DeviceQuery { "UDP(udp传输),TCP-ACTIVE(tcp主动模式,暂不支持),TCP-PASSIVE(tcp被动模式)", required = true) @PostMapping("/transport/{deviceId}/{streamMode}") public void updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){ - Device device = deviceService.getDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); device.setStreamMode(streamMode); deviceService.updateCustomDevice(device); } @@ -331,17 +309,17 @@ public class DeviceQuery { /** * 设备状态查询请求API接口 - * + * * @param deviceId 设备id */ @Operation(summary = "设备状态查询", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Parameter(name = "deviceId", description = "设备国标编号", required = true) @GetMapping("/devices/{deviceId}/status") public DeferredResult> deviceStatusApi(@PathVariable String deviceId) { - if (logger.isDebugEnabled()) { - logger.debug("设备状态查询API调用"); + if (log.isDebugEnabled()) { + log.debug("设备状态查询API调用"); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); String uuid = UUID.randomUUID().toString(); String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId; DeferredResult> result = new DeferredResult>(2*1000L); @@ -358,11 +336,11 @@ public class DeviceQuery { resultHolder.invokeResult(msg); }); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 获取设备状态: {}", e.getMessage()); + log.error("[命令发送失败] 获取设备状态: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } result.onTimeout(()->{ - logger.warn(String.format("获取设备状态超时")); + log.warn(String.format("获取设备状态超时")); // 释放rtpserver RequestMessage msg = new RequestMessage(); msg.setId(uuid); @@ -395,16 +373,16 @@ public class DeviceQuery { @Parameter(name = "endTime", description = "报警发生终止时间") @GetMapping("/alarm/{deviceId}") public DeferredResult> alarmApi(@PathVariable String deviceId, - @RequestParam(required = false) String startPriority, - @RequestParam(required = false) String endPriority, + @RequestParam(required = false) String startPriority, + @RequestParam(required = false) String endPriority, @RequestParam(required = false) String alarmMethod, @RequestParam(required = false) String alarmType, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime) { - if (logger.isDebugEnabled()) { - logger.debug("设备报警查询API调用"); + if (log.isDebugEnabled()) { + log.debug("设备报警查询API调用"); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); String key = DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId; String uuid = UUID.randomUUID().toString(); try { @@ -416,12 +394,12 @@ public class DeviceQuery { resultHolder.invokeResult(msg); }); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 设备报警查询: {}", e.getMessage()); + log.error("[命令发送失败] 设备报警查询: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } DeferredResult> result = new DeferredResult> (3 * 1000L); result.onTimeout(()->{ - logger.warn(String.format("设备报警查询超时")); + log.warn(String.format("设备报警查询超时")); // 释放rtpserver RequestMessage msg = new RequestMessage(); msg.setId(uuid); @@ -495,95 +473,10 @@ public class DeviceQuery { } } - /** - * 查询国标树 - * @param deviceId 设备ID - * @param parentId 父ID - * @param page 当前页 - * @param count 每页条数 - * @return 国标设备 - */ - @Operation(summary = "查询国标树") - @Parameter(name = "deviceId", description = "设备国标编号", required = true) - @Parameter(name = "parentId", description = "父级国标编号") - @Parameter(name = "onlyCatalog", description = "只获取目录") - @Parameter(name = "page", description = "当前页", required = true) - @Parameter(name = "count", description = "每页条数", required = true) - @GetMapping("/tree/{deviceId}") - public ResponseEntity getTree(@PathVariable String deviceId, - @RequestParam(required = false) String parentId, - @RequestParam(required = false) Boolean onlyCatalog, - int page, int count){ - - - if (page <= 0) { - page = 1; - } - if (onlyCatalog == null) { - onlyCatalog = false; - } - - List> treeData = deviceService.queryVideoDeviceTree(deviceId, parentId, onlyCatalog); - if (treeData == null || (page - 1) * count > treeData.size()) { - PageInfo> pageInfo = new PageInfo<>(); - pageInfo.setPageNum(page); - pageInfo.setTotal(treeData == null? 0 : treeData.size()); - pageInfo.setSize(0); - pageInfo.setList(new ArrayList<>()); - return new ResponseEntity<>(pageInfo,HttpStatus.OK); - } - - int toIndex = Math.min(page * count, treeData.size()); - // 处理分页 - List> trees = treeData.subList((page - 1) * count, toIndex); - PageInfo> pageInfo = new PageInfo<>(); - pageInfo.setPageNum(page); - pageInfo.setTotal(treeData.size()); - pageInfo.setSize(trees.size()); - pageInfo.setList(trees); - - return new ResponseEntity<>(pageInfo,HttpStatus.OK); - } - - /** - * 查询国标树下的通道 - * @param deviceId 设备ID - * @param parentId 父ID - * @param page 当前页 - * @param count 每页条数 - * @return 国标设备 - */ - @Operation(summary = "查询国标树下的通道") - @Parameter(name = "deviceId", description = "设备国标编号", required = true) - @Parameter(name = "parentId", description = "父级国标编号") - @Parameter(name = "page", description = "当前页", required = true) - @Parameter(name = "count", description = "每页条数", required = true) - @GetMapping("/tree/channel/{deviceId}") - public ResponseEntity getChannelInTreeNode(@PathVariable String deviceId, @RequestParam(required = false) String parentId, int page, int count){ - - if (page <= 0) { - page = 1; - } - - List treeData = deviceService.queryVideoDeviceInTreeNode(deviceId, parentId); - if (treeData == null || (page - 1) * count > treeData.size()) { - PageInfo> pageInfo = new PageInfo<>(); - pageInfo.setPageNum(page); - pageInfo.setTotal(treeData == null? 0 : treeData.size()); - pageInfo.setSize(0); - pageInfo.setList(new ArrayList<>()); - return new ResponseEntity<>(pageInfo,HttpStatus.OK); - } - - int toIndex = Math.min(page * count, treeData.size()); - // 处理分页 - List trees = treeData.subList((page - 1) * count, toIndex); - PageInfo pageInfo = new PageInfo<>(); - pageInfo.setPageNum(page); - pageInfo.setTotal(treeData.size()); - pageInfo.setSize(trees.size()); - pageInfo.setList(trees); - - return new ResponseEntity<>(pageInfo,HttpStatus.OK); + @GetMapping("/channel/raw") + @Operation(summary = "国标通道编辑时的数据回显") + @Parameter(name = "id", description = "通道的Id", required = true) + public DeviceChannel getRawChannel(int id) { + return deviceChannelService.getRawChannel(id); } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/GBRecordController.java similarity index 75% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/controller/GBRecordController.java index 0f971eca0..e48ae616e 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/GBRecordController.java @@ -1,23 +1,20 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.record; +package com.genersoft.iot.vmp.gb28181.controller; -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; import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.conf.security.JwtUtils; import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IPlayService; 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; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.StreamContent; @@ -26,10 +23,8 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; 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; @@ -43,19 +38,14 @@ import java.text.ParseException; import java.util.UUID; @Tag(name = "国标录像") - +@Slf4j @RestController @RequestMapping("/api/gb_record") public class GBRecordController { - private final static Logger logger = LoggerFactory.getLogger(GBRecordController.class); - @Autowired private SIPCommander cmder; - @Autowired - private IVideoManagerStorage storager; - @Autowired private DeferredResultHolder resultHolder; @@ -63,7 +53,7 @@ public class GBRecordController { private IPlayService playService; @Autowired - private IInviteStreamService inviteStreamService; + private IDeviceChannelService channelService; @Autowired private IDeviceService deviceService; @@ -79,8 +69,8 @@ public class GBRecordController { @GetMapping("/query/{deviceId}/{channelId}") public DeferredResult> recordinfo(@PathVariable String deviceId, @PathVariable String channelId, String startTime, String endTime){ - if (logger.isDebugEnabled()) { - logger.debug(String.format("录像信息查询 API调用,deviceId:%s ,startTime:%s, endTime:%s",deviceId, startTime, endTime)); + if (log.isDebugEnabled()) { + log.debug(String.format("录像信息查询 API调用,deviceId:%s ,startTime:%s, endTime:%s",deviceId, startTime, endTime)); } DeferredResult> result = new DeferredResult<>(); if (!DateUtil.verification(startTime, DateUtil.formatter)){ @@ -90,7 +80,7 @@ public class GBRecordController { throw new ControllerException(ErrorCode.ERROR100.getCode(), "endTime格式为" + DateUtil.PATTERN); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); // 指定超时时间 1分钟30秒 String uuid = UUID.randomUUID().toString(); int sn = (int)((Math.random()*9+1)*100000); @@ -107,7 +97,7 @@ public class GBRecordController { resultHolder.invokeResult(msg); })); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 查询录像: {}", e.getMessage()); + log.error("[命令发送失败] 查询录像: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } @@ -135,8 +125,8 @@ public class GBRecordController { public DeferredResult> download(HttpServletRequest request, @PathVariable String deviceId, @PathVariable String channelId, String startTime, String endTime, String downloadSpeed) { - if (logger.isDebugEnabled()) { - logger.debug(String.format("历史媒体下载 API调用,deviceId:%s,channelId:%s,downloadSpeed:%s", deviceId, channelId, downloadSpeed)); + if (log.isDebugEnabled()) { + log.debug(String.format("历史媒体下载 API调用,deviceId:%s,channelId:%s,downloadSpeed:%s", deviceId, channelId, downloadSpeed)); } String uuid = UUID.randomUUID().toString(); @@ -147,8 +137,18 @@ public class GBRecordController { requestMessage.setId(uuid); requestMessage.setKey(key); + Device device = deviceService.getDeviceByDeviceId(deviceId); + if (device == null) { + log.warn("[开始历史媒体下载] 未找到设备 deviceId: {},channelId:{}", deviceId, channelId); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到设备:" + deviceId); + } - playService.download(deviceId, channelId, startTime, endTime, Integer.parseInt(downloadSpeed), + DeviceChannel channel = channelService.getOne(deviceId, channelId); + if (channel == null) { + log.warn("[开始历史媒体下载] 未找到通道 deviceId: {},channelId:{}", deviceId, channelId); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到通道:" + channelId); + } + playService.download(device, channel, startTime, endTime, Integer.parseInt(downloadSpeed), (code, msg, data)->{ WVPResult wvpResult = new WVPResult<>(); @@ -181,15 +181,15 @@ public class GBRecordController { @GetMapping("/download/stop/{deviceId}/{channelId}/{stream}") public void playStop(@PathVariable String deviceId, @PathVariable String channelId, @PathVariable String stream) { - if (logger.isDebugEnabled()) { - logger.debug(String.format("设备历史媒体下载停止 API调用,deviceId/channelId:%s_%s", deviceId, channelId)); + if (log.isDebugEnabled()) { + log.debug(String.format("设备历史媒体下载停止 API调用,deviceId/channelId:%s_%s", deviceId, channelId)); } if (deviceId == null || channelId == null) { throw new ControllerException(ErrorCode.ERROR400); } - Device device = deviceService.getDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); if (device == null) { throw new ControllerException(ErrorCode.ERROR400.getCode(), "设备:" + deviceId + "未找到"); } @@ -197,8 +197,7 @@ public class GBRecordController { try { cmder.streamByeCmd(device, channelId, stream, null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { - logger.error("[停止历史媒体下载]停止历史媒体下载,发送BYE失败 {}", e.getMessage()); - throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); + log.warn("[停止历史媒体下载]停止历史媒体下载,发送BYE失败 {}", e.getMessage()); } } @@ -208,7 +207,18 @@ public class GBRecordController { @Parameter(name = "stream", description = "流ID", required = true) @GetMapping("/download/progress/{deviceId}/{channelId}/{stream}") public StreamContent getProgress(@PathVariable String deviceId, @PathVariable String channelId, @PathVariable String stream) { - StreamInfo downLoadInfo = playService.getDownLoadInfo(deviceId, channelId, stream); + Device device = deviceService.getDeviceByDeviceId(deviceId); + if (device == null) { + log.warn("[获取历史媒体下载进度] 未找到设备 deviceId: {},channelId:{}", deviceId, channelId); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到设备:" + deviceId); + } + + DeviceChannel channel = channelService.getOne(deviceId, channelId); + if (channel == null) { + log.warn("[获取历史媒体下载进度] 未找到通道 deviceId: {},channelId:{}", deviceId, channelId); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到通道:" + channelId); + } + StreamInfo downLoadInfo = playService.getDownLoadInfo(device, channel, stream); if (downLoadInfo == null) { throw new ControllerException(ErrorCode.ERROR404); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/GroupController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/GroupController.java new file mode 100644 index 000000000..ef30624c9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/GroupController.java @@ -0,0 +1,88 @@ +package com.genersoft.iot.vmp.gb28181.controller; + +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.gb28181.bean.Group; +import com.genersoft.iot.vmp.gb28181.bean.GroupTree; +import com.genersoft.iot.vmp.gb28181.service.IGroupService; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Slf4j +@Tag(name = "分组管理") +@RestController +@RequestMapping("/api/group") +public class GroupController { + + @Autowired + private IGroupService groupService; + + @Operation(summary = "添加分组") + @Parameter(name = "group", description = "group", required = true) + @ResponseBody + @PostMapping("/add") + public void add(@RequestBody Group group){ + groupService.add(group); + } + + @Operation(summary = "查询分组") + @Parameter(name = "query", description = "要搜索的内容", required = true) + @Parameter(name = "parent", description = "所属分组编号", required = true) + @ResponseBody + @GetMapping("/tree/list") + public List queryForTree( + @RequestParam(required = false) String query, + @RequestParam(required = false) Integer parent + ){ + if (ObjectUtils.isEmpty(query)) { + query = null; + } + return groupService.queryForTree(query, parent); + } + + @Operation(summary = "更新分组") + @Parameter(name = "group", description = "Group", required = true) + @ResponseBody + @PostMapping("/update") + public void update(@RequestBody Group group){ + groupService.update(group); + } + + @Operation(summary = "删除分组") + @Parameter(name = "id", description = "分组id", required = true) + @ResponseBody + @DeleteMapping("/delete") + public void delete(Integer id){ + Assert.notNull(id, "分组id(deviceId)不需要存在"); + boolean result = groupService.delete(id); + if (!result) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "移除失败"); + } + } + +// @Operation(summary = "根据分组Id查询分组") +// @Parameter(name = "groupDeviceId", description = "分组节点编号", required = true) +// @ResponseBody +// @GetMapping("/one") +// public Group queryGroupByDeviceId( +// @RequestParam(required = true) String deviceId +// ){ +// Assert.hasLength(deviceId, ""); +// return groupService.queryGroupByDeviceId(deviceId); +// } + +// @Operation(summary = "从通道中同步分组") +// @ResponseBody +// @GetMapping("/sync") +// public void sync(){ +// groupService.syncFromChannel(); +// } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/MediaController.java similarity index 70% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/controller/MediaController.java index 26e96857c..545302956 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/MediaController.java @@ -1,4 +1,4 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.media; +package com.genersoft.iot.vmp.gb28181.controller; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.exception.ControllerException; @@ -7,32 +7,31 @@ import com.genersoft.iot.vmp.conf.security.SecurityUtils; import com.genersoft.iot.vmp.conf.security.dto.LoginUser; import com.genersoft.iot.vmp.media.service.IMediaServerService; import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; -import com.genersoft.iot.vmp.service.IMediaService; -import com.genersoft.iot.vmp.service.IStreamProxyService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.streamProxy.service.IStreamProxyService; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.StreamContent; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; @Tag(name = "媒体流相关") @Controller - +@Slf4j @RequestMapping(value = "/api/media") public class MediaController { - private final static Logger logger = LoggerFactory.getLogger(MediaController.class); - @Autowired private IRedisCatchStorage redisCatchStorage; @@ -86,7 +85,7 @@ public class MediaController { if (useSourceIpAsStreamIp != null && useSourceIpAsStreamIp) { String host = request.getHeader("Host"); String localAddr = host.split(":")[0]; - logger.info("使用{}作为返回流的ip", localAddr); + log.info("使用{}作为返回流的ip", localAddr); streamInfo = mediaServerService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, localAddr, authority); }else { streamInfo = mediaServerService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority); @@ -96,17 +95,17 @@ public class MediaController { return new StreamContent(streamInfo); }else { //获取流失败,重启拉流后重试一次 - streamProxyService.stop(app,stream); - boolean start = streamProxyService.start(app, stream); + streamProxyService.stopByAppAndStream(app,stream); + boolean start = streamProxyService.startByAppAndStream(app, stream); try { Thread.sleep(1000); } catch (InterruptedException e) { - logger.error("[线程休眠失败], {}", e.getMessage()); + log.error("[线程休眠失败], {}", e.getMessage()); } if (useSourceIpAsStreamIp != null && useSourceIpAsStreamIp) { String host = request.getHeader("Host"); String localAddr = host.split(":")[0]; - logger.info("使用{}作为返回流的ip", localAddr); + log.info("使用{}作为返回流的ip", localAddr); streamInfo = mediaServerService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, localAddr, authority); }else { streamInfo = mediaServerService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority); @@ -118,4 +117,30 @@ public class MediaController { } } } + /** + * 获取推流播放地址 + * @param app 应用名 + * @param stream 流id + * @return + */ + @GetMapping(value = "/getPlayUrl") + @ResponseBody + @Operation(summary = "获取推流播放地址", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "app", description = "应用名", required = true) + @Parameter(name = "stream", description = "流id", required = true) + @Parameter(name = "mediaServerId", description = "媒体服务器id") + public StreamContent getPlayUrl(@RequestParam String app, @RequestParam String stream, + @RequestParam(required = false) String mediaServerId){ + boolean authority = false; + // 是否登陆用户, 登陆用户返回完整信息 + LoginUser userInfo = SecurityUtils.getUserInfo(); + if (userInfo!= null) { + authority = true; + } + StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority); + if (streamInfo == null){ + throw new ControllerException(ErrorCode.ERROR100.getCode(), "获取播放地址失败"); + } + return new StreamContent(streamInfo); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/MobilePositionController.java similarity index 76% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/controller/MobilePositionController.java index 05501cc44..1c3406f85 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/MobilePositionController.java @@ -1,23 +1,21 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.MobilePosition; +package com.genersoft.iot.vmp.gb28181.controller; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.security.JwtUtils; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; 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.IDeviceChannelService; -import com.genersoft.iot.vmp.service.IDeviceService; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.service.IMobilePositionService; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.github.pagehelper.util.StringUtil; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; @@ -32,15 +30,13 @@ import java.util.UUID; * 位置信息管理 */ @Tag(name = "位置信息管理") - +@Slf4j @RestController @RequestMapping("/api/position") public class MobilePositionController { - private final static Logger logger = LoggerFactory.getLogger(MobilePositionController.class); - @Autowired - private IVideoManagerStorage storager; + private IMobilePositionService mobilePositionService; @Autowired private SIPCommander cmder; @@ -51,8 +47,6 @@ public class MobilePositionController { @Autowired private IDeviceService deviceService; - @Autowired - private IDeviceChannelService deviceChannelService; /** * 查询历史轨迹 @@ -78,7 +72,7 @@ public class MobilePositionController { if (StringUtil.isEmpty(end)) { end = null; } - return storager.queryMobilePositions(deviceId, channelId, start, end); + return mobilePositionService.queryMobilePositions(deviceId, channelId, start, end); } /** @@ -90,7 +84,7 @@ public class MobilePositionController { @Parameter(name = "deviceId", description = "设备国标编号", required = true) @GetMapping("/latest/{deviceId}") public MobilePosition latestPosition(@PathVariable String deviceId) { - return storager.queryLatestPosition(deviceId); + return mobilePositionService.queryLatestPosition(deviceId); } /** @@ -102,7 +96,7 @@ public class MobilePositionController { @Parameter(name = "deviceId", description = "设备国标编号", required = true) @GetMapping("/realtime/{deviceId}") public DeferredResult realTimePosition(@PathVariable String deviceId) { - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); String uuid = UUID.randomUUID().toString(); String key = DeferredResultHolder.CALLBACK_CMD_MOBILE_POSITION + deviceId; try { @@ -114,12 +108,12 @@ public class MobilePositionController { resultHolder.invokeResult(msg); }); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 获取移动位置信息: {}", e.getMessage()); + log.error("[命令发送失败] 获取移动位置信息: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } DeferredResult result = new DeferredResult(5*1000L); result.onTimeout(()->{ - logger.warn(String.format("获取移动位置信息超时")); + log.warn(String.format("获取移动位置信息超时")); // 释放rtpserver RequestMessage msg = new RequestMessage(); msg.setId(uuid); @@ -150,29 +144,9 @@ public class MobilePositionController { if (StringUtil.isEmpty(interval)) { interval = "5"; } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); device.setSubscribeCycleForMobilePosition(Integer.parseInt(expires)); device.setMobilePositionSubmissionInterval(Integer.parseInt(interval)); deviceService.updateCustomDevice(device); } - - /** - * 数据位置信息格式处理 - * @param deviceId 设备ID - * @return true = 命令发送成功 - */ - @Operation(summary = "数据位置信息格式处理", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "deviceId", description = "设备国标编号", required = true) - @GetMapping("/transform/{deviceId}") - public void positionTransform(@PathVariable String deviceId) { - - Device device = deviceService.getDevice(deviceId); - if (device == null) { - throw new ControllerException(ErrorCode.ERROR400.getCode(), "未找到设备: " + deviceId); - } - boolean result = deviceChannelService.updateAllGps(device); - if (!result) { - throw new ControllerException(ErrorCode.ERROR100); - } - } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlatformController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlatformController.java new file mode 100755 index 000000000..79e6b3d07 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlatformController.java @@ -0,0 +1,293 @@ +package com.genersoft.iot.vmp.gb28181.controller; + +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.conf.security.JwtUtils; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.bean.PlatformChannel; +import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder; +import com.genersoft.iot.vmp.gb28181.controller.bean.UpdateChannelParam; +import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService; +import com.genersoft.iot.vmp.gb28181.service.IPlatformService; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +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; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; + +/** + * 级联平台管理 + */ +@Tag(name = "级联平台管理") +@Slf4j +@RestController +@RequestMapping("/api/platform") +public class PlatformController { + + @Autowired + private IPlatformChannelService platformChannelService; + + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private SipConfig sipConfig; + + @Autowired + private IPlatformService platformService; + + + @Operation(summary = "获取国标服务的配置", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @GetMapping("/server_config") + public JSONObject serverConfig() { + JSONObject result = new JSONObject(); + result.put("deviceIp", sipConfig.getShowIp()); + result.put("devicePort", sipConfig.getPort()); + result.put("username", sipConfig.getId()); + result.put("password", sipConfig.getPassword()); + return result; + } + + @Operation(summary = "获取级联服务器信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "id", description = "平台国标编号", required = true) + @GetMapping("/info/{id}") + public Platform getPlatform(@PathVariable String id) { + Platform parentPlatform = platformService.queryPlatformByServerGBId(id); + if (parentPlatform != null) { + return parentPlatform; + } else { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未查询到此平台"); + } + } + + @GetMapping("/query") + @Operation(summary = "分页查询级联平台", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "page", description = "当前页") + @Parameter(name = "count", description = "每页查询数量") + @Parameter(name = "query", description = "查询内容") + public PageInfo platforms(int page, int count, + @RequestParam(required = false) String query) { + + PageInfo parentPlatformPageInfo = platformService.queryPlatformList(page, count, query); + if (parentPlatformPageInfo != null && !parentPlatformPageInfo.getList().isEmpty()) { + for (Platform platform : parentPlatformPageInfo.getList()) { + platform.setMobilePositionSubscribe(subscribeHolder.getMobilePositionSubscribe(platform.getServerGBId()) != null); + platform.setCatalogSubscribe(subscribeHolder.getCatalogSubscribe(platform.getServerGBId()) != null); + } + } + return parentPlatformPageInfo; + } + + @Operation(summary = "添加上级平台信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/add") + @ResponseBody + public void add(@RequestBody Platform platform) { + + if (log.isDebugEnabled()) { + log.debug("保存上级平台信息API调用"); + } + Assert.notNull(platform.getName(), "平台名称不可为空"); + Assert.notNull(platform.getServerGBId(), "上级平台国标编号不可为空"); + Assert.notNull(platform.getServerIp(), "上级平台IP不可为空"); + Assert.isTrue(platform.getServerPort() > 0 && platform.getServerPort() < 65535, "上级平台端口异常"); + Assert.notNull(platform.getDeviceGBId(), "本平台国标编号不可为空"); + + if (ObjectUtils.isEmpty(platform.getServerGBDomain())) { + platform.setServerGBDomain(platform.getServerGBId().substring(0, 6)); + } + + if (platform.getExpires() <= 0) { + platform.setExpires(3600); + } + + if (platform.getKeepTimeout() <= 0) { + platform.setKeepTimeout(60); + } + + if (ObjectUtils.isEmpty(platform.getTransport())) { + platform.setTransport("UDP"); + } + + if (ObjectUtils.isEmpty(platform.getCharacterSet())) { + platform.setCharacterSet("GB2312"); + } + + Platform parentPlatformOld = platformService.queryPlatformByServerGBId(platform.getServerGBId()); + if (parentPlatformOld != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台 " + platform.getServerGBId() + " 已存在"); + } + platform.setCreateTime(DateUtil.getNow()); + platform.setUpdateTime(DateUtil.getNow()); + boolean updateResult = platformService.add(platform); + + if (!updateResult) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @Operation(summary = "更新上级平台信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/update") + @ResponseBody + public void updatePlatform(@RequestBody Platform parentPlatform) { + + if (log.isDebugEnabled()) { + log.debug("保存上级平台信息API调用"); + } + if (ObjectUtils.isEmpty(parentPlatform.getName()) + || ObjectUtils.isEmpty(parentPlatform.getServerGBId()) + || ObjectUtils.isEmpty(parentPlatform.getServerGBDomain()) + || ObjectUtils.isEmpty(parentPlatform.getServerIp()) + || ObjectUtils.isEmpty(parentPlatform.getServerPort()) + || ObjectUtils.isEmpty(parentPlatform.getDeviceGBId()) + || ObjectUtils.isEmpty(parentPlatform.getExpires()) + || ObjectUtils.isEmpty(parentPlatform.getKeepTimeout()) + || ObjectUtils.isEmpty(parentPlatform.getTransport()) + || ObjectUtils.isEmpty(parentPlatform.getCharacterSet()) + ) { + throw new ControllerException(ErrorCode.ERROR400); + } + platformService.update(parentPlatform); + } + + @Operation(summary = "删除上级平台", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "id", description = "上级平台ID") + @DeleteMapping("/delete") + @ResponseBody + public DeferredResult deletePlatform(Integer id) { + + if (log.isDebugEnabled()) { + log.debug("删除上级平台API调用"); + } + DeferredResult deferredResult = new DeferredResult<>(); + + platformService.delete(id, (object)->{ + deferredResult.setResult(WVPResult.success()); + }); + return deferredResult; + } + + @Operation(summary = "查询上级平台是否存在", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "serverGBId", description = "上级平台的国标编号") + @GetMapping("/exit/{serverGBId}") + @ResponseBody + public Boolean exitPlatform(@PathVariable String serverGBId) { + Platform platform = platformService.queryPlatformByServerGBId(serverGBId); + return platform != null; + } + + @Operation(summary = "分页查询级联平台的所有所有通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页条数", required = true) + @Parameter(name = "platformId", description = "上级平台的数据ID") + @Parameter(name = "channelType", description = "通道类型, 0:国标设备,1:推流设备,2:拉流代理") + @Parameter(name = "query", description = "查询内容") + @Parameter(name = "online", description = "是否在线") + @Parameter(name = "hasShare", description = "是否已经共享") + @GetMapping("/channel/list") + @ResponseBody + public PageInfo queryChannelList(int page, int count, + @RequestParam(required = false) Integer platformId, + @RequestParam(required = false) String query, + @RequestParam(required = false) Integer channelType, + @RequestParam(required = false) Boolean online, + @RequestParam(required = false) Boolean hasShare) { + + Assert.notNull(platformId, "上级平台的数据ID不可为NULL"); + if (ObjectUtils.isEmpty(query)) { + query = null; + } + + return platformChannelService.queryChannelList(page, count, query, channelType, online, platformId, hasShare); + } + + @Operation(summary = "向上级平台添加国标通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/channel/add") + @ResponseBody + public void addChannel(@RequestBody UpdateChannelParam param) { + + if (log.isDebugEnabled()) { + log.debug("给上级平台添加国标通道API调用"); + } + int result = 0; + if (param.getChannelIds() == null || param.getChannelIds().isEmpty()) { + if (param.isAll()) { + log.info("[国标级联]添加所有通道到上级平台, {}", param.getPlatformId()); + result = platformChannelService.addAllChannel(param.getPlatformId()); + } + }else { + result = platformChannelService.addChannels(param.getPlatformId(), param.getChannelIds()); + } + if (result <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @Operation(summary = "从上级平台移除国标通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @DeleteMapping("/channel/remove") + @ResponseBody + public void delChannelForGB(@RequestBody UpdateChannelParam param) { + + if (log.isDebugEnabled()) { + log.debug("给上级平台删除国标通道API调用"); + } + int result = 0; + if (param.getChannelIds() == null || param.getChannelIds().isEmpty()) { + if (param.isAll()) { + log.info("[国标级联]移除所有通道,上级平台, {}", param.getPlatformId()); + result = platformChannelService.removeAllChannel(param.getPlatformId()); + } + }else { + result = platformChannelService.removeChannels(param.getPlatformId(), param.getChannelIds()); + } + if (result <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @Operation(summary = "推送通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "id", description = "平台ID", required = true) + @GetMapping("/channel/push") + @ResponseBody + public void pushChannel(Integer id) { + Assert.notNull(id, "平台ID不可为空"); + platformChannelService.pushChannel(id); + } + + @Operation(summary = "添加通道-通过设备", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/channel/device/add") + @ResponseBody + public void addChannelByDevice(@RequestBody UpdateChannelParam param) { + Assert.notNull(param.getPlatformId(), "平台ID不可为空"); + Assert.notNull(param.getDeviceIds(), "设备ID不可为空"); + Assert.notEmpty(param.getDeviceIds(), "设备ID不可为空"); + platformChannelService.addChannelByDevice(param.getPlatformId(), param.getDeviceIds()); + } + + @Operation(summary = "移除通道-通过设备", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/channel/device/remove") + @ResponseBody + public void removeChannelByDevice(@RequestBody UpdateChannelParam param) { + Assert.notNull(param.getPlatformId(), "平台ID不可为空"); + Assert.notNull(param.getDeviceIds(), "设备ID不可为空"); + Assert.notEmpty(param.getDeviceIds(), "设备ID不可为空"); + platformChannelService.removeChannelByDevice(param.getPlatformId(), param.getDeviceIds()); + } + + @Operation(summary = "自定义共享通道信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @PostMapping("/channel/custom/update") + @ResponseBody + public void updateCustomChannel(@RequestBody PlatformChannel channel) { + Assert.isTrue(channel.getId() > 0, "共享通道ID必须存在"); + platformChannelService.updateCustomChannel(channel); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlayController.java similarity index 77% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlayController.java index 780f53697..8c53ca629 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlayController.java @@ -1,4 +1,4 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.play; +package com.genersoft.iot.vmp.gb28181.controller; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; @@ -8,17 +8,18 @@ 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.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; +import com.genersoft.iot.vmp.gb28181.service.IPlayService; +import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; 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.media.bean.MediaServer; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.service.IInviteStreamService; -import com.genersoft.iot.vmp.service.IPlayService; import com.genersoft.iot.vmp.service.bean.InviteErrorCode; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; @@ -28,9 +29,9 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; @@ -46,21 +47,13 @@ import java.util.UUID; * @author lin */ @Tag(name = "国标设备点播") - +@Slf4j @RestController @RequestMapping("/api/play") public class PlayController { - private final static Logger logger = LoggerFactory.getLogger(PlayController.class); - @Autowired - private SIPCommander cmder; - - @Autowired - private VideoStreamSessionManager streamSession; - - @Autowired - private IVideoManagerStorage storager; + private SipInviteSessionManager sessionManager; @Autowired private IInviteStreamService inviteStreamService; @@ -77,6 +70,12 @@ public class PlayController { @Autowired private UserSetting userSetting; + @Autowired + private IDeviceService deviceService; + + @Autowired + private IDeviceChannelService deviceChannelService; + @Operation(summary = "开始点播", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Parameter(name = "deviceId", description = "设备国标编号", required = true) @Parameter(name = "channelId", description = "通道国标编号", required = true) @@ -84,9 +83,14 @@ public class PlayController { public DeferredResult> play(HttpServletRequest request, @PathVariable String deviceId, @PathVariable String channelId) { - logger.info("[开始点播] deviceId:{}, channelId:{}, ", deviceId, channelId); + log.info("[开始点播] deviceId:{}, channelId:{}, ", deviceId, channelId); + Assert.notNull(deviceId, "设备国标编号不可为NULL"); + Assert.notNull(channelId, "通道国标编号不可为NULL"); // 获取可用的zlm - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); + Assert.notNull(deviceId, "设备不存在"); + DeviceChannel channel = deviceChannelService.getOne(deviceId, channelId); + Assert.notNull(channel, "通道不存在"); MediaServer newMediaServerItem = playService.getNewMediaServerItem(device); RequestMessage requestMessage = new RequestMessage(); @@ -97,28 +101,27 @@ public class PlayController { DeferredResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); result.onTimeout(()->{ - logger.info("[点播等待超时] deviceId:{}, channelId:{}, ", deviceId, channelId); + log.info("[点播等待超时] deviceId:{}, channelId:{}, ", deviceId, channelId); // 释放rtpserver WVPResult wvpResult = new WVPResult<>(); wvpResult.setCode(ErrorCode.ERROR100.getCode()); wvpResult.setMsg("点播超时"); requestMessage.setData(wvpResult); resultHolder.invokeAllResult(requestMessage); - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); - storager.stopPlay(deviceId, channelId); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); + deviceChannelService.stopPlay(channel.getId()); }); // 录像查询以channelId作为deviceId查询 resultHolder.put(key, uuid, result); - playService.play(newMediaServerItem, deviceId, channelId, null, (code, msg, data) -> { + playService.play(newMediaServerItem, deviceId, channelId, null, (code, msg, streamInfo) -> { WVPResult wvpResult = new WVPResult<>(); if (code == InviteErrorCode.SUCCESS.getCode()) { wvpResult.setCode(ErrorCode.SUCCESS.getCode()); wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); - if (data != null) { - StreamInfo streamInfo = (StreamInfo)data; + if (streamInfo != null) { if (userSetting.getUseSourceIpAsStreamIp()) { streamInfo=streamInfo.clone();//深拷贝 String host; @@ -155,18 +158,18 @@ public class PlayController { @GetMapping("/stop/{deviceId}/{channelId}") public JSONObject playStop(@PathVariable String deviceId, @PathVariable String channelId) { - logger.debug(String.format("设备预览/回放停止API调用,streamId:%s_%s", deviceId, channelId )); + log.debug(String.format("设备预览/回放停止API调用,streamId:%s_%s", deviceId, channelId )); if (deviceId == null || channelId == null) { throw new ControllerException(ErrorCode.ERROR400); } - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备[" + deviceId + "]不存在"); - } - - playService.stopPlay(device, channelId); + Device device = deviceService.getDeviceByDeviceId(deviceId); + DeviceChannel channel = deviceChannelService.getOneForSource(deviceId, channelId); + Assert.notNull(device, "设备不存在"); + Assert.notNull(channel, "通道不存在"); + String streamId = String.format("%s_%s", device.getDeviceId(), channel.getDeviceId()); + playService.stop(InviteSessionType.PLAY, device, channel, streamId); JSONObject json = new JSONObject(); json.put("deviceId", deviceId); json.put("channelId", channelId); @@ -201,18 +204,19 @@ public class PlayController { @GetMapping("/broadcast/{deviceId}/{channelId}") @PostMapping("/broadcast/{deviceId}/{channelId}") public AudioBroadcastResult broadcastApi(@PathVariable String deviceId, @PathVariable String channelId, Integer timeout, Boolean broadcastMode) { - if (logger.isDebugEnabled()) { - logger.debug("语音广播API调用"); + if (log.isDebugEnabled()) { + log.debug("语音广播API调用"); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); if (device == null) { throw new ControllerException(ErrorCode.ERROR400.getCode(), "未找到设备: " + deviceId); } - if (channelId == null) { + DeviceChannel channel = deviceChannelService.getOneForSource(device.getId(), channelId); + if (channel == null) { throw new ControllerException(ErrorCode.ERROR400.getCode(), "未找到通道: " + channelId); } - return playService.audioBroadcast(device, channelId, broadcastMode); + return playService.audioBroadcast(device, channel, broadcastMode); } @@ -222,26 +226,24 @@ public class PlayController { @GetMapping("/broadcast/stop/{deviceId}/{channelId}") @PostMapping("/broadcast/stop/{deviceId}/{channelId}") public void stopBroadcast(@PathVariable String deviceId, @PathVariable String channelId) { - if (logger.isDebugEnabled()) { - logger.debug("停止语音广播API调用"); + if (log.isDebugEnabled()) { + log.debug("停止语音广播API调用"); } -// try { -// playService.stopAudioBroadcast(deviceId, channelId); -// } catch (InvalidArgumentException | ParseException | SipException e) { -// logger.error("[命令发送失败] 停止语音: {}", e.getMessage()); -// throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); -// } - playService.stopAudioBroadcast(deviceId, channelId); + Device device = deviceService.getDeviceByDeviceId(deviceId); + Assert.notNull(device, "设备不存在"); + DeviceChannel channel = deviceChannelService.getOne(deviceId, channelId); + Assert.notNull(channel, "通道不存在"); + playService.stopAudioBroadcast(device, channel); } @Operation(summary = "获取所有的ssrc", security = @SecurityRequirement(name = JwtUtils.HEADER)) @GetMapping("/ssrc") public JSONObject getSSRC() { - if (logger.isDebugEnabled()) { - logger.debug("获取所有的ssrc"); + if (log.isDebugEnabled()) { + log.debug("获取所有的ssrc"); } JSONArray objects = new JSONArray(); - List allSsrc = streamSession.getAllSsrc(); + List allSsrc = sessionManager.getAll(); for (SsrcTransaction transaction : allSsrc) { JSONObject jsonObject = new JSONObject(); jsonObject.put("deviceId", transaction.getDeviceId()); @@ -260,11 +262,10 @@ public class PlayController { @Operation(summary = "获取截图", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Parameter(name = "deviceId", description = "设备国标编号", required = true) @Parameter(name = "channelId", description = "通道国标编号", required = true) - @Parameter(name = "isSubStream", description = "是否子码流(true-子码流,false-主码流),默认为false", required = true) @GetMapping("/snap") - public DeferredResult getSnap(String deviceId, String channelId,boolean isSubStream) { - if (logger.isDebugEnabled()) { - logger.debug("获取截图: {}/{}", deviceId, channelId); + public DeferredResult getSnap(String deviceId, String channelId) { + if (log.isDebugEnabled()) { + log.debug("获取截图: {}/{}", deviceId, channelId); } DeferredResult result = new DeferredResult<>(3 * 1000L); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlaybackController.java similarity index 77% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlaybackController.java index 44431cf21..210b022f7 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlaybackController.java @@ -1,4 +1,4 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.playback; +package com.genersoft.iot.vmp.gb28181.controller; import com.genersoft.iot.vmp.common.InviteInfo; import com.genersoft.iot.vmp.common.InviteSessionType; @@ -6,16 +6,17 @@ import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.exception.ServiceException; -import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.conf.security.JwtUtils; import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; +import com.genersoft.iot.vmp.gb28181.service.IPlayService; 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.IInviteStreamService; -import com.genersoft.iot.vmp.service.IPlayService; import com.genersoft.iot.vmp.service.bean.InviteErrorCode; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.StreamContent; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; @@ -23,8 +24,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.GetMapping; @@ -45,19 +45,14 @@ import java.util.UUID; * @author lin */ @Tag(name = "视频回放") - +@Slf4j @RestController @RequestMapping("/api/playback") public class PlaybackController { - private final static Logger logger = LoggerFactory.getLogger(PlaybackController.class); - @Autowired private SIPCommander cmder; - @Autowired - private IVideoManagerStorage storager; - @Autowired private IInviteStreamService inviteStreamService; @@ -70,6 +65,12 @@ public class PlaybackController { @Autowired private UserSetting userSetting; + @Autowired + private IDeviceService deviceService; + + @Autowired + private IDeviceChannelService channelService; + @Operation(summary = "开始视频回放", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Parameter(name = "deviceId", description = "设备国标编号", required = true) @Parameter(name = "channelId", description = "通道国标编号", required = true) @@ -79,8 +80,8 @@ public class PlaybackController { public DeferredResult> start(HttpServletRequest request, @PathVariable String deviceId, @PathVariable String channelId, String startTime, String endTime) { - if (logger.isDebugEnabled()) { - logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); + if (log.isDebugEnabled()) { + log.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); } String uuid = UUID.randomUUID().toString(); @@ -91,8 +92,18 @@ public class PlaybackController { RequestMessage requestMessage = new RequestMessage(); requestMessage.setKey(key); requestMessage.setId(uuid); + Device device = deviceService.getDeviceByDeviceId(deviceId); + if (device == null) { + log.warn("[录像回放] 未找到设备 deviceId: {},channelId:{}", deviceId, channelId); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到设备:" + deviceId); + } - playService.playBack(deviceId, channelId, startTime, endTime, + DeviceChannel channel = channelService.getOne(deviceId, channelId); + if (channel == null) { + log.warn("[录像回放] 未找到通道 deviceId: {},channelId:{}", deviceId, channelId); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到通道:" + channelId); + } + playService.playBack(device, channel, startTime, endTime, (code, msg, data)->{ WVPResult wvpResult = new WVPResult<>(); @@ -139,15 +150,15 @@ public class PlaybackController { if (ObjectUtils.isEmpty(deviceId) || ObjectUtils.isEmpty(channelId) || ObjectUtils.isEmpty(stream)) { throw new ControllerException(ErrorCode.ERROR400); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); if (device == null) { throw new ControllerException(ErrorCode.ERROR400.getCode(), "设备:" + deviceId + " 未找到"); } - try { - cmder.streamByeCmd(device, channelId, stream, null); - } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "发送bye失败: " + e.getMessage()); + DeviceChannel deviceChannel = channelService.getOneForSource(deviceId, channelId); + if (deviceChannel == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "通道:" + deviceChannel + " 未找到"); } + playService.stop(InviteSessionType.PLAYBACK, device, deviceChannel, stream); } @@ -155,7 +166,7 @@ public class PlaybackController { @Parameter(name = "streamId", description = "回放流ID", required = true) @GetMapping("/pause/{streamId}") public void playPause(@PathVariable String streamId) { - logger.info("playPause: "+streamId); + log.info("playPause: "+streamId); try { playService.pauseRtp(streamId); @@ -171,7 +182,7 @@ public class PlaybackController { @Parameter(name = "streamId", description = "回放流ID", required = true) @GetMapping("/resume/{streamId}") public void playResume(@PathVariable String streamId) { - logger.info("playResume: "+streamId); + log.info("playResume: "+streamId); try { playService.resumeRtp(streamId); } catch (ServiceException e) { @@ -187,16 +198,17 @@ public class PlaybackController { @Parameter(name = "seekTime", description = "拖动偏移量,单位s", required = true) @GetMapping("/seek/{streamId}/{seekTime}") public void playSeek(@PathVariable String streamId, @PathVariable long seekTime) { - logger.info("playSeek: "+streamId+", "+seekTime); + log.info("playSeek: "+streamId+", "+seekTime); InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); if (null == inviteInfo || inviteInfo.getStreamInfo() == null) { - logger.warn("streamId不存在!"); + log.warn("streamId不存在!"); throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在"); } - Device device = storager.queryVideoDevice(inviteInfo.getDeviceId()); + Device device = deviceService.getDeviceByDeviceId(inviteInfo.getDeviceId()); + DeviceChannel channel = channelService.getOneById(inviteInfo.getChannelId()); try { - cmder.playSeekCmd(device, inviteInfo.getStreamInfo(), seekTime); + cmder.playSeekCmd(device, channel, inviteInfo.getStreamInfo(), seekTime); } catch (InvalidArgumentException | ParseException | SipException e) { throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); } @@ -207,20 +219,21 @@ public class PlaybackController { @Parameter(name = "speed", description = "倍速0.25 0.5 1、2、4", required = true) @GetMapping("/speed/{streamId}/{speed}") public void playSpeed(@PathVariable String streamId, @PathVariable Double speed) { - logger.info("playSpeed: "+streamId+", "+speed); + log.info("playSpeed: "+streamId+", "+speed); InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); if (null == inviteInfo || inviteInfo.getStreamInfo() == null) { - logger.warn("streamId不存在!"); + log.warn("streamId不存在!"); throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在"); } if(speed != 0.25 && speed != 0.5 && speed != 1 && speed != 2.0 && speed != 4.0) { - logger.warn("不支持的speed: " + speed); + log.warn("不支持的speed: " + speed); throw new ControllerException(ErrorCode.ERROR100.getCode(), "不支持的speed(0.25 0.5 1、2、4)"); } - Device device = storager.queryVideoDevice(inviteInfo.getDeviceId()); + Device device = deviceService.getDeviceByDeviceId(inviteInfo.getDeviceId()); + DeviceChannel channel = channelService.getOneById(inviteInfo.getChannelId()); try { - cmder.playSpeedCmd(device, inviteInfo.getStreamInfo(), speed); + cmder.playSpeedCmd(device, channel, inviteInfo.getStreamInfo(), speed); } catch (InvalidArgumentException | ParseException | SipException e) { throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PtzController.java similarity index 81% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/controller/PtzController.java index 36047f0a1..d2773918a 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PtzController.java @@ -1,20 +1,19 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.ptz; +package com.genersoft.iot.vmp.gb28181.controller; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.security.JwtUtils; import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; 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.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; @@ -26,18 +25,16 @@ import java.text.ParseException; import java.util.UUID; @Tag(name = "云台控制") - +@Slf4j @RestController @RequestMapping("/api/ptz") public class PtzController { - private final static Logger logger = LoggerFactory.getLogger(PtzController.class); - @Autowired private SIPCommander cmder; @Autowired - private IVideoManagerStorage storager; + private IDeviceService deviceService; @Autowired private DeferredResultHolder resultHolder; @@ -62,10 +59,10 @@ public class PtzController { @PostMapping("/control/{deviceId}/{channelId}") public void ptz(@PathVariable String deviceId,@PathVariable String channelId, String command, int horizonSpeed, int verticalSpeed, int zoomSpeed){ - if (logger.isDebugEnabled()) { - logger.debug(String.format("设备云台控制 API调用,deviceId:%s ,channelId:%s ,command:%s ,horizonSpeed:%d ,verticalSpeed:%d ,zoomSpeed:%d",deviceId, channelId, command, horizonSpeed, verticalSpeed, zoomSpeed)); + if (log.isDebugEnabled()) { + log.debug(String.format("设备云台控制 API调用,deviceId:%s ,channelId:%s ,command:%s ,horizonSpeed:%d ,verticalSpeed:%d ,zoomSpeed:%d",deviceId, channelId, command, horizonSpeed, verticalSpeed, zoomSpeed)); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); int cmdCode = 0; switch (command){ case "left": @@ -109,7 +106,7 @@ public class PtzController { try { cmder.frontEndCmd(device, channelId, cmdCode, horizonSpeed, verticalSpeed, zoomSpeed); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 云台控制: {}", e.getMessage()); + log.error("[命令发送失败] 云台控制: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } } @@ -125,15 +122,15 @@ public class PtzController { @PostMapping("/front_end_command/{deviceId}/{channelId}") public void frontEndCommand(@PathVariable String deviceId,@PathVariable String channelId,int cmdCode, int parameter1, int parameter2, int combindCode2){ - if (logger.isDebugEnabled()) { - logger.debug(String.format("设备云台控制 API调用,deviceId:%s ,channelId:%s ,cmdCode:%d parameter1:%d parameter2:%d",deviceId, channelId, cmdCode, parameter1, parameter2)); + if (log.isDebugEnabled()) { + log.debug(String.format("设备云台控制 API调用,deviceId:%s ,channelId:%s ,cmdCode:%d parameter1:%d parameter2:%d",deviceId, channelId, cmdCode, parameter1, parameter2)); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); try { cmder.frontEndCmd(device, channelId, cmdCode, parameter1, parameter2, combindCode2); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 前端控制: {}", e.getMessage()); + log.error("[命令发送失败] 前端控制: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } } @@ -144,15 +141,15 @@ public class PtzController { @Parameter(name = "channelId", description = "通道国标编号", required = true) @GetMapping("/preset/query/{deviceId}/{channelId}") public DeferredResult presetQueryApi(@PathVariable String deviceId, @PathVariable String channelId) { - if (logger.isDebugEnabled()) { - logger.debug("设备预置位查询API调用"); + if (log.isDebugEnabled()) { + log.debug("设备预置位查询API调用"); } - Device device = storager.queryVideoDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); String uuid = UUID.randomUUID().toString(); String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId); DeferredResult result = new DeferredResult (3 * 1000L); result.onTimeout(()->{ - logger.warn(String.format("获取设备预置位超时")); + log.warn(String.format("获取设备预置位超时")); // 释放rtpserver RequestMessage msg = new RequestMessage(); msg.setId(uuid); @@ -173,7 +170,7 @@ public class PtzController { resultHolder.invokeResult(msg); }); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 获取设备预置位: {}", e.getMessage()); + log.error("[命令发送失败] 获取设备预置位: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } return result; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/RegionController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/RegionController.java new file mode 100644 index 000000000..7da244893 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/RegionController.java @@ -0,0 +1,118 @@ +package com.genersoft.iot.vmp.gb28181.controller; + +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.gb28181.bean.Region; +import com.genersoft.iot.vmp.gb28181.bean.RegionTree; +import com.genersoft.iot.vmp.gb28181.service.IRegionService; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +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.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Tag(name = "区域管理") +@RestController +@RequestMapping("/api/region") +public class RegionController { + + private final static Logger logger = LoggerFactory.getLogger(RegionController.class); + + @Autowired + private IRegionService regionService; + + @Operation(summary = "添加区域") + @Parameter(name = "region", description = "Region", required = true) + @ResponseBody + @PostMapping("/add") + public void add(@RequestBody Region region){ + regionService.add(region); + } + + @Operation(summary = "查询区域") + @Parameter(name = "query", description = "要搜索的内容", required = true) + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页查询数量", required = true) + @ResponseBody + @GetMapping("/page/list") + public PageInfo query( + @RequestParam(required = false) String query, + @RequestParam(required = true) int page, + @RequestParam(required = true) int count + ){ + return regionService.query(query, page, count); + } + + @Operation(summary = "查询区域") + @Parameter(name = "query", description = "要搜索的内容", required = true) + @Parameter(name = "parent", description = "所属行政区划编号", required = true) + @ResponseBody + @GetMapping("/tree/list") + public List queryForTree( + @RequestParam(required = false) String query, + @RequestParam(required = false) Integer parent + ){ + if (ObjectUtils.isEmpty(query)) { + query = null; + } + return regionService.queryForTree(query, parent); + } + + @Operation(summary = "更新区域") + @Parameter(name = "region", description = "Region", required = true) + @ResponseBody + @PostMapping("/update") + public void update(@RequestBody Region region){ + regionService.update(region); + } + + @Operation(summary = "删除区域") + @Parameter(name = "id", description = "区域ID", required = true) + @ResponseBody + @DeleteMapping("/delete") + public void delete(Integer id){ + Assert.notNull(id, "区域ID需要存在"); + boolean result = regionService.deleteByDeviceId(id); + if (!result) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "移除失败"); + } + } + + @Operation(summary = "根据区域Id查询区域") + @Parameter(name = "regionDeviceId", description = "行政区划节点编号", required = true) + @ResponseBody + @GetMapping("/one") + public Region queryRegionByDeviceId( + @RequestParam(required = true) String regionDeviceId + ){ + if (ObjectUtils.isEmpty(regionDeviceId.trim())) { + throw new ControllerException(ErrorCode.ERROR400); + } + return regionService.queryRegionByDeviceId(regionDeviceId); + } + + @Operation(summary = "获取所属的行政区划下的行政区划") + @Parameter(name = "parent", description = "所属的行政区划", required = false) + @ResponseBody + @GetMapping("/base/child/list") + public List getAllChild(@RequestParam(required = false) String parent){ + if (ObjectUtils.isEmpty(parent)) { + parent = null; + } + return regionService.getAllChild(parent); + } + + @Operation(summary = "从通道中同步行政区划") + @ResponseBody + @GetMapping("/sync") + public void sync(){ + regionService.syncFromChannel(); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/sse/SseController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/SseController.java similarity index 96% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/sse/SseController.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/controller/SseController.java index 575f22b36..2ca1a4dba 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/sse/SseController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/SseController.java @@ -1,4 +1,4 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.sse; +package com.genersoft.iot.vmp.gb28181.controller; import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEventListener; import io.swagger.v3.oas.annotations.tags.Tag; diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/bean/AudioBroadcastEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/AudioBroadcastEvent.java similarity index 61% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/bean/AudioBroadcastEvent.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/AudioBroadcastEvent.java index 55b710f29..3c58934e9 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/bean/AudioBroadcastEvent.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/AudioBroadcastEvent.java @@ -1,4 +1,4 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.play.bean; +package com.genersoft.iot.vmp.gb28181.controller.bean; /** diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/bean/ChannelReduce.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelReduce.java similarity index 97% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/bean/ChannelReduce.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelReduce.java index 2b01d45bb..b4c1d015b 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/bean/ChannelReduce.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelReduce.java @@ -1,4 +1,4 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.platform.bean; +package com.genersoft.iot.vmp.gb28181.controller.bean; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelToGroupByGbDeviceParam.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelToGroupByGbDeviceParam.java new file mode 100644 index 000000000..b95a7b8a5 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelToGroupByGbDeviceParam.java @@ -0,0 +1,12 @@ +package com.genersoft.iot.vmp.gb28181.controller.bean; + +import lombok.Data; + +import java.util.List; + +@Data +public class ChannelToGroupByGbDeviceParam { + private List deviceIds; + private String parentId; + private String businessGroup; +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelToGroupParam.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelToGroupParam.java new file mode 100644 index 000000000..58898933d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelToGroupParam.java @@ -0,0 +1,14 @@ +package com.genersoft.iot.vmp.gb28181.controller.bean; + +import lombok.Data; + +import java.util.List; + +@Data +public class ChannelToGroupParam { + + private String parentId; + private String businessGroup; + private List channelIds; + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelToRegionByGbDeviceParam.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelToRegionByGbDeviceParam.java new file mode 100644 index 000000000..6820bb6da --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelToRegionByGbDeviceParam.java @@ -0,0 +1,11 @@ +package com.genersoft.iot.vmp.gb28181.controller.bean; + +import lombok.Data; + +import java.util.List; + +@Data +public class ChannelToRegionByGbDeviceParam { + private List deviceIds; + private String civilCode; +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelToRegionParam.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelToRegionParam.java new file mode 100644 index 000000000..32505b235 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/ChannelToRegionParam.java @@ -0,0 +1,13 @@ +package com.genersoft.iot.vmp.gb28181.controller.bean; + +import lombok.Data; + +import java.util.List; + +@Data +public class ChannelToRegionParam { + + private String civilCode; + private List channelIds; + +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/bean/PlayResult.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/PlayResult.java similarity index 88% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/bean/PlayResult.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/PlayResult.java index 58d4ce20b..630eb6677 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/bean/PlayResult.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/PlayResult.java @@ -1,9 +1,8 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.play.bean; +package com.genersoft.iot.vmp.gb28181.controller.bean; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -import org.springframework.http.ResponseEntity; import org.springframework.web.context.request.async.DeferredResult; public class PlayResult { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/UpdateChannelParam.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/UpdateChannelParam.java new file mode 100755 index 000000000..5f36002c3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/bean/UpdateChannelParam.java @@ -0,0 +1,24 @@ +package com.genersoft.iot.vmp.gb28181.controller.bean; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +@Schema(description = "通道关联参数") +public class UpdateChannelParam { + + @Schema(description = "上级平台的数据库ID") + private Integer platformId; + + + @Schema(description = "关联所有通道") + private boolean all; + + @Schema(description = "待关联的通道ID") + List channelIds; + + @Schema(description = "待关联的设备ID") + List deviceIds; +} 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 new file mode 100644 index 000000000..52213d641 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/CommonGBChannelMapper.java @@ -0,0 +1,441 @@ +package com.genersoft.iot.vmp.gb28181.dao; + +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.dao.provider.ChannelProvider; +import com.genersoft.iot.vmp.streamPush.bean.StreamPush; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.Collection; +import java.util.List; + +@Mapper +@Repository +public interface CommonGBChannelMapper { + + + @SelectProvider(type = ChannelProvider.class, method = "queryByDeviceId") + CommonGBChannel queryByDeviceId(@Param("gbDeviceId") String gbDeviceId); + + @Insert(" ") + @Options(useGeneratedKeys = true, keyProperty = "gbId", keyColumn = "id") + int insert(CommonGBChannel commonGBChannel); + + @SelectProvider(type = ChannelProvider.class, method = "queryById") + CommonGBChannel queryById(@Param("gbId") int gbId); + + @Delete(value = {"delete from wvp_device_channel where id = #{gbId} "}) + void delete(int gbId); + + @Update(value = {" "}) + int update(CommonGBChannel commonGBChannel); + + @Update(value = {" "}) + int updateStatusById(@Param("gbId") int gbId, @Param("status") int status); + + @Update("") + int updateStatusForListById(List commonGBChannels, @Param("status") String status); + + @SelectProvider(type = ChannelProvider.class, method = "queryInListByStatus") + List queryInListByStatus(List commonGBChannelList, @Param("status") String status); + + + @Insert(" ") + int batchAdd(List commonGBChannels); + + @Update("") + int updateStatus(List commonGBChannels); + + @Update(value = {" "}) + void reset(@Param("id") int id, @Param("gbDeviceDbId") int gbDeviceDbId, @Param("updateTime") String updateTime); + + + @SelectProvider(type = ChannelProvider.class, method = "queryByIds") + List queryByIds(Collection ids); + + @Delete(value = {" "}) + void batchDelete(List channelListInDb); + + @SelectProvider(type = ChannelProvider.class, method = "queryByStreamPushId") + CommonGBChannel queryByStreamPushId(@Param("streamPushId") Integer streamPushId); + + @SelectProvider(type = ChannelProvider.class, method = "queryByStreamProxyId") + CommonGBChannel queryByStreamProxyId(@Param("streamProxyId") Integer streamProxyId); + + @SelectProvider(type = ChannelProvider.class, method = "queryList") + List queryList(@Param("query") String query, @Param("online") Boolean online, + @Param("hasCivilCode") Boolean hasCivilCode, + @Param("hasGroup") Boolean hasGroup); + + @Select("") + List queryForRegionTreeByCivilCode(@Param("query") String query, @Param("parentDeviceId") String parentDeviceId); + + @Update(value = {" "}) + int removeCivilCode(List allChildren); + + + @Update(value = {" "}) + int updateRegion(@Param("civilCode") String civilCode, @Param("channelList") List channelList); + + @SelectProvider(type = ChannelProvider.class, method = "queryByIdsOrCivilCode") + List queryByIdsOrCivilCode(@Param("civilCode") String civilCode, @Param("ids") List ids); + + @Update(value = {" "}) + int removeCivilCodeByChannels(List channelList); + + @SelectProvider(type = ChannelProvider.class, method = "queryByCivilCode") + List queryByCivilCode(@Param("civilCode") String civilCode); + + @SelectProvider(type = ChannelProvider.class, method = "queryByGbDeviceIds") + List queryByGbDeviceIds(List deviceIds); + + @Select(value = {" "}) + List queryByGbDeviceIdsForIds(List deviceIds); + + @SelectProvider(type = ChannelProvider.class, method = "queryByGroupList") + List queryByGroupList(List groupList); + + @Update(value = {" "}) + int removeParentIdByChannels(List channelList); + + @SelectProvider(type = ChannelProvider.class, method = "queryByBusinessGroup") + List queryByBusinessGroup(@Param("businessGroup") String businessGroup); + + @SelectProvider(type = ChannelProvider.class, method = "queryByParentId") + List queryByParentId(@Param("parentId") String parentId); + + @Update(value = {" "}) + int updateBusinessGroupByChannelList(@Param("businessGroup") String businessGroup, List channelList); + + @Update(value = {" "}) + int updateParentIdByChannelList(@Param("parentId") String parentId, List channelList); + + @Select("") + List queryForGroupTreeByParentId(@Param("query") String query, @Param("parent") String parent); + + @Update(value = {" "}) + int updateGroup(@Param("parentId") String parentId, @Param("businessGroup") String businessGroup, + List channelList); + + @Update({""}) + int batchUpdate(List commonGBChannels); + + @SelectProvider(type = ChannelProvider.class, method = "queryWithPlatform") + List queryWithPlatform(@Param("platformId") Integer platformId); + + @SelectProvider(type = ChannelProvider.class, method = "queryShareChannelByParentId") + List queryShareChannelByParentId(@Param("parentId") String parentId, @Param("platformId") Integer platformId); + + @SelectProvider(type = ChannelProvider.class, method = "queryShareChannelByCivilCode") + List queryShareChannelByCivilCode(@Param("civilCode") String civilCode, @Param("platformId") Integer platformId); + + @Update(value = {" "}) + int updateCivilCodeByChannelList(@Param("civilCode") String civilCode, List channelList); + + @SelectProvider(type = ChannelProvider.class, method = "queryListByStreamPushList") + List queryListByStreamPushList(List streamPushList); + +} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceAlarmMapper.java similarity index 98% rename from src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceAlarmMapper.java index 589e6f2ac..e885e6b4c 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceAlarmMapper.java @@ -1,4 +1,4 @@ -package com.genersoft.iot.vmp.storager.dao; +package com.genersoft.iot.vmp.gb28181.dao; import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; import org.apache.ibatis.annotations.Delete; 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 new file mode 100755 index 000000000..e5c65c416 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceChannelMapper.java @@ -0,0 +1,604 @@ +package com.genersoft.iot.vmp.gb28181.dao; + +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelReduce; +import com.genersoft.iot.vmp.gb28181.dao.provider.DeviceChannelProvider; +import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; +import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * 用于存储设备通道信息 + */ +@Mapper +@Repository +public interface DeviceChannelMapper { + + + @Insert("") + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") + int add(DeviceChannel channel); + + @Update(value = {" "}) + int update(DeviceChannel channel); + + @SelectProvider(type = DeviceChannelProvider.class, method = "queryChannels") + List queryChannels(@Param("deviceDbId") int deviceDbId, @Param("parentChannelId") String parentChannelId, @Param("query") String query, @Param("hasSubChannel") Boolean hasSubChannel, @Param("online") Boolean online, @Param("channelIds") List channelIds); + + @SelectProvider(type = DeviceChannelProvider.class, method = "queryChannelsByDeviceDbId") + List queryChannelsByDeviceDbId(@Param("deviceDbId") int deviceDbId); + + @Delete("DELETE FROM wvp_device_channel WHERE device_db_id=#{deviceId}") + int cleanChannelsByDeviceId(@Param("deviceId") int deviceId); + + @Delete("DELETE FROM wvp_device_channel WHERE id=#{id}") + int del(@Param("id") int id); + + @Select(value = {" "}) + List queryChannelsWithDeviceInfo(@Param("deviceId") String deviceId, @Param("parentChannelId") String parentChannelId, @Param("query") String query, @Param("hasSubChannel") Boolean hasSubChannel, @Param("online") Boolean online, @Param("channelIds") List channelIds); + + @Update(value = {"UPDATE wvp_device_channel SET stream_id=null WHERE device_db_id=#{deviceId} AND device_id=#{channelId}"}) + void stopPlay(@Param("deviceId") int deviceId, @Param("channelId") String channelId); + + @Update(value = {"UPDATE wvp_device_channel SET stream_id=#{streamId} WHERE id=#{channelId}"}) + void startPlay(@Param("channelId") Integer channelId, @Param("streamId") String streamId); + + + @Select(value = {" "}) + List queryChannelListInAll(@Param("query") String query, @Param("online") Boolean online, @Param("hasSubChannel") Boolean hasSubChannel, @Param("platformId") String platformId, @Param("catalogId") String catalogId); + + + @Update(value = {"UPDATE wvp_device_channel SET status='OFF' WHERE id=#{id}"}) + void offline(@Param("id") int id); + + @Insert("") + int batchAdd(@Param("addChannels") List addChannels); + + + @Update(value = {"UPDATE wvp_device_channel SET status='OFF' WHERE id=#{id}"}) + void online(@Param("id") int id); + + @Update({""}) + int batchUpdate(List updateChannels); + + + @Update({""}) + int batchUpdateForNotify(List updateChannels); + + @Update(" update wvp_device_channel" + + " set sub_count = (select *" + + " from (select count(0)" + + " from wvp_device_channel" + + " where device_db_id = #{deviceDbId} and parent_id = #{channelId}) as temp)" + + " where device_db_id = #{deviceDbId} and device_id = #{channelId}") + int updateChannelSubCount(@Param("deviceDbId") int deviceDbId, @Param("channelId") String channelId); + + @Update(value = {" "}) + int updatePosition(DeviceChannel deviceChannel); + + @Select("select " + + " id,\n" + + " device_db_id,\n" + + " create_time,\n" + + " update_time,\n" + + " sub_count,\n" + + " stream_id,\n" + + " has_audio,\n" + + " gps_time,\n" + + " stream_identification,\n" + + " channel_type,\n" + + " device_id,\n" + + " name,\n" + + " manufacturer,\n" + + " model,\n" + + " owner,\n" + + " civil_code,\n" + + " block,\n" + + " address,\n" + + " parental,\n" + + " parent_id,\n" + + " safety_way,\n" + + " register_way,\n" + + " cert_num,\n" + + " certifiable,\n" + + " err_code,\n" + + " end_time,\n" + + " secrecy,\n" + + " ip_address,\n" + + " port,\n" + + " password,\n" + + " status,\n" + + " longitude,\n" + + " latitude,\n" + + " ptz_type,\n" + + " position_type,\n" + + " room_type,\n" + + " use_type,\n" + + " supply_light_type,\n" + + " direction_type,\n" + + " resolution,\n" + + " business_group_id,\n" + + " download_speed,\n" + + " svc_space_support_mod,\n" + + " svc_time_support_mode\n" + + " from wvp_device_channel where device_db_id = #{deviceDbId}") + List queryAllChannelsForRefresh(@Param("deviceDbId") int deviceDbId); + + @Select("select de.* from wvp_device de left join wvp_device_channel dc on de.device_id = dc.deviceId where dc.device_id=#{channelId}") + List getDeviceByChannelDeviceId(String channelId); + + + @Delete({""}) + int batchDel(List deleteChannelList); + + @Update({""}) + int batchUpdateStatus(List channels); + + @Select("select count(1) from wvp_device_channel where status = 'ON'") + int getOnlineCount(); + + @Select("select count(1) from wvp_device_channel") + int getAllChannelCount(); + + @Update("") + void updateChannelStreamIdentification(DeviceChannel channel); + + + @Update({""}) + void batchUpdatePosition(List channelList); + + @SelectProvider(type = DeviceChannelProvider.class, method = "getOne") + DeviceChannel getOne(@Param("id") int id); + + @Select(value = {" "}) + DeviceChannel getOneForSource(@Param("id") int id); + + @SelectProvider(type = DeviceChannelProvider.class, method = "getOneByDeviceId") + DeviceChannel getOneByDeviceId(@Param("deviceDbId") int deviceDbId, @Param("channelId") String channelId); + + + @Select(value = {" "}) + DeviceChannel getOneByDeviceIdForSource(@Param("deviceDbId") int deviceDbId, @Param("channelId") String channelId); + + + @Update(value = {"UPDATE wvp_device_channel SET stream_id=null WHERE id=#{channelId}"}) + void stopPlayById(@Param("channelId") Integer channelId); + + @Update(value = {" "}) + void changeAudio(@Param("channelId") int channelId, @Param("audio") boolean audio); + + @Update("") + void updateStreamGPS(List gpsMsgInfoList); + + @Update("UPDATE wvp_device_channel SET status=#{status} WHERE device_id=#{deviceId} AND channel_id=#{channelId}") + void updateStatus(DeviceChannel channel); + + + @Update({""}) + void updateChannelForNotify(DeviceChannel channel); +} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceMapper.java similarity index 79% rename from src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceMapper.java index f41bf7250..cca4d84ff 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceMapper.java @@ -1,6 +1,8 @@ -package com.genersoft.iot.vmp.storager.dao; +package com.genersoft.iot.vmp.gb28181.dao; import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.github.pagehelper.PageInfo; import org.apache.ibatis.annotations.*; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @@ -15,6 +17,7 @@ import java.util.List; public interface DeviceMapper { @Select("SELECT " + + "id, " + "device_id, " + "coalesce(custom_name, name) as name, " + "password, " + @@ -44,8 +47,8 @@ public interface DeviceMapper { "on_line," + "media_server_id," + "broadcast_push_after_ack," + - "(SELECT count(0) FROM wvp_device_channel WHERE device_id=wvp_device.device_id) as channel_count "+ - " FROM wvp_device WHERE device_id = #{deviceId}") + "(SELECT count(0) FROM wvp_device_channel dc WHERE dc.device_db_id= de.id) as channel_count "+ + " FROM wvp_device de WHERE de.device_id = #{deviceId}") Device getDeviceByDeviceId(String deviceId); @Insert("INSERT INTO wvp_device (" + @@ -107,6 +110,7 @@ public interface DeviceMapper { "#{geoCoordSys}," + "#{onLine}" + ")") + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") int add(Device device); @Update(value = {" " ) List getDevices(Boolean onLine); @@ -174,6 +179,7 @@ public interface DeviceMapper { int del(String deviceId); @Select("SELECT " + + "id, " + "device_id, " + "coalesce(custom_name, name) as name, " + "password, " + @@ -204,7 +210,9 @@ public interface DeviceMapper { "on_line"+ " FROM wvp_device WHERE on_line = true") List getOnlineDevices(); + @Select("SELECT " + + "id,"+ "device_id,"+ "coalesce(custom_name,name)as name,"+ "password,"+ @@ -295,4 +303,58 @@ public interface DeviceMapper { @Select("select * FROM wvp_device where as_message_channel = true") List queryDeviceWithAsMessageChannel(); + + @Select(" ") + List getDeviceList(@Param("query") String query, @Param("status") Boolean status); + + @Select("select * from wvp_device_channel where id = #{id}") + DeviceChannel getRawChannel(@Param("id") int id); + + @Select("select * from wvp_device where id = #{id}") + Device query(@Param("id") Integer id); + + @Select("select wd.* from wvp_device wd left join wvp_device_channel wdc on wd.id = wdc.device_db_id where wdc.id = #{channelId}") + Device queryByChannelId(@Param("channelId") Integer channelId); + + @Select("select wd.* from wvp_device wd left join wvp_device_channel wdc on wd.id = wdc.device_db_id where wdc.device_id = #{channelDeviceId}") + Device getDeviceBySourceChannelDeviceId(@Param("channelDeviceId") String channelDeviceId); + } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMobilePositionMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceMobilePositionMapper.java similarity index 61% rename from src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMobilePositionMapper.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceMobilePositionMapper.java index 512431063..3e09df8ce 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMobilePositionMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceMobilePositionMapper.java @@ -1,4 +1,4 @@ -package com.genersoft.iot.vmp.storager.dao; +package com.genersoft.iot.vmp.gb28181.dao; import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; import org.apache.ibatis.annotations.Delete; @@ -12,8 +12,8 @@ import java.util.List; @Mapper public interface DeviceMobilePositionMapper { - @Insert("INSERT INTO wvp_device_mobile_position (device_id,channel_id, device_name,time,longitude,latitude,altitude,speed,direction,report_source,longitude_gcj02,latitude_gcj02,longitude_wgs84,latitude_wgs84,create_time)"+ - "VALUES (#{deviceId}, #{channelId}, #{deviceName}, #{time}, #{longitude}, #{latitude}, #{altitude}, #{speed}, #{direction}, #{reportSource}, #{longitudeGcj02}, #{latitudeGcj02}, #{longitudeWgs84}, #{latitudeWgs84}, #{createTime})") + @Insert("INSERT INTO wvp_device_mobile_position (device_id,channel_id, device_name,time,longitude,latitude,altitude,speed,direction,report_source,create_time)"+ + "VALUES (#{deviceId}, #{channelId}, #{deviceName}, #{time}, #{longitude}, #{latitude}, #{altitude}, #{speed}, #{direction}, #{reportSource}, #{createTime})") int insertNewPosition(MobilePosition mobilePosition); @Select(value = {" ") - void batchadd2(List mobilePositions); - @Insert("") void batchadd(List mobilePositions); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/GroupMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/GroupMapper.java new file mode 100644 index 000000000..5fb644680 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/GroupMapper.java @@ -0,0 +1,268 @@ +package com.genersoft.iot.vmp.gb28181.dao; + +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.Group; +import com.genersoft.iot.vmp.gb28181.bean.GroupTree; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import org.apache.ibatis.annotations.*; + +import java.util.List; +import java.util.Set; + +@Mapper +public interface GroupMapper { + + @Insert("INSERT INTO wvp_common_group (device_id, name, parent_id, parent_device_id, business_group, create_time, update_time, civil_code) " + + "VALUES (#{deviceId}, #{name}, #{parentId}, #{parentDeviceId}, #{businessGroup}, #{createTime}, #{updateTime}, #{civilCode})") + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") + int add(Group group); + + @Insert("INSERT INTO wvp_common_group (device_id, name, business_group, create_time, update_time, civil_code) " + + "VALUES (#{deviceId}, #{name}, #{businessGroup}, #{createTime}, #{updateTime}, #{civilCode})") + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") + int addBusinessGroup(Group group); + + @Delete("DELETE FROM wvp_common_group WHERE id=#{id}") + int delete(@Param("id") int id); + + @Update(" UPDATE wvp_common_group " + + " SET update_time=#{updateTime}, device_id=#{deviceId}, name=#{name}, parent_id=#{parentId}, " + + " parent_device_id=#{parentDeviceId}, business_group=#{businessGroup}, civil_code=#{civilCode}" + + " WHERE id = #{id}") + int update(Group group); + + @Select(value = {" "}) + List query(@Param("query") String query, @Param("parentId") String parentId, @Param("businessGroup") String businessGroup); + + @Select("SELECT * from wvp_common_group WHERE parent_id = #{parentId} ") + List getChildren(@Param("parentId") int parentId); + + @Select("SELECT * from wvp_common_group WHERE id = #{id} ") + Group queryOne(@Param("id") int id); + + + @Insert(" ") + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") + int batchAdd(List groupList); + + @Select(" ") + List queryForTree(@Param("query") String query, @Param("parentId") Integer parentId); + + @Select(" ") + List queryForTreeByBusinessGroup(@Param("query") String query, + @Param("businessGroup") String businessGroup); + + @Select(" ") + List queryBusinessGroupForTree(@Param("query") String query); + + @Select("SELECT * from wvp_common_group WHERE device_id = #{deviceId} and business_group = #{businessGroup}") + Group queryOneByDeviceId(@Param("deviceId") String deviceId, @Param("businessGroup") String businessGroup); + + @Delete("") + int batchDelete(List allChildren); + + @Select("SELECT * from wvp_common_group WHERE device_id = #{businessGroup} and business_group = #{businessGroup} ") + Group queryBusinessGroup(@Param("businessGroup") String businessGroup); + + @Select("SELECT * from wvp_common_group WHERE business_group = #{businessGroup} ") + List queryByBusinessGroup(@Param("businessGroup") String businessGroup); + + @Delete("DELETE FROM wvp_common_group WHERE business_group = #{businessGroup}") + int deleteByBusinessGroup(@Param("businessGroup") String businessGroup); + + @Update(" UPDATE wvp_common_group " + + " SET parent_device_id=#{group.deviceId}, business_group = #{group.businessGroup}" + + " WHERE parent_id = #{parentId}") + int updateChild(@Param("parentId") Integer parentId, Group group); + + @Select(" ") + List queryInGroupListByDeviceId(List groupList); + + @Select(" ") + Set queryInChannelList(List channelList); + + @Select(" ") + Set queryParentInChannelList(Set groupSet); + + @Select(" ") + List queryForPlatform(@Param("platformId") Integer platformId); + + @Select(" ") + Set queryNotShareGroupForPlatformByChannelList(List channelList, @Param("platformId") Integer platformId); + + @Select(" ") + Set queryNotShareGroupForPlatformByGroupList(Set allGroup, @Param("platformId") Integer platformId); + + + @Select(" ") + Set queryByChannelList(List channelList); + + @Update(value = " ", databaseId = "mysql") + @Update( value = " ", databaseId = "postgresql") + @Update( value = " ", databaseId = "kingbase") + void updateParentId(List groupListForAdd); + + @Update(value = " ", databaseId = "mysql") + @Update( value = " ", databaseId = "kingbase") + @Update( value = " ", databaseId = "postgresql") + void updateParentIdWithBusinessGroup(List groupListForAdd); + + @Select(" ") + List queryForPlatformByGroupId(@Param("groupId") int groupId); + + @Delete("DELETE FROM wvp_platform_group WHERE group_id = #{groupId}") + void deletePlatformGroup(@Param("groupId") int groupId); +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/PlatformChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/PlatformChannelMapper.java new file mode 100755 index 000000000..3311a249c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/PlatformChannelMapper.java @@ -0,0 +1,543 @@ +package com.genersoft.iot.vmp.gb28181.dao; + +import com.genersoft.iot.vmp.gb28181.bean.*; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +@Mapper +@Repository +public interface PlatformChannelMapper { + + + @Insert("") + int addChannels(@Param("platformId") Integer platformId, @Param("channelList") List channelList); + + @Delete("") + int delChannelForDeviceId(String deviceId); + + @Select("select d.*\n" + + "from wvp_platform_channel pgc\n" + + " left join wvp_device_channel dc on dc.id = pgc.device_channel_id\n" + + " left join wvp_device d on dc.device_id = d.device_id\n" + + "where dc.channel_type = 0 and dc.channel_id = #{channelId} and pgc.platform_id=#{platformId}") + List queryDeviceByPlatformIdAndChannelId(@Param("platformId") String platformId, @Param("channelId") String channelId); + + @Select(" ") + List queryPlatFormListForGBWithGBId(@Param("channelId") Integer channelId, List platforms); + + @Select("select dc.channel_id, dc.device_id,dc.name,d.manufacturer,d.model,d.firmware\n" + + "from wvp_platform_channel pgc\n" + + " left join wvp_device_channel dc on dc.id = pgc.device_channel_id\n" + + " left join wvp_device d on dc.device_id = d.device_id\n" + + "where dc.channel_type = 0 and dc.channel_id = #{channelId} and pgc.platform_id=#{platformId}") + List queryDeviceInfoByPlatformIdAndChannelId(@Param("platformId") String platformId, @Param("channelId") String channelId); + + @Select("SELECT pgc.platform_id from wvp_platform_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id WHERE dc.channel_type = 0 and dc.device_id=#{channelId}") + List queryParentPlatformByChannelId(@Param("channelId") String channelId); + + @Select("") + List queryForPlatformForWebList(@Param("platformId") Integer platformId, @Param("query") String query, + @Param("channelType") Integer channelType, @Param("online") Boolean online, + @Param("hasShare") Boolean hasShare); + + @Select("select\n" + + " wdc.id as gb_id,\n" + + " wdc.device_db_id as gb_device_db_id,\n" + + " wdc.stream_push_id,\n" + + " wdc.stream_proxy_id,\n" + + " wdc.create_time,\n" + + " wdc.update_time,\n" + + " coalesce(wpgc.custom_device_id, wdc.gb_device_id, wdc.device_id) as gb_device_id,\n" + + " coalesce(wpgc.custom_name, wdc.gb_name, wdc.name) as gb_name,\n" + + " coalesce(wpgc.custom_manufacturer, wdc.gb_manufacturer, wdc.manufacturer) as gb_manufacturer,\n" + + " coalesce(wpgc.custom_model, wdc.gb_model, wdc.model) as gb_model,\n" + + " coalesce(wpgc.custom_owner, wdc.gb_owner, wdc.owner) as gb_owner,\n" + + " coalesce(wpgc.custom_civil_code, wdc.gb_civil_code, wdc.civil_code) as gb_civil_code,\n" + + " coalesce(wpgc.custom_block, wdc.gb_block, wdc.block) as gb_block,\n" + + " coalesce(wpgc.custom_address, wdc.gb_address, wdc.address) as gb_address,\n" + + " coalesce(wpgc.custom_parental, wdc.gb_parental, wdc.parental) as gb_parental,\n" + + " coalesce(wpgc.custom_parent_id, wdc.gb_parent_id, wdc.parent_id) as gb_parent_id,\n" + + " coalesce(wpgc.custom_safety_way, wdc.gb_safety_way, wdc.safety_way) as gb_safety_way,\n" + + " coalesce(wpgc.custom_register_way, wdc.gb_register_way, wdc.register_way) as gb_register_way,\n" + + " coalesce(wpgc.custom_cert_num, wdc.gb_cert_num, wdc.cert_num) as gb_cert_num,\n" + + " coalesce(wpgc.custom_certifiable, wdc.gb_certifiable, wdc.certifiable) as gb_certifiable,\n" + + " coalesce(wpgc.custom_err_code, wdc.gb_err_code, wdc.err_code) as gb_err_code,\n" + + " coalesce(wpgc.custom_end_time, wdc.gb_end_time, wdc.end_time) as gb_end_time,\n" + + " coalesce(wpgc.custom_secrecy, wdc.gb_secrecy, wdc.secrecy) as gb_secrecy,\n" + + " coalesce(wpgc.custom_ip_address, wdc.gb_ip_address, wdc.ip_address) as gb_ip_address,\n" + + " coalesce(wpgc.custom_port, wdc.gb_port, wdc.port) as gb_port,\n" + + " coalesce(wpgc.custom_password, wdc.gb_password, wdc.password) as gb_password,\n" + + " coalesce(wpgc.custom_status, wdc.gb_status, wdc.status) as gb_status,\n" + + " coalesce(wpgc.custom_longitude, wdc.gb_longitude, wdc.longitude) as gb_longitude,\n" + + " coalesce(wpgc.custom_latitude, wdc.gb_latitude, wdc.latitude) as gb_latitude,\n" + + " coalesce(wpgc.custom_ptz_type, wdc.gb_ptz_type, wdc.ptz_type) as gb_ptz_type,\n" + + " coalesce(wpgc.custom_position_type, wdc.gb_position_type, wdc.position_type) as gb_position_type,\n" + + " coalesce(wpgc.custom_room_type, wdc.gb_room_type, wdc.room_type) as gb_room_type,\n" + + " coalesce(wpgc.custom_use_type, wdc.gb_use_type, wdc.use_type) as gb_use_type,\n" + + " coalesce(wpgc.custom_supply_light_type, wdc.gb_supply_light_type, wdc.supply_light_type) as gb_supply_light_type,\n" + + " coalesce(wpgc.custom_direction_type, wdc.gb_direction_type, wdc.direction_type) as gb_direction_type,\n" + + " coalesce(wpgc.custom_resolution, wdc.gb_resolution, wdc.resolution) as gb_resolution,\n" + + " coalesce(wpgc.custom_business_group_id, wdc.gb_business_group_id, wdc.business_group_id) as gb_business_group_id,\n" + + " coalesce(wpgc.custom_download_speed, wdc.gb_download_speed, wdc.download_speed) as gb_download_speed,\n" + + " coalesce(wpgc.custom_svc_space_support_mod, wdc.gb_svc_space_support_mod, wdc.svc_space_support_mod) as gb_svc_space_support_mod,\n" + + " coalesce(wpgc.custom_svc_time_support_mode, wdc.gb_svc_time_support_mode, wdc.svc_time_support_mode) as gb_svc_time_support_mode\n" + + " from wvp_device_channel wdc" + + " left join wvp_platform_channel wpgc on wdc.id = wpgc.device_channel_id" + + " where wdc.channel_type = 0 and wpgc.platform_id = #{platformId} and coalesce(wpgc.custom_device_id, wdc.gb_device_id, wdc.device_id) = #{channelDeviceId} order by wdc.id " + + ) + List queryOneWithPlatform(@Param("platformId") Integer platformId, @Param("channelDeviceId") String channelDeviceId); + + + @Select("") + List queryNotShare(@Param("platformId") Integer platformId, List channelIds); + + @Select("") + List queryShare(@Param("platformId") Integer platformId, List channelIds); + + @Delete("") + int removeChannelsWithPlatform(@Param("platformId") Integer platformId, List channelList); + + @Delete("") + int removeChannels(List channelList); + + @Insert("") + int addPlatformGroup(Collection groupListNotShare, @Param("platformId") Integer platformId); + + @Insert("") + int addPlatformRegion(List regionListNotShare, @Param("platformId") Integer platformId); + + @Delete("") + int removePlatformGroup(List groupList, @Param("platformId") Integer platformId); + + @Delete("") + void removePlatformGroupById(@Param("id") int id, @Param("platformId") Integer platformId); + + @Delete("") + void removePlatformRegionById(@Param("id") int id, @Param("platformId") Integer platformId); + + @Select(" ") + Set queryShareChildrenGroup(@Param("parentId") Integer parentId, @Param("platformId") Integer platformId); + + @Select(" ") + Set queryShareChildrenRegion(@Param("parentId") String parentId, @Param("platformId") Integer platformId); + + @Select(" ") + Set queryShareParentGroupByGroupSet(Set groupSet, @Param("platformId") Integer platformId); + + @Select(" ") + Set queryShareParentRegionByRegionSet(Set regionSet, @Param("platformId") Integer platformId); + + @Select(" ") + List queryPlatFormListByChannelList(Collection ids); + + @Select(" ") + List queryPlatFormListByChannelId(@Param("channelId") int channelId); + + @Delete("") + void removeChannelsByPlatformId(@Param("platformId") Integer platformId); + + @Delete("") + void removePlatformGroupsByPlatformId(@Param("platformId") Integer platformId); + + @Delete("") + void removePlatformRegionByPlatformId(@Param("platformId") Integer platformId); + + @Update(value = {" "}) + void updateCustomChannel(PlatformChannel channel); + + + @Select("") + CommonGBChannel queryShareChannel(@Param("platformId") int platformId, @Param("gbId") int gbId); + + + @Select(" ") + Set queryShareGroup(@Param("platformId") Integer platformId); + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/PlatformMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/PlatformMapper.java new file mode 100755 index 000000000..1fe7ba2a7 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/PlatformMapper.java @@ -0,0 +1,97 @@ +package com.genersoft.iot.vmp.gb28181.dao; + +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * 用于存储上级平台 + */ +@Mapper +@Repository +public interface PlatformMapper { + + @Insert("INSERT INTO wvp_platform (enable, name, server_gb_id, server_gb_domain, server_ip, server_port,device_gb_id,device_ip,"+ + " device_port,username,password,expires,keep_timeout,transport,character_set,ptz,rtcp,status,catalog_group, update_time," + + " create_time, as_message_channel, send_stream_ip, auto_push_channel, catalog_with_platform,catalog_with_group,catalog_with_region, "+ + " civil_code,manufacturer,model,address,register_way,secrecy) " + + " VALUES (#{enable}, #{name}, #{serverGBId}, #{serverGBDomain}, #{serverIp}, #{serverPort}, #{deviceGBId}, #{deviceIp}, " + + " #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, #{status}, #{catalogGroup},#{updateTime}," + + " #{createTime}, #{asMessageChannel}, #{sendStreamIp}, #{autoPushChannel}, #{catalogWithPlatform}, #{catalogWithGroup},#{catalogWithRegion}, " + + " #{civilCode}, #{manufacturer}, #{model}, #{address}, #{registerWay}, #{secrecy})") + int add(Platform parentPlatform); + + @Update("UPDATE wvp_platform " + + "SET update_time = #{updateTime}," + + " enable=#{enable}, " + + " name=#{name}," + + " server_gb_id=#{serverGBId}, " + + " server_gb_domain=#{serverGBDomain}, " + + " server_ip=#{serverIp}," + + " server_port=#{serverPort}, " + + " device_gb_id=#{deviceGBId}," + + " device_ip=#{deviceIp}, " + + " device_port=#{devicePort}, " + + " username=#{username}, " + + " password=#{password}, " + + " expires=#{expires}, " + + " keep_timeout=#{keepTimeout}, " + + " transport=#{transport}, " + + " character_set=#{characterSet}, " + + " ptz=#{ptz}, " + + " rtcp=#{rtcp}, " + + " status=#{status}, " + + " catalog_group=#{catalogGroup}, " + + " as_message_channel=#{asMessageChannel}, " + + " send_stream_ip=#{sendStreamIp}, " + + " auto_push_channel=#{autoPushChannel}, " + + " catalog_with_platform=#{catalogWithPlatform}, " + + " catalog_with_group=#{catalogWithGroup}, " + + " catalog_with_region=#{catalogWithRegion}, " + + " civil_code=#{civilCode}, " + + " manufacturer=#{manufacturer}, " + + " model=#{model}, " + + " address=#{address}, " + + " register_way=#{registerWay}, " + + " secrecy=#{secrecy} " + + "WHERE id=#{id}") + int update(Platform parentPlatform); + + @Delete("DELETE FROM wvp_platform WHERE id=#{id}") + int delete(@Param("id") Integer id); + + @Select(" ") + List queryList(@Param("query") String query); + + @Select("SELECT * FROM wvp_platform WHERE enable=#{enable} ") + List getEnableParentPlatformList(boolean enable); + + @Select("SELECT * FROM wvp_platform WHERE enable=true and as_message_channel=true") + List queryEnablePlatformListWithAsMessageChannel(); + + @Select("SELECT * FROM wvp_platform WHERE server_gb_id=#{platformGbId}") + Platform getParentPlatByServerGBId(String platformGbId); + + @Select("SELECT * FROM wvp_platform WHERE id=#{id}") + Platform query(int id); + + @Update("UPDATE wvp_platform SET status=#{online} WHERE server_gb_id=#{platformGbID}" ) + int updateStatus(@Param("platformGbID") String platformGbID, @Param("online") boolean online); + + @Select("SELECT * FROM wvp_platform WHERE enable=true") + List queryEnablePlatformList(); + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/RegionMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/RegionMapper.java new file mode 100644 index 000000000..42db7bbf9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/RegionMapper.java @@ -0,0 +1,182 @@ +package com.genersoft.iot.vmp.gb28181.dao; + +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.Group; +import com.genersoft.iot.vmp.gb28181.bean.Region; +import com.genersoft.iot.vmp.gb28181.bean.RegionTree; +import org.apache.ibatis.annotations.*; + +import java.util.List; +import java.util.Set; + +@Mapper +public interface RegionMapper { + + @Insert("INSERT INTO wvp_common_region (device_id, name, parent_id, parent_device_id, create_time, update_time) " + + "VALUES (#{deviceId}, #{name}, #{parentId}, #{parentDeviceId}, #{createTime}, #{updateTime})") + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") + void add(Region region); + + @Delete("DELETE FROM wvp_common_region WHERE id=#{id}") + int delete(@Param("id") int id); + + @Update(" UPDATE wvp_common_region " + + " SET update_time=#{updateTime}, device_id=#{deviceId}, name=#{name}, parent_id=#{parentId}, parent_device_id=#{parentDeviceId}" + + " WHERE id = #{id}") + int update(Region region); + + @Select(value = {" "}) + List query(@Param("query") String query, @Param("parentId") String parentId); + + @Select("SELECT * from wvp_common_region WHERE parent_id = #{parentId} ORDER BY id ") + List getChildren(@Param("parentId") Integer parentId); + + @Select("SELECT * from wvp_common_region WHERE id = #{id} ") + Region queryOne(@Param("id") int id); + + @Select(" select dc.civil_code as civil_code " + + " from wvp_device_channel dc " + + " where dc.civil_code not in " + + " (select device_id from wvp_common_region)") + List getUninitializedCivilCode(); + + @Select(" ") + List queryInList(Set codes); + + + @Insert(" ") + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") + int batchAdd(List regionList); + + @Select(" ") + List queryForTree(@Param("query") String query, @Param("parentId") Integer parentId); + + @Delete("") + void batchDelete(List allChildren); + + @Select(" ") + List queryInRegionListByDeviceId(List regionList); + + @Select(" ") + List queryByPlatform(@Param("platformId") Integer platformId); + + + @Update(value = " ", databaseId = "mysql") + @Update( value = " ", databaseId = "kingbase") + @Update( value = " ", databaseId = "postgresql") + void updateParentId(List regionListForAdd); + + @Update(" ") + void updateChild(@Param("parentId") int parentId, @Param("parentDeviceId") String parentDeviceId); + + @Select("SELECT * from wvp_common_region WHERE device_id = #{deviceId} ") + Region queryByDeviceId(@Param("deviceId") String deviceId); + + @Select(" ") + Set queryParentInChannelList(Set regionSet); + + @Select(" ") + Set queryByChannelList(List channelList); + + @Select(" ") + Set queryNotShareRegionForPlatformByChannelList(List channelList, @Param("platformId") Integer platformId); + + @Select(" ") + Set queryNotShareRegionForPlatformByRegionList(Set allRegion, @Param("platformId") Integer platformId); + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/provider/ChannelProvider.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/provider/ChannelProvider.java new file mode 100644 index 000000000..d4f5ce73f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/provider/ChannelProvider.java @@ -0,0 +1,327 @@ +package com.genersoft.iot.vmp.gb28181.dao.provider; + +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.Group; +import com.genersoft.iot.vmp.streamPush.bean.StreamPush; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class ChannelProvider { + + public final static String BASE_SQL = "select\n" + + " id as gb_id,\n" + + " device_db_id as gb_device_db_id,\n" + + " stream_push_id,\n" + + " stream_proxy_id,\n" + + " create_time,\n" + + " update_time,\n" + + " coalesce(gb_device_id, device_id) as gb_device_id,\n" + + " coalesce(gb_name, name) as gb_name,\n" + + " coalesce(gb_manufacturer, manufacturer) as gb_manufacturer,\n" + + " coalesce(gb_model, model) as gb_model,\n" + + " coalesce(gb_owner, owner) as gb_owner,\n" + + " coalesce(gb_civil_code, civil_code) as gb_civil_code,\n" + + " coalesce(gb_block, block) as gb_block,\n" + + " coalesce(gb_address, address) as gb_address,\n" + + " coalesce(gb_parental, parental) as gb_parental,\n" + + " coalesce(gb_parent_id, parent_id) as gb_parent_id,\n" + + " coalesce(gb_safety_way, safety_way) as gb_safety_way,\n" + + " coalesce(gb_register_way, register_way) as gb_register_way,\n" + + " coalesce(gb_cert_num, cert_num) as gb_cert_num,\n" + + " coalesce(gb_certifiable, certifiable) as gb_certifiable,\n" + + " coalesce(gb_err_code, err_code) as gb_err_code,\n" + + " coalesce(gb_end_time, end_time) as gb_end_time,\n" + + " coalesce(gb_secrecy, secrecy) as gb_secrecy,\n" + + " coalesce(gb_ip_address, ip_address) as gb_ip_address,\n" + + " coalesce(gb_port, port) as gb_port,\n" + + " coalesce(gb_password, password) as gb_password,\n" + + " coalesce(gb_status, status) as gb_status,\n" + + " coalesce(gb_longitude, longitude) as gb_longitude,\n" + + " coalesce(gb_latitude, latitude) as gb_latitude,\n" + + " coalesce(gb_ptz_type, ptz_type) as gb_ptz_type,\n" + + " coalesce(gb_position_type, position_type) as gb_position_type,\n" + + " coalesce(gb_room_type, room_type) as gb_room_type,\n" + + " coalesce(gb_use_type, use_type) as gb_use_type,\n" + + " coalesce(gb_supply_light_type, supply_light_type) as gb_supply_light_type,\n" + + " coalesce(gb_direction_type, direction_type) as gb_direction_type,\n" + + " coalesce(gb_resolution, resolution) as gb_resolution,\n" + + " coalesce(gb_business_group_id, business_group_id) as gb_business_group_id,\n" + + " coalesce(gb_download_speed, download_speed) as gb_download_speed,\n" + + " coalesce(gb_svc_space_support_mod, svc_space_support_mod) as gb_svc_space_support_mod,\n" + + " coalesce(gb_svc_time_support_mode,svc_time_support_mode) as gb_svc_time_support_mode\n" + + " from wvp_device_channel\n" + ; + + private final static String BASE_SQL_FOR_PLATFORM = + "select\n" + + " wdc.id as gb_id,\n" + + " wdc.device_db_id as gb_device_db_id,\n" + + " wdc.stream_push_id,\n" + + " wdc.stream_proxy_id,\n" + + " wdc.create_time,\n" + + " wdc.update_time,\n" + + " coalesce(wpgc.custom_device_id, wdc.gb_device_id, wdc.device_id) as gb_device_id,\n" + + " coalesce(wpgc.custom_name, wdc.gb_name, wdc.name) as gb_name,\n" + + " coalesce(wpgc.custom_manufacturer, wdc.gb_manufacturer, wdc.manufacturer) as gb_manufacturer,\n" + + " coalesce(wpgc.custom_model, wdc.gb_model, wdc.model) as gb_model,\n" + + " coalesce(wpgc.custom_owner, wdc.gb_owner, wdc.owner) as gb_owner,\n" + + " coalesce(wpgc.custom_civil_code, wdc.gb_civil_code, wdc.civil_code) as gb_civil_code,\n" + + " coalesce(wpgc.custom_block, wdc.gb_block, wdc.block) as gb_block,\n" + + " coalesce(wpgc.custom_address, wdc.gb_address, wdc.address) as gb_address,\n" + + " coalesce(wpgc.custom_parental, wdc.gb_parental, wdc.parental) as gb_parental,\n" + + " coalesce(wpgc.custom_parent_id, wdc.gb_parent_id, wdc.parent_id) as gb_parent_id,\n" + + " coalesce(wpgc.custom_safety_way, wdc.gb_safety_way, wdc.safety_way) as gb_safety_way,\n" + + " coalesce(wpgc.custom_register_way, wdc.gb_register_way, wdc.register_way) as gb_register_way,\n" + + " coalesce(wpgc.custom_cert_num, wdc.gb_cert_num, wdc.cert_num) as gb_cert_num,\n" + + " coalesce(wpgc.custom_certifiable, wdc.gb_certifiable, wdc.certifiable) as gb_certifiable,\n" + + " coalesce(wpgc.custom_err_code, wdc.gb_err_code, wdc.err_code) as gb_err_code,\n" + + " coalesce(wpgc.custom_end_time, wdc.gb_end_time, wdc.end_time) as gb_end_time,\n" + + " coalesce(wpgc.custom_secrecy, wdc.gb_secrecy, wdc.secrecy) as gb_secrecy,\n" + + " coalesce(wpgc.custom_ip_address, wdc.gb_ip_address, wdc.ip_address) as gb_ip_address,\n" + + " coalesce(wpgc.custom_port, wdc.gb_port, wdc.port) as gb_port,\n" + + " coalesce(wpgc.custom_password, wdc.gb_password, wdc.password) as gb_password,\n" + + " coalesce(wpgc.custom_status, wdc.gb_status, wdc.status) as gb_status,\n" + + " coalesce(wpgc.custom_longitude, wdc.gb_longitude, wdc.longitude) as gb_longitude,\n" + + " coalesce(wpgc.custom_latitude, wdc.gb_latitude, wdc.latitude) as gb_latitude,\n" + + " coalesce(wpgc.custom_ptz_type, wdc.gb_ptz_type, wdc.ptz_type) as gb_ptz_type,\n" + + " coalesce(wpgc.custom_position_type, wdc.gb_position_type, wdc.position_type) as gb_position_type,\n" + + " coalesce(wpgc.custom_room_type, wdc.gb_room_type, wdc.room_type) as gb_room_type,\n" + + " coalesce(wpgc.custom_use_type, wdc.gb_use_type, wdc.use_type) as gb_use_type,\n" + + " coalesce(wpgc.custom_supply_light_type, wdc.gb_supply_light_type, wdc.supply_light_type) as gb_supply_light_type,\n" + + " coalesce(wpgc.custom_direction_type, wdc.gb_direction_type, wdc.direction_type) as gb_direction_type,\n" + + " coalesce(wpgc.custom_resolution, wdc.gb_resolution, wdc.resolution) as gb_resolution,\n" + + " coalesce(wpgc.custom_business_group_id, wdc.gb_business_group_id, wdc.business_group_id) as gb_business_group_id,\n" + + " coalesce(wpgc.custom_download_speed, wdc.gb_download_speed, wdc.download_speed) as gb_download_speed,\n" + + " coalesce(wpgc.custom_svc_space_support_mod, wdc.gb_svc_space_support_mod, wdc.svc_space_support_mod) as gb_svc_space_support_mod,\n" + + " coalesce(wpgc.custom_svc_time_support_mode, wdc.gb_svc_time_support_mode, wdc.svc_time_support_mode) as gb_svc_time_support_mode\n" + + " from wvp_device_channel wdc" + + " left join wvp_platform_channel wpgc on wdc.id = wpgc.device_channel_id" + ; + + public String queryByDeviceId(Map params ){ + return BASE_SQL + " where channel_type = 0 and coalesce(gb_device_id, device_id) = #{gbDeviceId}"; + } + + public String queryById(Map params ){ + return BASE_SQL + " where channel_type = 0 and id = #{gbId}"; + } + + public String queryByStreamPushId(Map params ){ + return BASE_SQL + " where channel_type = 0 and stream_push_id = #{streamPushId}"; + } + + public String queryByStreamProxyId(Map params ){ + return BASE_SQL + " where channel_type = 0 and stream_proxy_id = #{streamProxyId}"; + } + + + public String queryList(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL); + sqlBuild.append(" where channel_type = 0 "); + if (params.get("query") != null) { + sqlBuild.append(" AND (coalesce(gb_device_id, device_id) LIKE concat('%',#{query},'%')" + + " OR coalesce(gb_name, name) LIKE concat('%',#{query},'%') )") + ; + } + if (params.get("online") != null && (Boolean)params.get("online")) { + sqlBuild.append(" AND coalesce(gb_status, status) = 'ON'"); + } + if (params.get("online") != null && !(Boolean)params.get("online")) { + sqlBuild.append(" AND coalesce(gb_status, status) = 'OFF'"); + } + if (params.get("hasCivilCode") != null && (Boolean)params.get("hasCivilCode")) { + sqlBuild.append(" AND coalesce(gb_civil_code, civil_code) is not null"); + } + if (params.get("hasCivilCode") != null && !(Boolean)params.get("hasCivilCode")) { + sqlBuild.append(" AND coalesce(gb_civil_code, civil_code) is null"); + } + if (params.get("hasGroup") != null && (Boolean)params.get("hasGroup")) { + sqlBuild.append(" AND coalesce(gb_parent_id, parent_id) is not null"); + } + if (params.get("hasGroup") != null && !(Boolean)params.get("hasGroup")) { + sqlBuild.append(" AND coalesce(gb_parent_id, parent_id) is null"); + } + return sqlBuild.toString(); + } + + public String queryInListByStatus(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL); + sqlBuild.append("where channel_type = 0 and gb_status=#{status} and id in ( "); + + List commonGBChannelList = (List)params.get("commonGBChannelList"); + boolean first = true; + for (CommonGBChannel channel : commonGBChannelList) { + if (!first) { + sqlBuild.append(","); + } + sqlBuild.append(channel.getGbId()); + first = false; + } + sqlBuild.append(" )"); + return sqlBuild.toString() ; + } + + public String queryByIds(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL); + sqlBuild.append("where channel_type = 0 and id in ( "); + + Collection ids = (Collection)params.get("ids"); + boolean first = true; + for (Integer id : ids) { + if (!first) { + sqlBuild.append(","); + } + sqlBuild.append(id); + first = false; + } + sqlBuild.append(" )"); + return sqlBuild.toString() ; + } + + public String queryByGbDeviceIds(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL); + sqlBuild.append("where channel_type = 0 and device_db_id in ( "); + + Collection ids = (Collection)params.get("deviceIds"); + boolean first = true; + for (Integer id : ids) { + if (!first) { + sqlBuild.append(","); + } + sqlBuild.append(id); + first = false; + } + sqlBuild.append(" )"); + return sqlBuild.toString() ; + } + + public String queryByDeviceIds(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL); + sqlBuild.append("where channel_type = 0 and id in ( "); + + Collection ids = (Collection)params.get("deviceIds"); + boolean first = true; + for (Integer id : ids) { + if (!first) { + sqlBuild.append(","); + } + sqlBuild.append(id); + first = false; + } + sqlBuild.append(" )"); + return sqlBuild.toString() ; + } + + public String queryByIdsOrCivilCode(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL); + sqlBuild.append("where channel_type = 0 and "); + if (params.get("civilCode") != null) { + sqlBuild.append(" coalesce(gb_civil_code, civil_code) = #{civilCode} "); + if (params.get("ids") != null) { + sqlBuild.append(" OR "); + } + } + if (params.get("ids") != null) { + sqlBuild.append(" id in ( "); + Collection ids = (Collection)params.get("ids"); + boolean first = true; + for (Integer id : ids) { + if (!first) { + sqlBuild.append(","); + } + sqlBuild.append(id); + first = false; + } + sqlBuild.append(" )"); + } + return sqlBuild.toString() ; + } + + public String queryByCivilCode(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL); + sqlBuild.append("where channel_type = 0 and coalesce(gb_civil_code, civil_code) = #{civilCode} "); + return sqlBuild.toString(); + } + + public String queryByBusinessGroup(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL); + sqlBuild.append("where channel_type = 0 and coalesce(gb_business_group_id, business_group_id) = #{businessGroup} "); + return sqlBuild.toString() ; + } + + public String queryByParentId(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL); + sqlBuild.append("where channel_type = 0 and gb_parent_id = #{parentId} "); + return sqlBuild.toString() ; + } + + public String queryByGroupList(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL); + + sqlBuild.append(" where channel_type = 0 and gb_parent_id in ( "); + Collection ids = (Collection)params.get("groupList"); + boolean first = true; + for (Group group : ids) { + if (!first) { + sqlBuild.append(","); + } + sqlBuild.append(group.getDeviceId()); + first = false; + } + sqlBuild.append(" )"); + + return sqlBuild.toString() ; + } + + public String queryListByStreamPushList(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL); + + sqlBuild.append(" where channel_type = 0 and stream_push_id in ( "); + Collection ids = (Collection)params.get("streamPushList"); + boolean first = true; + for (StreamPush streamPush : ids) { + if (!first) { + sqlBuild.append(","); + } + sqlBuild.append(streamPush.getId()); + first = false; + } + sqlBuild.append(" )"); + + return sqlBuild.toString() ; + } + + public String queryWithPlatform(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL_FOR_PLATFORM); + sqlBuild.append(" where wpgc.platform_id = #{platformId}"); + return sqlBuild.toString() ; + } + + public String queryShareChannelByParentId(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL_FOR_PLATFORM); + sqlBuild.append(" where wpgc.platform_id = #{platformId} and coalesce(wpgc.custom_parent_id, wdc.gb_parent_id, wdc.parent_id) = #{parentId}"); + return sqlBuild.toString() ; + } + + public String queryShareChannelByCivilCode(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL_FOR_PLATFORM); + sqlBuild.append(" where wpgc.platform_id = #{platformId} and coalesce(wpgc.custom_civil_code, wdc.gb_civil_code, wdc.civil_code) = #{civilCode}"); + return sqlBuild.toString() ; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/provider/DeviceChannelProvider.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/provider/DeviceChannelProvider.java new file mode 100644 index 000000000..e21b7729d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/provider/DeviceChannelProvider.java @@ -0,0 +1,175 @@ +package com.genersoft.iot.vmp.gb28181.dao.provider; + +import java.util.List; +import java.util.Map; + +public class DeviceChannelProvider { + + public String getBaseSelectSql(){ + return "SELECT " + + " dc.id,\n" + + " dc.device_db_id,\n" + + " dc.create_time,\n" + + " dc.update_time,\n" + + " dc.sub_count,\n" + + " dc.stream_id,\n" + + " dc.has_audio,\n" + + " dc.gps_time,\n" + + " dc.stream_identification,\n" + + " dc.channel_type,\n" + + " coalesce(dc.gb_device_id, dc.device_id) as device_id,\n" + + " coalesce(dc.gb_name, dc.name) as name,\n" + + " coalesce(dc.gb_manufacturer, dc.manufacturer) as manufacturer,\n" + + " coalesce(dc.gb_model, dc.model) as model,\n" + + " coalesce(dc.gb_owner, dc.owner) as owner,\n" + + " coalesce(dc.gb_civil_code, dc.civil_code) as civil_code,\n" + + " coalesce(dc.gb_block, dc.block) as block,\n" + + " coalesce(dc.gb_address, dc.address) as address,\n" + + " coalesce(dc.gb_parental, dc.parental) as parental,\n" + + " coalesce(dc.gb_parent_id, dc.parent_id) as parent_id,\n" + + " coalesce(dc.gb_safety_way, dc.safety_way) as safety_way,\n" + + " coalesce(dc.gb_register_way, dc.register_way) as register_way,\n" + + " coalesce(dc.gb_cert_num, dc.cert_num) as cert_num,\n" + + " coalesce(dc.gb_certifiable, dc.certifiable) as certifiable,\n" + + " coalesce(dc.gb_err_code, dc.err_code) as err_code,\n" + + " coalesce(dc.gb_end_time, dc.end_time) as end_time,\n" + + " coalesce(dc.gb_secrecy, dc.secrecy) as secrecy,\n" + + " coalesce(dc.gb_ip_address, dc.ip_address) as ip_address,\n" + + " coalesce(dc.gb_port, dc.port) as port,\n" + + " coalesce(dc.gb_password, dc.password) as password,\n" + + " coalesce(dc.gb_status, dc.status) as status,\n" + + " coalesce(dc.gb_longitude, dc.longitude) as longitude,\n" + + " coalesce(dc.gb_latitude, dc.latitude) as latitude,\n" + + " coalesce(dc.gb_ptz_type, dc.ptz_type) as ptz_type,\n" + + " coalesce(dc.gb_position_type, dc.position_type) as position_type,\n" + + " coalesce(dc.gb_room_type, dc.room_type) as room_type,\n" + + " coalesce(dc.gb_use_type, dc.use_type) as use_type,\n" + + " coalesce(dc.gb_supply_light_type, dc.supply_light_type) as supply_light_type,\n" + + " coalesce(dc.gb_direction_type, dc.direction_type) as direction_type,\n" + + " coalesce(dc.gb_resolution, dc.resolution) as resolution,\n" + + " coalesce(dc.gb_business_group_id, dc.business_group_id) as business_group_id,\n" + + " coalesce(dc.gb_download_speed, dc.download_speed) as download_speed,\n" + + " coalesce(dc.gb_svc_space_support_mod, dc.svc_space_support_mod) as svc_space_support_mod,\n" + + " coalesce(dc.gb_svc_time_support_mode,dc.svc_time_support_mode) as svc_time_support_mode\n" + + " from " + + " wvp_device_channel dc " + ; + } + public String queryChannels(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(getBaseSelectSql()); + sqlBuild.append(" where dc.device_db_id = #{deviceDbId}"); + if (params.get("query") != null) { + sqlBuild.append(" AND coalesce(dc.gb_device_id, dc.device_id) LIKE concat('%',#{query},'%')" + + " OR coalesce(dc.gb_name, dc.name) LIKE concat('%',#{query},'%')") + ; + } + if (params.get("parentChannelId") != null ) { + sqlBuild.append(" AND (dc.parent_id=#{parentChannelId} OR coalesce(dc.gb_civil_code, dc.civil_code) = #{parentChannelId})"); + } + if (params.get("online") != null && (Boolean)params.get("online")) { + sqlBuild.append(" AND coalesce(gb_status, status) = 'ON'"); + } + if (params.get("online") != null && !(Boolean)params.get("online")) { + sqlBuild.append(" AND coalesce(gb_status, status) = 'OFF'"); + } + if (params.get("hasSubChannel") != null && (Boolean)params.get("hasSubChannel")) { + sqlBuild.append(" AND dc.sub_count > 0"); + } + if (params.get("hasSubChannel") != null && !(Boolean)params.get("hasSubChannel")) { + sqlBuild.append(" AND dc.sub_count = 0"); + } + List channelIds = (List)params.get("channelIds"); + if (channelIds != null && !channelIds.isEmpty()) { + sqlBuild.append(" AND dc.device_id in ("); + boolean first = true; + for (String id : channelIds) { + if (!first) { + sqlBuild.append(","); + } + sqlBuild.append(id); + first = false; + } + sqlBuild.append(" )"); + } + return sqlBuild.toString(); + } + + + public String queryChannelsByDeviceDbId(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(getBaseSelectSql()); + sqlBuild.append(" where dc.device_db_id = #{deviceDbId}"); + return sqlBuild.toString(); + } + + public String queryAllChannels(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(getBaseSelectSql()); + sqlBuild.append(" where dc.device_db_id = #{deviceDbId}"); + return sqlBuild.toString(); + } + + public String getOne(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(getBaseSelectSql()); + sqlBuild.append(" where dc.id=#{id}"); + return sqlBuild.toString(); + } + + public String getOneByDeviceId(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(getBaseSelectSql()); + sqlBuild.append(" where dc.device_db_id=#{deviceDbId} and coalesce(dc.gb_device_id, dc.device_id) = #{channelId}"); + return sqlBuild.toString(); + } + + + + public String queryByDeviceId(Map params ){ + return getBaseSelectSql() + " where channel_type = 0 and coalesce(gb_device_id, device_id) = #{gbDeviceId}"; + } + + public String queryById(Map params ){ + return getBaseSelectSql() + " where channel_type = 0 and id = #{gbId}"; + } + + public String queryByStreamPushId(Map params ){ + return getBaseSelectSql() + " where channel_type = 0 and stream_push_id = #{streamPushId}"; + } + + public String queryByStreamProxyId(Map params ){ + return getBaseSelectSql() + " where channel_type = 0 and stream_proxy_id = #{streamProxyId}"; + } + + + public String queryList(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(getBaseSelectSql()); + sqlBuild.append(" where channel_type = 0 "); + if (params.get("query") != null) { + sqlBuild.append(" AND (coalesce(gb_device_id, device_id) LIKE concat('%',#{query},'%')" + + " OR coalesce(gb_name, name) LIKE concat('%',#{query},'%') )") + ; + } + if (params.get("online") != null && (Boolean)params.get("online")) { + sqlBuild.append(" AND coalesce(gb_status, status) = 'ON'"); + } + if (params.get("online") != null && !(Boolean)params.get("online")) { + sqlBuild.append(" AND coalesce(gb_status, status) = 'OFF'"); + } + if (params.get("hasCivilCode") != null && (Boolean)params.get("hasCivilCode")) { + sqlBuild.append(" AND coalesce(gb_civil_code, civil_code) is not null"); + } + if (params.get("hasCivilCode") != null && !(Boolean)params.get("hasCivilCode")) { + sqlBuild.append(" AND coalesce(gb_civil_code, civil_code) is null"); + } + if (params.get("hasGroup") != null && (Boolean)params.get("hasGroup")) { + sqlBuild.append(" AND coalesce(gb_parent_id, parent_id) is not null"); + } + if (params.get("hasGroup") != null && !(Boolean)params.get("hasGroup")) { + sqlBuild.append(" AND coalesce(gb_parent_id, parent_id) is null"); + } + return sqlBuild.toString(); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java index b87684a87..8355f2a99 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java @@ -52,8 +52,8 @@ public class EventPublisher { } - public void catalogEventPublish(String platformId, DeviceChannel deviceChannel, String type) { - List deviceChannelList = new ArrayList<>(); + public void catalogEventPublish(Integer platformId, CommonGBChannel deviceChannel, String type) { + List deviceChannelList = new ArrayList<>(); deviceChannelList.add(deviceChannel); catalogEventPublish(platformId, deviceChannelList, type); } @@ -72,22 +72,22 @@ public class EventPublisher { * @param deviceChannels * @param type */ - public void catalogEventPublish(String platformId, List deviceChannels, String type) { + public void catalogEventPublish(Integer platformId, List deviceChannels, String type) { CatalogEvent outEvent = new CatalogEvent(this); - List channels = new ArrayList<>(); + List channels = new ArrayList<>(); if (deviceChannels.size() > 1) { // 数据去重 Set gbIdSet = new HashSet<>(); - for (DeviceChannel deviceChannel : deviceChannels) { - if (deviceChannel != null && deviceChannel.getChannelId() != null && !gbIdSet.contains(deviceChannel.getChannelId())) { - gbIdSet.add(deviceChannel.getChannelId()); + for (CommonGBChannel deviceChannel : deviceChannels) { + if (deviceChannel != null && deviceChannel.getGbDeviceId() != null && !gbIdSet.contains(deviceChannel.getGbDeviceId())) { + gbIdSet.add(deviceChannel.getGbDeviceId()); channels.add(deviceChannel); } } }else { channels = deviceChannels; } - outEvent.setDeviceChannels(channels); + outEvent.setChannels(channels); outEvent.setType(type); outEvent.setPlatformId(platformId); applicationEventPublisher.publishEvent(outEvent); @@ -100,26 +100,9 @@ public class EventPublisher { applicationEventPublisher.publishEvent(event); } - - public void catalogEventPublishForStream(String platformId, List gbStreams, String type) { - CatalogEvent outEvent = new CatalogEvent(this); - outEvent.setGbStreams(gbStreams); - outEvent.setType(type); - outEvent.setPlatformId(platformId); - applicationEventPublisher.publishEvent(outEvent); - } - - - public void catalogEventPublishForStream(String platformId, GbStream gbStream, String type) { - List gbStreamList = new ArrayList<>(); - gbStreamList.add(gbStream); - catalogEventPublishForStream(platformId, gbStreamList, type); - } - public void recordEndEventPush(RecordInfo recordInfo) { RecordEndEvent outEvent = new RecordEndEvent(this); outEvent.setRecordInfo(recordInfo); applicationEventPublisher.publishEvent(outEvent); } - } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java index e07761a91..455be8df0 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java @@ -1,11 +1,11 @@ package com.genersoft.iot.vmp.gb28181.event; import com.genersoft.iot.vmp.gb28181.bean.DeviceNotFoundEvent; +import com.genersoft.iot.vmp.gb28181.event.sip.SipEvent; import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPResponse; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -14,51 +14,48 @@ import javax.sip.ResponseEvent; import javax.sip.TimeoutEvent; import javax.sip.TransactionTerminatedEvent; import javax.sip.header.WarningHeader; -import java.time.Instant; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.DelayQueue; /** * @author lin */ +@Slf4j @Component public class SipSubscribe { - private final Logger logger = LoggerFactory.getLogger(SipSubscribe.class); + private final Map subscribes = new ConcurrentHashMap<>(); - private Map errorSubscribes = new ConcurrentHashMap<>(); + private final DelayQueue delayQueue = new DelayQueue<>(); - private Map okSubscribes = new ConcurrentHashMap<>(); - - private Map okTimeSubscribes = new ConcurrentHashMap<>(); - - private Map errorTimeSubscribes = new ConcurrentHashMap<>(); - - // @Scheduled(cron="*/5 * * * * ?") //每五秒执行一次 - // @Scheduled(fixedRate= 100 * 60 * 60 ) - @Scheduled(cron="0 0/5 * * * ?") //每5分钟执行一次 + @Scheduled(fixedRate = 200) //每200毫秒执行 public void execute(){ - logger.info("[定时任务] 清理过期的SIP订阅信息"); - - Instant instant = Instant.now().minusMillis(TimeUnit.MINUTES.toMillis(5)); - - for (String key : okTimeSubscribes.keySet()) { - if (okTimeSubscribes.get(key).isBefore(instant)){ - okSubscribes.remove(key); - okTimeSubscribes.remove(key); - } + if (delayQueue.isEmpty()) { + return; } - for (String key : errorTimeSubscribes.keySet()) { - if (errorTimeSubscribes.get(key).isBefore(instant)){ - errorSubscribes.remove(key); - errorTimeSubscribes.remove(key); + try { + SipEvent take = delayQueue.take(); + // 出现超时异常 + if(take.getErrorEvent() != null) { + EventResult eventResult = new EventResult<>(); + eventResult.type = EventResultType.timeout; + eventResult.msg = "消息超时未回复"; + eventResult.statusCode = -1024; + take.getErrorEvent().response(eventResult); } + subscribes.remove(take.getKey()); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + public void updateTimeout(String callId) { + SipEvent sipEvent = subscribes.get(callId); + if (sipEvent != null) { + delayQueue.remove(sipEvent); + delayQueue.offer(sipEvent); } - logger.debug("okTimeSubscribes.size:{}",okTimeSubscribes.size()); - logger.debug("okSubscribes.size:{}",okSubscribes.size()); - logger.debug("errorTimeSubscribes.size:{}",errorTimeSubscribes.size()); - logger.debug("errorSubscribes.size:{}",errorSubscribes.size()); } public interface Event { void response(EventResult eventResult); @@ -154,43 +151,33 @@ public class SipSubscribe { } } - public void addErrorSubscribe(String key, SipSubscribe.Event event) { - errorSubscribes.put(key, event); - errorTimeSubscribes.put(key, Instant.now()); + + public void addSubscribe(String key, SipEvent event) { + SipEvent sipEvent = subscribes.get(key); + if (sipEvent != null) { + subscribes.remove(key); + delayQueue.remove(sipEvent); + } + subscribes.put(key, event); + delayQueue.offer(event); } - public void addOkSubscribe(String key, SipSubscribe.Event event) { - okSubscribes.put(key, event); - okTimeSubscribes.put(key, Instant.now()); + public SipEvent getSubscribe(String key) { + return subscribes.get(key); } - public SipSubscribe.Event getErrorSubscribe(String key) { - return errorSubscribes.get(key); - } - - public void removeErrorSubscribe(String key) { + public void removeSubscribe(String key) { if(key == null){ return; } - errorSubscribes.remove(key); - errorTimeSubscribes.remove(key); - } - - public SipSubscribe.Event getOkSubscribe(String key) { - return okSubscribes.get(key); - } - - public void removeOkSubscribe(String key) { - if(key == null){ - return; + SipEvent sipEvent = subscribes.get(key); + if (sipEvent != null) { + subscribes.remove(key); + delayQueue.remove(sipEvent); } - okSubscribes.remove(key); - okTimeSubscribes.remove(key); } - public int getErrorSubscribesSize(){ - return errorSubscribes.size(); - } - public int getOkSubscribesSize(){ - return okSubscribes.size(); + + public boolean isEmpty(){ + return subscribes.isEmpty(); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java index aef590763..317683d61 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java @@ -1,8 +1,7 @@ package com.genersoft.iot.vmp.gb28181.event.alarm; +import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @@ -18,27 +17,26 @@ import java.util.concurrent.ConcurrentHashMap; * @author xiaoQQya * @since 2021/01/20 */ +@Slf4j @Component public class AlarmEventListener implements ApplicationListener { - private static final Logger logger = LoggerFactory.getLogger(AlarmEventListener.class); - private static final Map SSE_CACHE = new ConcurrentHashMap<>(); public void addSseEmitter(String browserId, PrintWriter writer) { SSE_CACHE.put(browserId, writer); - logger.info("SSE 在线数量: {}", SSE_CACHE.size()); + log.info("SSE 在线数量: {}", SSE_CACHE.size()); } public void removeSseEmitter(String browserId, PrintWriter writer) { SSE_CACHE.remove(browserId, writer); - logger.info("SSE 在线数量: {}", SSE_CACHE.size()); + log.info("SSE 在线数量: {}", SSE_CACHE.size()); } @Override public void onApplicationEvent(@NotNull AlarmEvent event) { - if (logger.isDebugEnabled()) { - logger.debug("设备报警事件触发, deviceId: {}, {}", event.getAlarmInfo().getDeviceId(), event.getAlarmInfo().getAlarmDescription()); + if (log.isDebugEnabled()) { + log.debug("设备报警事件触发, deviceId: {}, {}", event.getAlarmInfo().getDeviceId(), event.getAlarmInfo().getAlarmDescription()); } String msg = "设备编号: " + event.getAlarmInfo().getDeviceId() + "" @@ -48,7 +46,7 @@ public class AlarmEventListener implements ApplicationListener { for (Iterator> it = SSE_CACHE.entrySet().iterator(); it.hasNext(); ) { Map.Entry response = it.next(); - logger.info("推送到 SSE 连接, 浏览器 ID: {}", response.getKey()); + log.info("推送到 SSE 连接, 浏览器 ID: {}", response.getKey()); try { PrintWriter writer = response.getValue(); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java index 2008f22e3..5f83e6e9b 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java @@ -1,7 +1,7 @@ package com.genersoft.iot.vmp.gb28181.event.device; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java index e6cfac3d5..411b54d5c 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java @@ -1,10 +1,7 @@ package com.genersoft.iot.vmp.gb28181.event.record; import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @@ -16,12 +13,10 @@ import java.util.concurrent.ConcurrentHashMap; * @author: pan * @data: 2022-02-23 */ - +@Slf4j @Component public class RecordEndEventListener implements ApplicationListener { - private final static Logger logger = LoggerFactory.getLogger(RecordEndEventListener.class); - private Map handlerMap = new ConcurrentHashMap<>(); public interface RecordEndEventHandler{ void handler(RecordInfo recordInfo); @@ -33,11 +28,11 @@ public class RecordEndEventListener implements ApplicationListener deviceChannels; - private List gbStreams; + @Setter + @Getter + private List channels; + + @Setter + @Getter private String type; - private String platformId; - public List getDeviceChannels() { - return deviceChannels; - } + @Setter + @Getter + private Integer platformId; - public void setDeviceChannels(List deviceChannels) { - this.deviceChannels = deviceChannels; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getPlatformId() { - return platformId; - } - - public void setPlatformId(String platformId) { - this.platformId = platformId; - } - - public List getGbStreams() { - return gbStreams; - } - - public void setGbStreams(List gbStreams) { - this.gbStreams = gbStreams; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java index 18ad2b011..28a422648 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java @@ -1,16 +1,16 @@ package com.genersoft.iot.vmp.gb28181.event.subscribe.catalog; -import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.bean.*; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; -import com.genersoft.iot.vmp.service.IGbStreamService; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder; +import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo; +import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService; +import com.genersoft.iot.vmp.gb28181.service.IPlatformService; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; -import org.springframework.util.ObjectUtils; import javax.sip.InvalidArgumentException; import javax.sip.SipException; @@ -23,62 +23,49 @@ import java.util.Map; /** * catalog事件 */ +@Slf4j @Component public class CatalogEventLister implements ApplicationListener { - private final static Logger logger = LoggerFactory.getLogger(CatalogEventLister.class); + @Autowired + private IPlatformChannelService platformChannelService; @Autowired - private IVideoManagerStorage storager; + private IPlatformService platformService; @Autowired - private SIPCommanderFroPlatform sipCommanderFroPlatform; - - @Autowired - private IGbStreamService gbStreamService; + private ISIPCommanderForPlatform sipCommanderFroPlatform; @Autowired private SubscribeHolder subscribeHolder; - @Autowired - private UserSetting userSetting; - @Override public void onApplicationEvent(CatalogEvent event) { SubscribeInfo subscribe = null; - ParentPlatform parentPlatform = null; + Platform parentPlatform = null; - Map> parentPlatformMap = new HashMap<>(); - Map channelMap = new HashMap<>(); - if (!ObjectUtils.isEmpty(event.getPlatformId())) { - subscribe = subscribeHolder.getCatalogSubscribe(event.getPlatformId()); - if (subscribe == null) { + Map> parentPlatformMap = new HashMap<>(); + Map channelMap = new HashMap<>(); + if (event.getPlatformId() != null) { + parentPlatform = platformService.queryOne(event.getPlatformId()); + if (parentPlatform == null) { return; } - parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformId()); - if (parentPlatform != null && !parentPlatform.isStatus()) { + subscribe = subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId()); + if (subscribe == null) { return; } }else { // 获取所用订阅 List platforms = subscribeHolder.getAllCatalogSubscribePlatform(); - if (event.getDeviceChannels() != null) { - if (platforms.size() > 0) { - for (DeviceChannel deviceChannel : event.getDeviceChannels()) { - List parentPlatformsForGB = storager.queryPlatFormListForGBWithGBId(deviceChannel.getChannelId(), platforms); - parentPlatformMap.put(deviceChannel.getChannelId(), parentPlatformsForGB); - channelMap.put(deviceChannel.getChannelId(), deviceChannel); - } - } - }else if (event.getGbStreams() != null) { - if (platforms.size() > 0) { - for (GbStream gbStream : event.getGbStreams()) { - if (gbStream == null || ObjectUtils.isEmpty(gbStream.getGbId())) { - continue; - } - List parentPlatformsForGB = storager.queryPlatFormListForStreamWithGBId(gbStream.getApp(),gbStream.getStream(), platforms); - parentPlatformMap.put(gbStream.getGbId(), parentPlatformsForGB); + if (event.getChannels() != null) { + if (!platforms.isEmpty()) { + for (CommonGBChannel deviceChannel : event.getChannels()) { + List parentPlatformsForGB = platformChannelService.queryPlatFormListByChannelDeviceId( + deviceChannel.getGbId(), platforms); + parentPlatformMap.put(deviceChannel.getGbDeviceId(), parentPlatformsForGB); + channelMap.put(deviceChannel.getGbDeviceId(), deviceChannel); } } } @@ -88,51 +75,39 @@ public class CatalogEventLister implements ApplicationListener { case CatalogEvent.OFF: case CatalogEvent.DEL: - if (parentPlatform != null || subscribe != null) { - List deviceChannelList = new ArrayList<>(); - if (event.getDeviceChannels() != null) { - deviceChannelList.addAll(event.getDeviceChannels()); + if (parentPlatform != null) { + List deviceChannelList = new ArrayList<>(); + if (event.getChannels() != null) { + deviceChannelList.addAll(event.getChannels()); } - if (event.getGbStreams() != null && event.getGbStreams().size() > 0){ - for (GbStream gbStream : event.getGbStreams()) { - if (gbStream != null - && gbStream.getStreamType() != null - && gbStream.getStreamType().equals("push") - && !userSetting.isUsePushingAsStatus()) { - continue; - } - DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform); - deviceChannelList.add(deviceChannelByStream); - } - } - if (deviceChannelList.size() > 0) { - logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size()); + if (!deviceChannelList.isEmpty()) { + log.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), parentPlatform.getServerGBId(), deviceChannelList.size()); try { sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), parentPlatform, deviceChannelList, subscribe, null); } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | IllegalAccessException e) { - logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); } } - }else if (parentPlatformMap.keySet().size() > 0) { + }else if (!parentPlatformMap.keySet().isEmpty()) { for (String gbId : parentPlatformMap.keySet()) { - List parentPlatforms = parentPlatformMap.get(gbId); - if (parentPlatforms != null && parentPlatforms.size() > 0) { - for (ParentPlatform platform : parentPlatforms) { + List parentPlatforms = parentPlatformMap.get(gbId); + if (parentPlatforms != null && !parentPlatforms.isEmpty()) { + for (Platform platform : parentPlatforms) { SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId()); if (subscribeInfo == null) { continue; } - logger.info("[Catalog事件: {}]平台:{},影响通道{}", event.getType(), platform.getServerGBId(), gbId); - List deviceChannelList = new ArrayList<>(); - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setChannelId(gbId); + log.info("[Catalog事件: {}]平台:{},影响通道{}", event.getType(), platform.getServerGBId(), gbId); + List deviceChannelList = new ArrayList<>(); + CommonGBChannel deviceChannel = new CommonGBChannel(); + deviceChannel.setGbDeviceId(gbId); deviceChannelList.add(deviceChannel); try { sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), platform, deviceChannelList, subscribeInfo, null); } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | IllegalAccessException e) { - logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); } } } @@ -145,49 +120,38 @@ public class CatalogEventLister implements ApplicationListener { break; case CatalogEvent.ADD: case CatalogEvent.UPDATE: - if (parentPlatform != null || subscribe != null) { - List deviceChannelList = new ArrayList<>(); - if (event.getDeviceChannels() != null) { - deviceChannelList.addAll(event.getDeviceChannels()); + if (parentPlatform != null) { + List deviceChannelList = new ArrayList<>(); + if (event.getChannels() != null) { + deviceChannelList.addAll(event.getChannels()); } - if (event.getGbStreams() != null && !event.getGbStreams().isEmpty()){ - for (GbStream gbStream : event.getGbStreams()) { - deviceChannelList.add( - gbStreamService.getDeviceChannelListByStreamWithStatus(gbStream, gbStream.getCatalogId(), parentPlatform)); - } - } if (!deviceChannelList.isEmpty()) { - logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size()); + log.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), parentPlatform.getServerGBId(), deviceChannelList.size()); try { sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), parentPlatform, deviceChannelList, subscribe, null); } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | IllegalAccessException e) { - logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); } } }else if (!parentPlatformMap.keySet().isEmpty()) { for (String gbId : parentPlatformMap.keySet()) { - List parentPlatforms = parentPlatformMap.get(gbId); + List parentPlatforms = parentPlatformMap.get(gbId); if (parentPlatforms != null && !parentPlatforms.isEmpty()) { - for (ParentPlatform platform : parentPlatforms) { + for (Platform platform : parentPlatforms) { SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId()); if (subscribeInfo == null) { continue; } - logger.info("[Catalog事件: {}]平台:{},影响通道{}", event.getType(), platform.getServerGBId(), gbId); - List deviceChannelList = new ArrayList<>(); - DeviceChannel deviceChannel = channelMap.get(gbId); - deviceChannelList.add(deviceChannel); - GbStream gbStream = storager.queryStreamInParentPlatform(platform.getServerGBId(), gbId); - if(gbStream != null){ - DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStreamWithStatus(gbStream, gbStream.getCatalogId(), platform); - deviceChannelList.add(deviceChannelByStream); - } + log.info("[Catalog事件: {}]平台:{},影响通道{}", event.getType(), platform.getServerGBId(), gbId); + List channelList = new ArrayList<>(); + CommonGBChannel deviceChannel = channelMap.get(gbId); + channelList.add(deviceChannel); try { - sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), platform, deviceChannelList, subscribeInfo, null); + sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), platform, channelList, subscribeInfo, null); } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | IllegalAccessException e) { - logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/mobilePosition/MobilePositionEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/mobilePosition/MobilePositionEvent.java index 068665175..f6a4ad759 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/mobilePosition/MobilePositionEvent.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/mobilePosition/MobilePositionEvent.java @@ -1,20 +1,17 @@ package com.genersoft.iot.vmp.gb28181.event.subscribe.mobilePosition; import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; +import lombok.Getter; +import lombok.Setter; import org.springframework.context.ApplicationEvent; + public class MobilePositionEvent extends ApplicationEvent { public MobilePositionEvent(Object source) { super(source); } + @Getter + @Setter private MobilePosition mobilePosition; - - public MobilePosition getMobilePosition() { - return mobilePosition; - } - - public void setMobilePosition(MobilePosition mobilePosition) { - this.mobilePosition = mobilePosition; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/mobilePosition/MobilePositionEventLister.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/mobilePosition/MobilePositionEventLister.java index 7a96116bd..40dc6c853 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/mobilePosition/MobilePositionEventLister.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/mobilePosition/MobilePositionEventLister.java @@ -1,13 +1,13 @@ package com.genersoft.iot.vmp.gb28181.event.subscribe.mobilePosition; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder; import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderForPlatform; import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @@ -20,42 +20,49 @@ import java.util.List; /** * 移动位置通知消息转发 */ +@Slf4j @Component public class MobilePositionEventLister implements ApplicationListener { - private final static Logger logger = LoggerFactory.getLogger(MobilePositionEventLister.class); + @Autowired + private IPlatformChannelService platformChannelService; @Autowired - private IVideoManagerStorage storager; - - @Autowired - private SIPCommanderFroPlatform sipCommanderFroPlatform; + private SIPCommanderForPlatform sipCommanderForPlatform; @Autowired private SubscribeHolder subscribeHolder; @Override public void onApplicationEvent(MobilePositionEvent event) { + if (event.getMobilePosition().getChannelId() == 0) { + return; + } + // 获取所用订阅 List platforms = subscribeHolder.getAllMobilePositionSubscribePlatform(); if (platforms.isEmpty()) { return; } - List parentPlatformsForGB = storager.queryPlatFormListForGBWithGBId(event.getMobilePosition().getChannelId(), platforms); + List platformsForGB = platformChannelService.queryPlatFormListByChannelDeviceId(event.getMobilePosition().getChannelId(), platforms); - for (ParentPlatform platform : parentPlatformsForGB) { - logger.info("[向上级发送MobilePosition] 通道:{},平台:{}, 位置: {}:{}", event.getMobilePosition().getChannelId(), - platform.getServerGBId(), event.getMobilePosition().getLongitude(), event.getMobilePosition().getLatitude()); + for (Platform platform : platformsForGB) { + if (log.isDebugEnabled()){ + log.debug("[向上级发送MobilePosition] 通道:{},平台:{}, 位置: {}:{}", event.getMobilePosition().getChannelId(), + platform.getServerGBId(), event.getMobilePosition().getLongitude(), event.getMobilePosition().getLatitude()); + } SubscribeInfo subscribe = subscribeHolder.getMobilePositionSubscribe(platform.getServerGBId()); try { - sipCommanderFroPlatform.sendNotifyMobilePosition(platform, GPSMsgInfo.getInstance(event.getMobilePosition()), + GPSMsgInfo gpsMsgInfo = GPSMsgInfo.getInstance(event.getMobilePosition()); + // 获取通道编号 + CommonGBChannel commonGBChannel = platformChannelService.queryChannelByPlatformIdAndChannelId(platform.getId(), event.getMobilePosition().getChannelId()); + sipCommanderForPlatform.sendNotifyMobilePosition(platform, gpsMsgInfo, commonGBChannel, subscribe); } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | IllegalAccessException e) { - logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); } } - } } \ No newline at end of file diff --git a/src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/ICloudRecordService.java similarity index 97% rename from src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/service/ICloudRecordService.java index 25261db60..8f9e9ef8b 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/ICloudRecordService.java @@ -1,4 +1,4 @@ -package com.genersoft.iot.vmp.service; +package com.genersoft.iot.vmp.gb28181.service; import com.alibaba.fastjson2.JSONArray; import com.genersoft.iot.vmp.media.bean.MediaServer; diff --git a/src/main/java/com/genersoft/iot/vmp/service/IDeviceAlarmService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceAlarmService.java similarity index 96% rename from src/main/java/com/genersoft/iot/vmp/service/IDeviceAlarmService.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceAlarmService.java index 51e53d266..83716fa97 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/IDeviceAlarmService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceAlarmService.java @@ -1,4 +1,4 @@ -package com.genersoft.iot.vmp.service; +package com.genersoft.iot.vmp.gb28181.service; import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; import com.github.pagehelper.PageInfo; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceChannelService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceChannelService.java new file mode 100755 index 000000000..8b5a8abbd --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceChannelService.java @@ -0,0 +1,122 @@ +package com.genersoft.iot.vmp.gb28181.service; + +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; +import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelReduce; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; +import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; +import com.github.pagehelper.PageInfo; + +import java.util.List; + +/** + * 国标通道业务类 + * @author lin + */ +public interface IDeviceChannelService { + + /** + * 批量添加设备通道 + */ + int updateChannels(Device device, List channels); + + /** + * 获取统计信息 + * @return + */ + ResourceBaseInfo getOverview(); + + /** + * 查询所有未分配的通道 + * @param platformId + * @return + */ + List queryAllChannelList(String platformId); + + PageInfo queryAllChannelList(int page, int count, String query, Boolean online, Boolean channelType, String platformId, String catalogId); + + /** + * 查询通道所属的设备 + */ + List getDeviceByChannelId(String channelId); + + /** + * 批量删除通道 + * @param deleteChannelList 待删除的通道列表 + */ + int deleteChannelsForNotify(List deleteChannelList); + + int updateChannelsStatus(List channels); + + /** + * 获取一个通道 + */ + DeviceChannel getOne(String deviceId, String channelId); + + DeviceChannel getOneForSource(String deviceId, String channelId); + + /** + * 直接批量更新通道 + */ + void batchUpdateChannelForNotify(List channels); + + /** + * 直接批量添加 + */ + void batchAddChannel(List deviceChannels); + + /** + * 修改通道的码流类型 + */ + void updateChannelStreamIdentification(DeviceChannel channel); + + List queryChaneListByDeviceId(String deviceId); + + void updateChannelGPS(Device device, DeviceChannel deviceChannel, MobilePosition mobilePosition); + + void startPlay(Integer channelId, String stream); + + void stopPlay(Integer channelId); + + void batchUpdateChannelGPS(List channelList); + + void batchAddMobilePosition(List addMobilePositionList); + + void online(DeviceChannel channel); + + void offline(DeviceChannel channel); + + void delete(DeviceChannel channel); + + void cleanChannelsForDevice(int deviceId); + + boolean resetChannels(int deviceDbId, List deviceChannels); + + PageInfo getSubChannels(int deviceDbId, String channelId, String query, Boolean channelType, Boolean online, int page, int count); + + List queryChannelExtendsByDeviceId(String deviceId, List channelIds, Boolean online); + + PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean channelType, Boolean online, int page, int count); + + + List queryDeviceWithAsMessageChannel(); + + DeviceChannel getRawChannel(int id); + + DeviceChannel getOneById(Integer channelId); + + DeviceChannel getOneForSourceById(Integer channelId); + + DeviceChannel getBroadcastChannel(int deviceDbId); + + void changeAudio(Integer channelId, Boolean audio); + + void updateChannelStatus(DeviceChannel channel); + + void addChannel(DeviceChannel channel); + + void updateChannelForNotify(DeviceChannel channel); + + DeviceChannel getOneForSource(int deviceDbId, String channelId); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceService.java similarity index 82% rename from src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceService.java index afa004421..0812f8cfa 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceService.java @@ -1,12 +1,11 @@ -package com.genersoft.iot.vmp.service; +package com.genersoft.iot.vmp.gb28181.service; import com.genersoft.iot.vmp.common.CommonCallback; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; import com.genersoft.iot.vmp.gb28181.bean.SyncStatus; -import com.genersoft.iot.vmp.vmanager.bean.BaseTree; import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; +import com.github.pagehelper.PageInfo; import java.util.List; @@ -81,7 +80,7 @@ public interface IDeviceService { * @param deviceId 设备编号 * @return 设备信息 */ - Device getDevice(String deviceId); + Device getDeviceByDeviceId(String deviceId); /** * 获取所有在线设备 @@ -89,6 +88,8 @@ public interface IDeviceService { */ List getAllOnlineDevice(); + List getAllByStatus(Boolean status); + /** * 判断是否注册已经失效 * @param device 设备信息 @@ -116,23 +117,6 @@ public interface IDeviceService { */ void updateDevice(Device device); - /** - * 树形查询接口 - * @param deviceId 设备ID - * @param parentId 父ID - * @param onlyCatalog 只获取目录 - * @return - */ - List> queryVideoDeviceTree(String deviceId, String parentId, boolean onlyCatalog); - - /** - * 查询树节点下的通道 - * @param deviceId 设备ID - * @param parentId 父ID - * @return - */ - List queryVideoDeviceInTreeNode(String deviceId, String parentId); - /** * 检查设备编号是否已经存在 * @param deviceId 设备编号 @@ -169,4 +153,12 @@ public interface IDeviceService { * 获取所有设备 */ List getAll(); + + PageInfo getAll(int page, int count, String query, Boolean status); + + Device getDevice(Integer gbDeviceDbId); + + Device getDeviceByChannelId(Integer channelId); + + Device getDeviceBySourceChannelDeviceId(String requesterId); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelPlayService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelPlayService.java new file mode 100644 index 000000000..8abb01607 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelPlayService.java @@ -0,0 +1,18 @@ +package com.genersoft.iot.vmp.gb28181.service; + +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.InviteInfo; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.service.bean.ErrorCallback; + +public interface IGbChannelPlayService { + + void start(CommonGBChannel channel, InviteInfo inviteInfo, Platform platform, ErrorCallback callback); + + void playGbDeviceChannel(CommonGBChannel channel, ErrorCallback callback); + + void playProxy(CommonGBChannel channel, ErrorCallback callback); + + void playPush(CommonGBChannel channel, String platformDeviceId, String platformName, ErrorCallback callback); +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelService.java new file mode 100644 index 000000000..ac13ffa09 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelService.java @@ -0,0 +1,84 @@ +package com.genersoft.iot.vmp.gb28181.service; + +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.streamPush.bean.StreamPush; +import com.github.pagehelper.PageInfo; + +import java.util.Collection; +import java.util.List; + +public interface IGbChannelService { + + CommonGBChannel queryByDeviceId(String gbDeviceId); + + int add(CommonGBChannel commonGBChannel); + + int delete(int gbId); + + void delete(Collection ids); + + int update(CommonGBChannel commonGBChannel); + + int offline(CommonGBChannel commonGBChannel); + + int offline(List commonGBChannelList); + + int online(CommonGBChannel commonGBChannel); + + int online(List commonGBChannelList); + + void batchAdd(List commonGBChannels); + + void updateStatus(List channelList); + + CommonGBChannel getOne(int id); + + List getIndustryCodeList(); + + List getDeviceTypeList(); + + List getNetworkIdentificationTypeList(); + + void reset(int id); + + PageInfo queryList(int page, int count, String query, Boolean online, Boolean hasCivilCode, Boolean hasGroup); + + void removeCivilCode(List allChildren); + + void addChannelToRegion(String civilCode, List channelIds); + + void deleteChannelToRegion(String civilCode, List channelIds); + + void deleteChannelToRegionByCivilCode(String civilCode); + + void deleteChannelToRegionByChannelIds(List channelIds); + + void addChannelToRegionByGbDevice(String civilCode, List deviceIds); + + void deleteChannelToRegionByGbDevice(List deviceIds); + + void removeParentIdByBusinessGroup(String businessGroup); + + void removeParentIdByGroupList(List groupList); + + void updateBusinessGroup(String oldBusinessGroup, String newBusinessGroup); + + void updateParentIdGroup(String oldParentId, String newParentId); + + void addChannelToGroup(String parentId, String businessGroup, List channelIds); + + void deleteChannelToGroup(String parentId, String businessGroup, List channelIds); + + void addChannelToGroupByGbDevice(String parentId, String businessGroup, List deviceIds); + + void deleteChannelToGroupByGbDevice(List deviceIds); + + void batchUpdate(List commonGBChannels); + + CommonGBChannel queryOneWithPlatform(Integer platformId, String channelDeviceId); + + void updateCivilCode(String oldCivilCode, String newCivilCode); + + List queryListByStreamPushList(List streamPushList); + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGroupService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGroupService.java new file mode 100644 index 000000000..a3265bd6d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGroupService.java @@ -0,0 +1,24 @@ +package com.genersoft.iot.vmp.gb28181.service; + +import com.genersoft.iot.vmp.gb28181.bean.Group; +import com.genersoft.iot.vmp.gb28181.bean.GroupTree; + +import java.util.List; + + +public interface IGroupService { + + void add(Group group); + + void update(Group group); + + Group queryGroupByDeviceId(String regionDeviceId); + + List queryForTree(String query, Integer parent); + + void syncFromChannel(); + + boolean delete(int id); + + boolean batchAdd(List groupList); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IInviteStreamService.java similarity index 66% rename from src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/service/IInviteStreamService.java index e54fa92a3..9903d59bc 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IInviteStreamService.java @@ -1,9 +1,12 @@ -package com.genersoft.iot.vmp.service; +package com.genersoft.iot.vmp.gb28181.service; 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.service.bean.ErrorCallback; +import java.util.List; + /** * 记录国标点播的状态,包括实时预览,下载,录像回放 */ @@ -21,18 +24,12 @@ public interface IInviteStreamService { /** * 获取点播的状态信息 */ - InviteInfo getInviteInfo(InviteSessionType type, - String deviceId, - String channelId, - String stream); + InviteInfo getInviteInfo(InviteSessionType type, Integer channelId, String stream); /** * 移除点播的状态信息 */ - void removeInviteInfo(InviteSessionType type, - String deviceId, - String channelId, - String stream); + void removeInviteInfo(InviteSessionType type, Integer channelId, String stream); /** * 移除点播的状态信息 */ @@ -40,14 +37,14 @@ public interface IInviteStreamService { /** * 移除点播的状态信息 */ - void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId); + void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, Integer channelId); + + List getAllInviteInfo(InviteSessionType type, Integer channelId, String stream); /** * 获取点播的状态信息 */ - InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, - String deviceId, - String channelId); + InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, Integer channelId); /** * 获取点播的状态信息 @@ -58,12 +55,12 @@ public interface IInviteStreamService { /** * 添加一个invite回调 */ - void once(InviteSessionType type, String deviceId, String channelId, String stream, ErrorCallback callback); + void once(InviteSessionType type, Integer channelId, String stream, ErrorCallback callback); /** * 调用一个invite回调 */ - void call(InviteSessionType type, String deviceId, String channelId, String stream, int code, String msg, Object data); + void call(InviteSessionType type, Integer channelId, String stream, int code, String msg, StreamInfo data); /** * 清空一个设备的所有invite信息 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlatformChannelService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlatformChannelService.java new file mode 100755 index 000000000..c862b6653 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlatformChannelService.java @@ -0,0 +1,48 @@ +package com.genersoft.iot.vmp.gb28181.service; + +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.Group; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.bean.PlatformChannel; +import com.github.pagehelper.PageInfo; + +import java.util.List; + +/** + * 平台关联通道管理 + * @author lin + */ +public interface IPlatformChannelService { + + PageInfo queryChannelList(int page, int count, String query, Integer channelType, Boolean online, Integer platformId, Boolean hasShare); + + int addAllChannel(Integer platformId); + + int removeAllChannel(Integer platformId); + + int addChannels(Integer platformId, List channelIds); + + int removeChannels(Integer platformId, List channelIds); + + void removeChannels(List ids); + + void removeChannel(int gbId); + + List queryByPlatform(Platform platform); + + void pushChannel(Integer platformId); + + void addChannelByDevice(Integer platformId, List deviceIds); + + void removeChannelByDevice(Integer platformId, List deviceIds); + + void updateCustomChannel(PlatformChannel channel); + + void checkGroupRemove(List channelList, List groups); + + void checkGroupAdd(List channelList); + + List queryPlatFormListByChannelDeviceId(Integer channelId, List platforms); + + CommonGBChannel queryChannelByPlatformIdAndChannelId(Integer platformId, Integer channelId); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlatformService.java similarity index 59% rename from src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlatformService.java index 79bdf78c9..6c43dcf5c 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlatformService.java @@ -1,17 +1,19 @@ -package com.genersoft.iot.vmp.service; +package com.genersoft.iot.vmp.gb28181.service; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; -import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; -import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback; +import com.genersoft.iot.vmp.common.CommonCallback; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; +import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback; import com.github.pagehelper.PageInfo; import javax.sip.InvalidArgumentException; import javax.sip.SipException; import java.text.ParseException; +import java.util.List; /** * 国标平台的业务类 @@ -19,7 +21,7 @@ import java.text.ParseException; */ public interface IPlatformService { - ParentPlatform queryPlatformByServerGBId(String platformGbId); + Platform queryPlatformByServerGBId(String platformGbId); /** * 分页获取上级平台 @@ -27,37 +29,37 @@ public interface IPlatformService { * @param count * @return */ - PageInfo queryParentPlatformList(int page, int count); + PageInfo queryPlatformList(int page, int count, String query); /** * 添加级联平台 * @param parentPlatform 级联平台 */ - boolean add(ParentPlatform parentPlatform); + boolean add(Platform parentPlatform); /** * 添加级联平台 * @param parentPlatform 级联平台 */ - boolean update(ParentPlatform parentPlatform); + boolean update(Platform parentPlatform); /** * 平台上线 * @param parentPlatform 平台信息 */ - void online(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo); + void online(Platform parentPlatform, SipTransactionInfo sipTransactionInfo); /** * 平台离线 * @param parentPlatform 平台信息 */ - void offline(ParentPlatform parentPlatform, boolean stopRegisterTask); + void offline(Platform parentPlatform, boolean stopRegisterTask); /** * 向上级平台发起注册 * @param parentPlatform */ - void login(ParentPlatform parentPlatform); + void login(Platform parentPlatform); /** * 向上级平台发送位置订阅 @@ -73,13 +75,19 @@ public interface IPlatformService { * @param errorEvent 信令错误事件 * @param timeoutCallback 超时事件 */ - void broadcastInvite(ParentPlatform platform, String channelId, MediaServer mediaServerItem, HookSubscribe.Event hookEvent, + void broadcastInvite(Platform platform, CommonGBChannel channelId, MediaServer mediaServerItem, HookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, InviteTimeOutCallback timeoutCallback) throws InvalidArgumentException, ParseException, SipException; /** * 语音喊话回复BYE */ - void stopBroadcast(ParentPlatform platform, DeviceChannel channel, String stream,boolean sendBye, MediaServer mediaServerItem); + void stopBroadcast(Platform platform, CommonGBChannel channel, String stream, boolean sendBye, MediaServer mediaServerItem); - void addSimulatedSubscribeInfo(ParentPlatform parentPlatform); + void addSimulatedSubscribeInfo(Platform parentPlatform); + + Platform queryOne(Integer platformId); + + List queryEnablePlatformList(); + + void delete(Integer platformId, CommonCallback callback); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java new file mode 100755 index 000000000..7f8bc0142 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java @@ -0,0 +1,73 @@ +package com.genersoft.iot.vmp.gb28181.service; + +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.exception.ServiceException; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.controller.bean.AudioBroadcastEvent; +import com.genersoft.iot.vmp.media.bean.MediaInfo; +import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.service.bean.ErrorCallback; +import com.genersoft.iot.vmp.service.bean.SSRCInfo; +import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; +import gov.nist.javax.sip.message.SIPResponse; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import javax.sip.header.CallIdHeader; +import java.text.ParseException; + +/** + * 点播处理 + */ +public interface IPlayService { + + SSRCInfo play(MediaServer mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback callback); + + StreamInfo onPublishHandlerForPlay(MediaServer mediaServerItem, MediaInfo mediaInfo, Device device, DeviceChannel channel); + + MediaServer getNewMediaServerItem(Device device); + + void playBack(Device device, DeviceChannel channel, String startTime, String endTime, ErrorCallback callback); + void zlmServerOffline(String mediaServerId); + + void download(Device device, DeviceChannel channel, String startTime, String endTime, int downloadSpeed, ErrorCallback callback); + + StreamInfo getDownLoadInfo(Device device, DeviceChannel channel, String stream); + + void zlmServerOnline(String mediaServerId); + + AudioBroadcastResult audioBroadcast(Device device, DeviceChannel deviceChannel, Boolean broadcastMode); + + boolean audioBroadcastCmd(Device device, DeviceChannel channel, MediaServer mediaServerItem, String app, String stream, int timeout, boolean isFromPlatform, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException; + + boolean audioBroadcastInUse(Device device, DeviceChannel channel); + + void stopAudioBroadcast(Device device, DeviceChannel channel); + + void pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; + + void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; + + void startPushStream(SendRtpInfo sendRtpItem, DeviceChannel channel, SIPResponse sipResponse, Platform platform, CallIdHeader callIdHeader); + + void startSendRtpStreamFailHand(SendRtpInfo sendRtpItem, Platform platform, CallIdHeader callIdHeader); + + void talkCmd(Device device, DeviceChannel channel, MediaServer mediaServerItem, String stream, AudioBroadcastEvent event); + + void stopTalk(Device device, DeviceChannel channel, Boolean streamIsReady); + + void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback); + + void stop(InviteSessionType type, Device device, DeviceChannel channel, String stream); + + void stop(InviteInfo inviteInfo); + + void play(CommonGBChannel channel, ErrorCallback callback); + + void playBack(CommonGBChannel channel, Long startTime, Long stopTime, ErrorCallback callback); + + void download(CommonGBChannel channel, Long startTime, Long stopTime, Integer downloadSpeed, ErrorCallback callback); + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IRegionService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IRegionService.java new file mode 100644 index 000000000..3fe220748 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IRegionService.java @@ -0,0 +1,37 @@ +package com.genersoft.iot.vmp.gb28181.service; + +import com.genersoft.iot.vmp.gb28181.bean.Region; +import com.genersoft.iot.vmp.gb28181.bean.RegionTree; +import com.github.pagehelper.PageInfo; + +import java.util.List; + + +public interface IRegionService { + + void add(Region region); + + boolean deleteByDeviceId(Integer regionDeviceId); + + /** + * 查询区划列表 + */ + PageInfo query(String query, int page, int count); + + /** + * 更新区域 + */ + void update(Region region); + + List getAllChild(String parent); + + Region queryRegionByDeviceId(String regionDeviceId); + + List queryForTree(String query, Integer parent); + + void syncFromChannel(); + + boolean delete(int id); + + boolean batchAdd(List regionList); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceAlarmServiceImpl.java similarity index 84% rename from src/main/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImpl.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceAlarmServiceImpl.java index e9936c4af..a08111af1 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceAlarmServiceImpl.java @@ -1,10 +1,9 @@ -package com.genersoft.iot.vmp.service.impl; +package com.genersoft.iot.vmp.gb28181.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; -import com.genersoft.iot.vmp.storager.dao.DeviceAlarmMapper; +import com.genersoft.iot.vmp.gb28181.service.IDeviceAlarmService; +import com.genersoft.iot.vmp.gb28181.dao.DeviceAlarmMapper; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceChannelServiceImpl.java new file mode 100755 index 000000000..b1b861ac0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceChannelServiceImpl.java @@ -0,0 +1,677 @@ +package com.genersoft.iot.vmp.gb28181.service.impl; + +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.InviteSessionType; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; +import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelReduce; +import com.genersoft.iot.vmp.gb28181.dao.DeviceChannelMapper; +import com.genersoft.iot.vmp.gb28181.dao.DeviceMapper; +import com.genersoft.iot.vmp.gb28181.dao.DeviceMobilePositionMapper; +import com.genersoft.iot.vmp.gb28181.dao.PlatformChannelMapper; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; +import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; +import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; +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.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author lin + */ +@Slf4j +@Service +@DS("master") +public class DeviceChannelServiceImpl implements IDeviceChannelService { + + @Autowired + private EventPublisher eventPublisher; + + @Autowired + private IInviteStreamService inviteStreamService; + + @Autowired + private DeviceChannelMapper channelMapper; + + @Autowired + private PlatformChannelMapper platformChannelMapper; + + @Autowired + private DeviceMapper deviceMapper; + + @Autowired + private DeviceMobilePositionMapper deviceMobilePositionMapper; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IPlatformChannelService platformChannelService; + + + @Override + public int updateChannels(Device device, List channels) { + List addChannels = new ArrayList<>(); + List updateChannels = new ArrayList<>(); + HashMap channelsInStore = new HashMap<>(); + int result = 0; + if (channels != null && !channels.isEmpty()) { + List channelList = channelMapper.queryChannelsByDeviceDbId(device.getId()); + if (channelList.isEmpty()) { + for (DeviceChannel channel : channels) { + channel.setDeviceDbId(device.getId()); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); + if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { + channel.setStreamId(inviteInfo.getStreamInfo().getStream()); + } + String now = DateUtil.getNow(); + channel.setUpdateTime(now); + channel.setCreateTime(now); + addChannels.add(channel); + } + }else { + for (DeviceChannel deviceChannel : channelList) { + channelsInStore.put(deviceChannel.getDeviceDbId() + deviceChannel.getDeviceId(), deviceChannel); + } + for (DeviceChannel channel : channels) { + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); + if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { + channel.setStreamId(inviteInfo.getStreamInfo().getStream()); + } + String now = DateUtil.getNow(); + channel.setUpdateTime(now); + DeviceChannel deviceChannelInDb = channelsInStore.get(channel.getDeviceDbId() + channel.getDeviceId()); + if ( deviceChannelInDb != null) { + channel.setId(deviceChannelInDb.getId()); + channel.setUpdateTime(now); + updateChannels.add(channel); + }else { + channel.setCreateTime(now); + channel.setUpdateTime(now); + addChannels.add(channel); + } + } + } + Set channelSet = new HashSet<>(); + // 滤重 + List addChannelList = new ArrayList<>(); + List updateChannelList = new ArrayList<>(); + addChannels.forEach(channel -> { + if (channelSet.add(channel.getDeviceId())) { + addChannelList.add(channel); + } + }); + channelSet.clear(); + updateChannels.forEach(channel -> { + if (channelSet.add(channel.getDeviceId())) { + updateChannelList.add(channel); + } + }); + + int limitCount = 500; + if (!addChannelList.isEmpty()) { + if (addChannelList.size() > limitCount) { + for (int i = 0; i < addChannelList.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > addChannelList.size()) { + toIndex = addChannelList.size(); + } + result += channelMapper.batchAdd(addChannelList.subList(i, toIndex)); + } + }else { + result += channelMapper.batchAdd(addChannelList); + } + } + if (!updateChannelList.isEmpty()) { + if (updateChannelList.size() > limitCount) { + for (int i = 0; i < updateChannelList.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > updateChannelList.size()) { + toIndex = updateChannelList.size(); + } + result += channelMapper.batchUpdate(updateChannelList.subList(i, toIndex)); + } + }else { + result += channelMapper.batchUpdate(updateChannelList); + } + } + } + return result; + } + + @Override + public ResourceBaseInfo getOverview() { + + int online = channelMapper.getOnlineCount(); + int total = channelMapper.getAllChannelCount(); + + return new ResourceBaseInfo(total, online); + } + + + @Override + public List queryAllChannelList(String platformId) { + return channelMapper.queryChannelListInAll(null, null, null, platformId, null); + } + + @Override + public PageInfo queryAllChannelList(int page, int count, String query, Boolean online, Boolean channelType, String platformId, String catalogId) { + PageHelper.startPage(page, count); + List all = channelMapper.queryChannelListInAll(query, online, channelType, platformId, catalogId); + return new PageInfo<>(all); + } + + @Override + public List getDeviceByChannelId(String channelId) { + + return channelMapper.getDeviceByChannelDeviceId(channelId); + } + + @Override + @Transactional + public int deleteChannelsForNotify(List channels) { + int limitCount = 1000; + int result = 0; + if (!channels.isEmpty()) { + if (channels.size() > limitCount) { + for (int i = 0; i < channels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > channels.size()) { + toIndex = channels.size(); + } + result += channelMapper.batchDel(channels.subList(i, toIndex)); + } + }else { + result += channelMapper.batchDel(channels); + } + } + return result; + } + + @Transactional + @Override + public int updateChannelsStatus(List channels) { + int limitCount = 1000; + int result = 0; + if (!channels.isEmpty()) { + if (channels.size() > limitCount) { + for (int i = 0; i < channels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > channels.size()) { + toIndex = channels.size(); + } + result += channelMapper.batchUpdateStatus(channels.subList(i, toIndex)); + } + }else { + result += channelMapper.batchUpdateStatus(channels); + } + } + return result; + } + + @Override + public void online(DeviceChannel channel) { + channelMapper.online(channel.getId()); + } + + @Override + public void offline(DeviceChannel channel) { + channelMapper.offline(channel.getId()); + } + + @Override + public void delete(DeviceChannel channel) { + channelMapper.del(channel.getId()); + } + + @Override + public DeviceChannel getOne(String deviceId, String channelId){ + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到设备:" + deviceId); + } + return channelMapper.getOneByDeviceId(device.getId(), channelId); + } + + @Override + public DeviceChannel getOneForSource(String deviceId, String channelId){ + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到设备:" + deviceId); + } + return channelMapper.getOneByDeviceIdForSource(device.getId(), channelId); + } + + @Override + public DeviceChannel getOneForSource(int deviceDbId, String channelId) { + return channelMapper.getOneByDeviceIdForSource(deviceDbId, channelId); + } + + @Override + @Transactional + public synchronized void batchUpdateChannelForNotify(List channels) { + String now = DateUtil.getNow(); + for (DeviceChannel channel : channels) { + channel.setUpdateTime(now); + } + int limitCount = 1000; + if (!channels.isEmpty()) { + if (channels.size() > limitCount) { + for (int i = 0; i < channels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > channels.size()) { + toIndex = channels.size(); + } + channelMapper.batchUpdateForNotify(channels.subList(i, toIndex)); + } + }else { + channelMapper.batchUpdateForNotify(channels); + } + } + } + + @Override + @Transactional + public void batchAddChannel(List channels) { + String now = DateUtil.getNow(); + for (DeviceChannel channel : channels) { + channel.setUpdateTime(now); + channel.setCreateTime(now); + } + int limitCount = 1000; + if (!channels.isEmpty()) { + if (channels.size() > limitCount) { + for (int i = 0; i < channels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > channels.size()) { + toIndex = channels.size(); + } + channelMapper.batchAdd(channels.subList(i, toIndex)); + } + }else { + channelMapper.batchAdd(channels); + } + } + for (DeviceChannel channel : channels) { + if (channel.getParentId() != null) { + channelMapper.updateChannelSubCount(channel.getDeviceDbId(), channel.getParentId()); + } + } + } + + @Override + public void updateChannelStreamIdentification(DeviceChannel channel) { + Assert.isTrue(channel.getId() > 0, "通道ID必须存在"); + Assert.hasLength(channel.getStreamIdentification(), "码流标识必须存在"); + if (ObjectUtils.isEmpty(channel.getStreamIdentification())) { + log.info("[重置通道码流类型] 设备: {}, 码流: {}", channel.getDeviceId(), channel.getStreamIdentification()); + }else { + log.info("[更新通道码流类型] 设备: {}, 通道:{}, 码流: {}", channel.getDeviceId(), channel.getDeviceId(), + channel.getStreamIdentification()); + } + channelMapper.updateChannelStreamIdentification(channel); + } + + @Override + public List queryChaneListByDeviceId(String deviceId) { + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到通道:" + deviceId); + } + return channelMapper.queryChannelsByDeviceDbId(device.getId()); + } + + @Override + public void updateChannelGPS(Device device, DeviceChannel deviceChannel, MobilePosition mobilePosition) { + if (userSetting.getSavePositionHistory()) { + deviceMobilePositionMapper.insertNewPosition(mobilePosition); + } + + if (deviceChannel.getDeviceId().equals(deviceChannel.getDeviceId())) { + deviceChannel.setDeviceId(null); + } + if (deviceChannel.getGpsTime() == null) { + deviceChannel.setGpsTime(DateUtil.getNow()); + } + + int updated = channelMapper.updatePosition(deviceChannel); + if (updated == 0) { + return; + } + + List deviceChannels = new ArrayList<>(); + if (deviceChannel.getDeviceId() == null) { + // 有的设备这里上报的deviceId与通道Id是一样,这种情况更新设备下的全部通道 + List deviceChannelsInDb = queryChaneListByDeviceId(device.getDeviceId()); + deviceChannels.addAll(deviceChannelsInDb); + }else { + deviceChannels.add(deviceChannel); + } + if (deviceChannels.isEmpty()) { + return; + } + if (deviceChannels.size() > 100) { + log.warn("[更新通道位置信息后发送通知] 设备可能是平台,上报的位置信息未标明通道编号," + + "导致所有通道被更新位置, deviceId:{}", device.getDeviceId()); + } + for (DeviceChannel channel : deviceChannels) { + // 向关联了该通道并且开启移动位置订阅的上级平台发送移动位置订阅消息 + mobilePosition.setChannelId(channel.getId()); + try { + eventPublisher.mobilePositionEventPublish(mobilePosition); + }catch (Exception e) { + log.error("[向上级转发移动位置失败] ", e); + } + // 发送redis消息。 通知位置信息的变化 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(mobilePosition.getTime())); + jsonObject.put("serial", mobilePosition.getDeviceId()); + jsonObject.put("code", channel.getDeviceId()); + jsonObject.put("longitude", mobilePosition.getLongitude()); + jsonObject.put("latitude", mobilePosition.getLatitude()); + jsonObject.put("altitude", mobilePosition.getAltitude()); + jsonObject.put("direction", mobilePosition.getDirection()); + jsonObject.put("speed", mobilePosition.getSpeed()); + redisCatchStorage.sendMobilePositionMsg(jsonObject); + } + } + + @Override + public void startPlay(Integer channelId, String stream) { + channelMapper.startPlay(channelId, stream); + } + + @Override + public void stopPlay(Integer channelId) { + channelMapper.stopPlayById(channelId); + } + + @Override + @Transactional + public void batchUpdateChannelGPS(List channelList) { + for (DeviceChannel deviceChannel : channelList) { + deviceChannel.setUpdateTime(DateUtil.getNow()); + if (deviceChannel.getGpsTime() == null) { + deviceChannel.setGpsTime(DateUtil.getNow()); + } + } + int count = 1000; + if (channelList.size() > count) { + for (int i = 0; i < channelList.size(); i+=count) { + int toIndex = i+count; + if ( i + count > channelList.size()) { + toIndex = channelList.size(); + } + List channels = channelList.subList(i, toIndex); + channelMapper.batchUpdatePosition(channels); + } + }else { + channelMapper.batchUpdatePosition(channelList); + } + } + + @Override + @Transactional + public void batchAddMobilePosition(List mobilePositions) { +// int count = 500; +// if (mobilePositions.size() > count) { +// for (int i = 0; i < mobilePositions.size(); i+=count) { +// int toIndex = i+count; +// if ( i + count > mobilePositions.size()) { +// toIndex = mobilePositions.size(); +// } +// List mobilePositionsSub = mobilePositions.subList(i, toIndex); +// deviceMobilePositionMapper.batchadd(mobilePositionsSub); +// } +// }else { +// deviceMobilePositionMapper.batchadd(mobilePositions); +// } + deviceMobilePositionMapper.batchadd(mobilePositions); + } + + @Override + public void cleanChannelsForDevice(int deviceId) { + channelMapper.cleanChannelsByDeviceId(deviceId); + } + + @Override + @Transactional + public boolean resetChannels(int deviceDbId, List deviceChannelList) { + if (CollectionUtils.isEmpty(deviceChannelList)) { + return false; + } + List allChannels = channelMapper.queryAllChannelsForRefresh(deviceDbId); + Map allChannelMap = new ConcurrentHashMap<>(); + if (!allChannels.isEmpty()) { + for (DeviceChannel deviceChannel : allChannels) { + allChannelMap.put(deviceChannel.getDeviceDbId() + deviceChannel.getDeviceId(), deviceChannel); + } + } + // 数据去重 + List channels = new ArrayList<>(); + + List updateChannels = new ArrayList<>(); + List addChannels = new ArrayList<>(); + List deleteChannels = new ArrayList<>(); + StringBuilder stringBuilder = new StringBuilder(); + Map subContMap = new HashMap<>(); + + // 数据去重 + Set gbIdSet = new HashSet<>(); + for (DeviceChannel deviceChannel : deviceChannelList) { + if (gbIdSet.contains(deviceChannel.getDeviceDbId() + deviceChannel.getDeviceId())) { + stringBuilder.append(deviceChannel.getDeviceId()).append(","); + continue; + } + gbIdSet.add(deviceChannel.getDeviceDbId() + deviceChannel.getDeviceId()); + DeviceChannel channelInDb = allChannelMap.get(deviceChannel.getDeviceDbId() + deviceChannel.getDeviceId()); + if (channelInDb != null) { + deviceChannel.setStreamId(channelInDb.getStreamId()); + deviceChannel.setHasAudio(channelInDb.isHasAudio()); + deviceChannel.setId(channelInDb.getId()); + if (channelInDb.getStatus() != null && channelInDb.getStatus().equalsIgnoreCase(deviceChannel.getStatus())){ + List ids = platformChannelMapper.queryParentPlatformByChannelId(deviceChannel.getDeviceId()); + if (!CollectionUtils.isEmpty(ids)){ + ids.forEach(platformId->{ + eventPublisher.catalogEventPublish(platformId, deviceChannel, deviceChannel.getStatus().equals("ON")? CatalogEvent.ON:CatalogEvent.OFF); + }); + } + } + deviceChannel.setUpdateTime(DateUtil.getNow()); + updateChannels.add(deviceChannel); + }else { + deviceChannel.setCreateTime(DateUtil.getNow()); + deviceChannel.setUpdateTime(DateUtil.getNow()); + addChannels.add(deviceChannel); + } + allChannelMap.remove(deviceChannel.getDeviceDbId() + deviceChannel.getDeviceId()); + channels.add(deviceChannel); + if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) { + if (subContMap.get(deviceChannel.getParentId()) == null) { + subContMap.put(deviceChannel.getParentId(), 1); + }else { + Integer count = subContMap.get(deviceChannel.getParentId()); + subContMap.put(deviceChannel.getParentId(), count++); + } + } + } + deleteChannels.addAll(allChannelMap.values()); + if (!channels.isEmpty()) { + for (DeviceChannel channel : channels) { + if (subContMap.get(channel.getDeviceId()) != null){ + Integer count = subContMap.get(channel.getDeviceId()); + if (count > 0) { + channel.setSubCount(count); + channel.setParental(1); + } + } + } + } + + if (stringBuilder.length() > 0) { + log.info("[目录查询]收到的数据存在重复: {}" , stringBuilder); + } + if(CollectionUtils.isEmpty(channels)){ + log.info("通道重设,数据为空={}" , deviceChannelList); + return false; + } + int limitCount = 50; + if (!addChannels.isEmpty()) { + if (addChannels.size() > limitCount) { + for (int i = 0; i < addChannels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > addChannels.size()) { + toIndex = addChannels.size(); + } + channelMapper.batchAdd(addChannels.subList(i, toIndex)); + } + }else { + channelMapper.batchAdd(addChannels); + } + } + if (!updateChannels.isEmpty()) { + if (updateChannels.size() > limitCount) { + for (int i = 0; i < updateChannels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > updateChannels.size()) { + toIndex = updateChannels.size(); + } + channelMapper.batchUpdate(updateChannels.subList(i, toIndex)); + } + }else { + channelMapper.batchUpdate(updateChannels); + } + // 不对收到的通道做比较,已确定是否真的发生变化,所以不发送更新通知 + + } + if (!deleteChannels.isEmpty()) { + try { + // 这些通道可能关联了,上级平台需要删除同时发送消息 + List ids = new ArrayList<>(); + deleteChannels.stream().forEach(deviceChannel -> { + ids.add(deviceChannel.getId()); + }); + platformChannelService.removeChannels(ids); + }catch (Exception e) { + log.error("[移除通道国标级联共享失败]", e); + } + if (deleteChannels.size() > limitCount) { + for (int i = 0; i < deleteChannels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > deleteChannels.size()) { + toIndex = deleteChannels.size(); + } + channelMapper.batchDel(deleteChannels.subList(i, toIndex)); + } + }else { + channelMapper.batchDel(deleteChannels); + } + } + return true; + + } + + @Override + public PageInfo getSubChannels(int deviceDbId, String channelId, String query, Boolean channelType, Boolean online, int page, int count) { + PageHelper.startPage(page, count); + List all = channelMapper.queryChannels(deviceDbId, channelId, query, channelType, online,null); + return new PageInfo<>(all); + } + + @Override + public List queryChannelExtendsByDeviceId(String deviceId, List channelIds, Boolean online) { + return channelMapper.queryChannelsWithDeviceInfo(deviceId, null,null, null, online,channelIds); + } + + @Override + public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, int page, int count) { + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到设备:" + deviceId); + } + // 获取到所有正在播放的流 + PageHelper.startPage(page, count); + List all = channelMapper.queryChannels(device.getId(), null, query, hasSubChannel, online,null); + return new PageInfo<>(all); + } + + @Override + public List queryDeviceWithAsMessageChannel() { + return deviceMapper.queryDeviceWithAsMessageChannel(); + } + + @Override + public DeviceChannel getRawChannel(int id) { + return deviceMapper.getRawChannel(id); + } + + @Override + public DeviceChannel getOneById(Integer channelId) { + return channelMapper.getOne(channelId); + } + + @Override + public DeviceChannel getOneForSourceById(Integer channelId) { + return channelMapper.getOneForSource(channelId); + } + + @Override + public DeviceChannel getBroadcastChannel(int deviceDbId) { + List channels = channelMapper.queryChannelsByDeviceDbId(deviceDbId); + if (channels.size() == 1) { + return channels.get(0); + } + for (DeviceChannel channel : channels) { + // 获取137类型的 + if (SipUtils.isFrontEnd(channel.getDeviceId())) { + return channel; + } + } + return null; + } + + @Override + public void changeAudio(Integer channelId, Boolean audio) { + channelMapper.changeAudio(channelId, audio); + } + + @Override + public void updateChannelStatus(DeviceChannel channel) { + channelMapper.updateStatus(channel); + } + + @Override + public void addChannel(DeviceChannel channel) { + channelMapper.add(channel); + } + + @Override + public void updateChannelForNotify(DeviceChannel channel) { + channelMapper.updateChannelForNotify(channel); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java similarity index 73% rename from src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java index 47b902e11..cbef7b5fc 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java @@ -1,13 +1,20 @@ -package com.genersoft.iot.vmp.service.impl; +package com.genersoft.iot.vmp.gb28181.service.impl; 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; import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.dao.DeviceChannelMapper; +import com.genersoft.iot.vmp.gb28181.dao.DeviceMapper; +import com.genersoft.iot.vmp.gb28181.dao.PlatformChannelMapper; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask; import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask; @@ -15,42 +22,35 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler; import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.service.IDeviceChannelService; -import com.genersoft.iot.vmp.service.IDeviceService; -import com.genersoft.iot.vmp.service.IInviteStreamService; import com.genersoft.iot.vmp.media.service.IMediaServerService; +import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; -import com.genersoft.iot.vmp.storager.dao.DeviceMapper; -import com.genersoft.iot.vmp.storager.dao.PlatformChannelMapper; import com.genersoft.iot.vmp.utils.DateUtil; -import com.genersoft.iot.vmp.vmanager.bean.BaseTree; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +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.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Service; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ObjectUtils; import javax.sip.InvalidArgumentException; import javax.sip.SipException; import java.text.ParseException; import java.time.Instant; -import java.util.*; +import java.util.List; import java.util.concurrent.TimeUnit; /** * 设备业务(目录订阅) */ +@Slf4j @Service @DS("master") public class DeviceServiceImpl implements IDeviceService { - private final static Logger logger = LoggerFactory.getLogger(DeviceServiceImpl.class); - @Autowired private SIPCommander cmder; @@ -82,10 +82,7 @@ public class DeviceServiceImpl implements IDeviceService { private DeviceChannelMapper deviceChannelMapper; @Autowired - DataSourceTransactionManager dataSourceTransactionManager; - - @Autowired - TransactionDefinition transactionDefinition; + private ISendRtpServerService sendRtpServerService; @Autowired private UserSetting userSetting; @@ -94,7 +91,7 @@ public class DeviceServiceImpl implements IDeviceService { private ISIPCommander commander; @Autowired - private VideoStreamSessionManager streamSession; + private SipInviteSessionManager sessionManager; @Autowired private IMediaServerService mediaServerService; @@ -104,7 +101,7 @@ public class DeviceServiceImpl implements IDeviceService { @Override public void online(Device device, SipTransactionInfo sipTransactionInfo) { - logger.info("[设备上线] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort()); + log.info("[设备上线] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort()); Device deviceInRedis = redisCatchStorage.getDevice(device.getDeviceId()); Device deviceInDb = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); @@ -132,13 +129,13 @@ public class DeviceServiceImpl implements IDeviceService { device.setOnLine(true); device.setCreateTime(now); device.setUpdateTime(now); - logger.info("[设备上线,首次注册]: {},查询设备信息以及通道信息", device.getDeviceId()); + log.info("[设备上线,首次注册]: {},查询设备信息以及通道信息", device.getDeviceId()); deviceMapper.add(device); redisCatchStorage.updateDevice(device); try { commander.deviceInfoQuery(device); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 查询设备信息: {}", e.getMessage()); + log.error("[命令发送失败] 查询设备信息: {}", e.getMessage()); } sync(device); }else { @@ -148,11 +145,11 @@ public class DeviceServiceImpl implements IDeviceService { deviceMapper.update(device); redisCatchStorage.updateDevice(device); if (userSetting.getSyncChannelOnDeviceOnline()) { - logger.info("[设备上线,离线状态下重新注册]: {},查询设备信息以及通道信息", device.getDeviceId()); + log.info("[设备上线,离线状态下重新注册]: {},查询设备信息以及通道信息", device.getDeviceId()); try { commander.deviceInfoQuery(device); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 查询设备信息: {}", e.getMessage()); + log.error("[命令发送失败] 查询设备信息: {}", e.getMessage()); } sync(device); // TODO 如果设备下的通道级联到了其他平台,那么需要发送事件或者notify给上级平台 @@ -171,8 +168,8 @@ public class DeviceServiceImpl implements IDeviceService { } }else { - if (deviceChannelMapper.queryAllChannels(device.getDeviceId()).size() == 0) { - logger.info("[设备上线]: {},通道数为0,查询通道信息", device.getDeviceId()); + if (deviceChannelMapper.queryChannelsByDeviceDbId(device.getId()).isEmpty()) { + log.info("[设备上线]: {},通道数为0,查询通道信息", device.getDeviceId()); sync(device); } @@ -202,7 +199,7 @@ public class DeviceServiceImpl implements IDeviceService { @Override public void offline(String deviceId, String reason) { - logger.warn("[设备离线],{}, device:{}", reason, deviceId); + log.warn("[设备离线],{}, device:{}", reason, deviceId); Device device = deviceMapper.getDeviceByDeviceId(deviceId); if (device == null) { return; @@ -222,30 +219,30 @@ public class DeviceServiceImpl implements IDeviceService { //进行通道离线 // deviceChannelMapper.offlineByDeviceId(deviceId); // 离线释放所有ssrc - List ssrcTransactions = streamSession.getSsrcTransactionForAll(deviceId, null, null, null); + List ssrcTransactions = sessionManager.getSsrcTransactionByDeviceId(deviceId); if (ssrcTransactions != null && ssrcTransactions.size() > 0) { for (SsrcTransaction ssrcTransaction : ssrcTransactions) { mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); - streamSession.removeByCallId(deviceId, ssrcTransaction.getChannelId(), ssrcTransaction.getCallId()); + sessionManager.removeByCallId(ssrcTransaction.getCallId()); } } // 移除订阅 removeCatalogSubscribe(device, null); removeMobilePositionSubscribe(device, null); - List audioBroadcastCatches = audioBroadcastManager.get(deviceId); - if (audioBroadcastCatches.size() > 0) { + List audioBroadcastCatches = audioBroadcastManager.getByDeviceId(deviceId); + if (!audioBroadcastCatches.isEmpty()) { for (AudioBroadcastCatch audioBroadcastCatch : audioBroadcastCatches) { - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(deviceId, audioBroadcastCatch.getChannelId(), null, null); + SendRtpInfo sendRtpItem = sendRtpServerService.queryByChannelId(audioBroadcastCatch.getChannelId(), deviceId); if (sendRtpItem != null) { - redisCatchStorage.deleteSendRTPServer(deviceId, sendRtpItem.getChannelId(), null, null); + sendRtpServerService.delete(sendRtpItem); MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); mediaServerService.stopSendRtp(mediaInfo, sendRtpItem.getApp(), sendRtpItem.getStream(), null); } - audioBroadcastManager.del(deviceId, audioBroadcastCatch.getChannelId()); + audioBroadcastManager.del(audioBroadcastCatch.getChannelId()); } } } @@ -255,7 +252,7 @@ public class DeviceServiceImpl implements IDeviceService { if (device == null || device.getSubscribeCycleForCatalog() < 0) { return false; } - logger.info("[添加目录订阅] 设备{}", device.getDeviceId()); + log.info("[添加目录订阅] 设备{}", device.getDeviceId()); // 添加目录订阅 CatalogSubscribeTask catalogSubscribeTask = new CatalogSubscribeTask(device, sipCommander, dynamicTask); // 刷新订阅 @@ -272,7 +269,7 @@ public class DeviceServiceImpl implements IDeviceService { if (device == null || device.getSubscribeCycleForCatalog() < 0) { return false; } - logger.info("[移除目录订阅]: {}", device.getDeviceId()); + log.info("[移除目录订阅]: {}", device.getDeviceId()); String taskKey = device.getDeviceId() + "catalog"; if (device.isOnLine()) { Runnable runnable = dynamicTask.get(taskKey); @@ -290,7 +287,7 @@ public class DeviceServiceImpl implements IDeviceService { if (device == null || device.getSubscribeCycleForMobilePosition() < 0) { return false; } - logger.info("[添加移动位置订阅] 设备{}", device.getDeviceId()); + log.info("[添加移动位置订阅] 设备{}", device.getDeviceId()); // 添加目录订阅 MobilePositionSubscribeTask mobilePositionSubscribeTask = new MobilePositionSubscribeTask(device, sipCommander, dynamicTask); // 设置最小值为30 @@ -306,7 +303,7 @@ public class DeviceServiceImpl implements IDeviceService { if (device == null || device.getSubscribeCycleForCatalog() < 0) { return false; } - logger.info("[移除移动位置订阅]: {}", device.getDeviceId()); + log.info("[移除移动位置订阅]: {}", device.getDeviceId()); String taskKey = device.getDeviceId() + "mobile_position"; if (device.isOnLine()) { Runnable runnable = dynamicTask.get(taskKey); @@ -332,7 +329,7 @@ public class DeviceServiceImpl implements IDeviceService { @Override public void sync(Device device) { if (catalogResponseMessageHandler.isSyncRunning(device.getDeviceId())) { - logger.info("开启同步时发现同步已经存在"); + log.info("开启同步时发现同步已经存在"); return; } int sn = (int)((Math.random()*9+1)*100000); @@ -343,14 +340,14 @@ public class DeviceServiceImpl implements IDeviceService { catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), errorMsg); }); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[同步通道], 信令发送失败:{}", e.getMessage() ); + log.error("[同步通道], 信令发送失败:{}", e.getMessage() ); String errorMsg = String.format("同步通道失败,信令发送失败: %s", e.getMessage()); catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), errorMsg); } } @Override - public Device getDevice(String deviceId) { + public Device getDeviceByDeviceId(String deviceId) { Device device = redisCatchStorage.getDevice(deviceId); if (device == null) { device = deviceMapper.getDeviceByDeviceId(deviceId); @@ -366,6 +363,11 @@ public class DeviceServiceImpl implements IDeviceService { return deviceMapper.getOnlineDevices(); } + @Override + public List getAllByStatus(Boolean status) { + return deviceMapper.getDevices(status); + } + @Override public boolean expire(Device device) { Instant registerTimeDate = Instant.from(DateUtil.formatter.parse(device.getRegisterTime())); @@ -381,7 +383,7 @@ public class DeviceServiceImpl implements IDeviceService { try { sipCommander.deviceStatusQuery(device, null); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 设备状态查询: {}", e.getMessage()); + log.error("[命令发送失败] 设备状态查询: {}", e.getMessage()); } } @@ -396,93 +398,13 @@ public class DeviceServiceImpl implements IDeviceService { String now = DateUtil.getNow(); device.setUpdateTime(now); - device.setCharset(device.getCharset().toUpperCase()); + device.setCharset(device.getCharset() == null ? "" : device.getCharset().toUpperCase()); device.setUpdateTime(DateUtil.getNow()); if (deviceMapper.update(device) > 0) { redisCatchStorage.updateDevice(device); } } - /** - * 更新通道坐标系 - */ - private void updateDeviceChannelGeoCoordSys(Device device) { - List deviceChannels = deviceChannelMapper.getAllChannelWithCoordinate(device.getDeviceId()); - if (deviceChannels.size() > 0) { - List deviceChannelsForStore = new ArrayList<>(); - for (DeviceChannel deviceChannel : deviceChannels) { - deviceChannelsForStore.add(deviceChannelService.updateGps(deviceChannel, device)); - } - deviceChannelService.updateChannels(device.getDeviceId(), deviceChannelsForStore); - } - } - - - @Override - public List> queryVideoDeviceTree(String deviceId, String parentId, boolean onlyCatalog) { - Device device = deviceMapper.getDeviceByDeviceId(deviceId); - if (device == null) { - return null; - } - if (ObjectUtils.isEmpty(parentId) ) { - parentId = deviceId; - } - List rootNodes = deviceChannelMapper.getSubChannelsByDeviceId(deviceId, parentId, onlyCatalog); - return transportChannelsToTree(rootNodes, ""); - } - - @Override - public List queryVideoDeviceInTreeNode(String deviceId, String parentId) { - Device device = deviceMapper.getDeviceByDeviceId(deviceId); - if (device == null) { - return null; - } - if (ObjectUtils.isEmpty(parentId) || parentId.equals(deviceId)) { - return deviceChannelMapper.getSubChannelsByDeviceId(deviceId, null, false); - }else { - return deviceChannelMapper.getSubChannelsByDeviceId(deviceId, parentId, false); - } - } - - private List> transportChannelsToTree(List channels, String parentId) { - if (channels == null) { - return null; - } - List> treeNotes = new ArrayList<>(); - if (channels.size() == 0) { - return treeNotes; - } - for (DeviceChannel channel : channels) { - - BaseTree node = new BaseTree<>(); - node.setId(channel.getChannelId()); - node.setDeviceId(channel.getDeviceId()); - node.setName(channel.getName()); - node.setPid(parentId); - node.setBasicData(channel); - node.setParent(false); - if (channel.getChannelId().length() <= 8) { - node.setParent(true); - }else { - if (channel.getChannelId().length() != 20) { - node.setParent(channel.getParental() == 1); - }else { - try { - int type = Integer.parseInt(channel.getChannelId().substring(10, 13)); - if (type == 215 || type == 216 || type == 200) { - node.setParent(true); - } - }catch (NumberFormatException e) { - node.setParent(false); - } - } - } - treeNotes.add(node); - } - Collections.sort(treeNotes); - return treeNotes; - } - @Override public boolean isExist(String deviceId) { return deviceMapper.getDeviceByDeviceId(deviceId) != null; @@ -500,7 +422,7 @@ public class DeviceServiceImpl implements IDeviceService { public void updateCustomDevice(Device device) { Device deviceInStore = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); if (deviceInStore == null) { - logger.warn("更新设备时未找到设备信息"); + log.warn("更新设备时未找到设备信息"); return; } @@ -525,6 +447,7 @@ public class DeviceServiceImpl implements IDeviceService { if (!ObjectUtils.isEmpty(device.getStreamMode())) { deviceInStore.setStreamMode(device.getStreamMode()); } + deviceInStore.setBroadcastPushAfterAck(device.isBroadcastPushAfterAck()); // 目录订阅相关的信息 if (deviceInStore.getSubscribeCycleForCatalog() != device.getSubscribeCycleForCatalog()) { if (device.getSubscribeCycleForCatalog() > 0) { @@ -582,7 +505,6 @@ public class DeviceServiceImpl implements IDeviceService { // 坐标系变化,需要重新计算GCJ02坐标和WGS84坐标 if (!deviceInStore.getGeoCoordSys().equals(device.getGeoCoordSys())) { deviceInStore.setGeoCoordSys(device.getGeoCoordSys()); - updateDeviceChannelGeoCoordSys(deviceInStore); } }else { deviceInStore.setGeoCoordSys("WGS84"); @@ -600,25 +522,17 @@ public class DeviceServiceImpl implements IDeviceService { } @Override + @Transactional public boolean delete(String deviceId) { - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); - boolean result = false; - try { - platformChannelMapper.delChannelForDeviceId(deviceId); - deviceChannelMapper.cleanChannelsByDeviceId(deviceId); - if ( deviceMapper.del(deviceId) < 0 ) { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - } - result = true; - dataSourceTransactionManager.commit(transactionStatus); //手动提交 - }catch (Exception e) { - dataSourceTransactionManager.rollback(transactionStatus); + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到设备:" + deviceId); } - if (result) { - redisCatchStorage.removeDevice(deviceId); - } - return result; + platformChannelMapper.delChannelForDeviceId(deviceId); + deviceChannelMapper.cleanChannelsByDeviceId(device.getId()); + deviceMapper.del(deviceId); + redisCatchStorage.removeDevice(deviceId); + return true; } @Override @@ -633,5 +547,25 @@ public class DeviceServiceImpl implements IDeviceService { return deviceMapper.getAll(); } + @Override + public PageInfo getAll(int page, int count, String query, Boolean status) { + PageHelper.startPage(page, count); + List all = deviceMapper.getDeviceList(query, status); + return new PageInfo<>(all); + } + @Override + public Device getDevice(Integer id) { + return deviceMapper.query(id); + } + + @Override + public Device getDeviceByChannelId(Integer channelId) { + return deviceMapper.queryByChannelId(channelId); + } + + @Override + public Device getDeviceBySourceChannelDeviceId(String channelId) { + return deviceMapper.getDeviceBySourceChannelDeviceId(channelId); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelPlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelPlayServiceImpl.java new file mode 100644 index 000000000..01f6ff8c0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelPlayServiceImpl.java @@ -0,0 +1,165 @@ +package com.genersoft.iot.vmp.gb28181.service.impl; + +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.InviteInfo; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.bean.PlayException; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelPlayService; +import com.genersoft.iot.vmp.gb28181.service.IPlayService; +import com.genersoft.iot.vmp.service.bean.ErrorCallback; +import com.genersoft.iot.vmp.service.bean.InviteErrorCode; +import com.genersoft.iot.vmp.streamProxy.service.IStreamProxyPlayService; +import com.genersoft.iot.vmp.streamPush.service.IStreamPushPlayService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.sip.message.Response; + +@Slf4j +@Service +public class GbChannelPlayServiceImpl implements IGbChannelPlayService { + + @Autowired + private IPlayService deviceChannelPlayService; + + @Autowired + private IStreamProxyPlayService streamProxyPlayService; + + @Autowired + private IStreamPushPlayService streamPushPlayService; + + + @Override + public void start(CommonGBChannel channel, InviteInfo inviteInfo, Platform platform, ErrorCallback callback) { + if (channel == null || inviteInfo == null || callback == null) { + log.warn("[通用通道点播] 参数异常, channel: {}, inviteInfo: {}, callback: {}", channel != null, inviteInfo != null, callback != null); + throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); + } + log.info("[点播通用通道] 类型:{}, 通道: {}({})", inviteInfo.getSessionName(), channel.getGbName(), channel.getGbDeviceId()); + if ("Play".equalsIgnoreCase(inviteInfo.getSessionName())) { + if (channel.getGbDeviceDbId() != null) { + // 国标通道 + playGbDeviceChannel(channel, callback); + } else if (channel.getStreamProxyId() != null) { + // 拉流代理 + playProxy(channel, callback); + } else if (channel.getStreamPushId() != null) { + // 推流 + playPush(channel, platform.getServerGBId(), platform.getName(), callback); + } else { + // 通道数据异常 + log.error("[点播通用通道] 通道数据异常,无法识别通道来源: {}({})", channel.getGbName(), channel.getGbDeviceId()); + throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); + } + }else if ("Playback".equals(inviteInfo.getSessionName())) { + if (channel.getGbDeviceDbId() != null) { + // 国标通道 + playbackGbDeviceChannel(channel, inviteInfo.getStartTime(), inviteInfo.getStopTime(), callback); + } else if (channel.getStreamProxyId() != null) { + // 拉流代理 + log.warn("[回放通用通道] 不支持回放拉流代理的录像: {}({})", channel.getGbName(), channel.getGbDeviceId()); + throw new PlayException(Response.FORBIDDEN, "forbidden"); + } else if (channel.getStreamPushId() != null) { + // 推流 + log.warn("[回放通用通道] 不支持回放推流的录像: {}({})", channel.getGbName(), channel.getGbDeviceId()); + throw new PlayException(Response.FORBIDDEN, "forbidden"); + } else { + // 通道数据异常 + log.error("[回放通用通道] 通道数据异常,无法识别通道来源: {}({})", channel.getGbName(), channel.getGbDeviceId()); + throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); + } + }else if ("Download".equals(inviteInfo.getSessionName())) { + if (channel.getGbDeviceDbId() != null) { + int downloadSpeed = 4; + try { + if (inviteInfo.getDownloadSpeed() != null){ + downloadSpeed = Integer.parseInt(inviteInfo.getDownloadSpeed()); + } + }catch (Exception ignored) {} + + // 国标通道 + downloadGbDeviceChannel(channel, inviteInfo.getStartTime(), inviteInfo.getStopTime(), downloadSpeed, callback); + } else if (channel.getStreamProxyId() != null) { + // 拉流代理 + log.warn("[下载通用通道录像] 不支持下载拉流代理的录像: {}({})", channel.getGbName(), channel.getGbDeviceId()); + throw new PlayException(Response.FORBIDDEN, "forbidden"); + } else if (channel.getStreamPushId() != null) { + // 推流 + log.warn("[下载通用通道录像] 不支持下载推流的录像: {}({})", channel.getGbName(), channel.getGbDeviceId()); + throw new PlayException(Response.FORBIDDEN, "forbidden"); + } else { + // 通道数据异常 + log.error("[回放通用通道] 通道数据异常,无法识别通道来源: {}({})", channel.getGbName(), channel.getGbDeviceId()); + throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); + } + }else { + // 不支持的点播方式 + log.error("[点播通用通道] 不支持的点播方式:{}, {}({})", inviteInfo.getSessionName(), channel.getGbName(), channel.getGbDeviceId()); + throw new PlayException(Response.BAD_REQUEST, "bad request"); + } + } + + @Override + public void playGbDeviceChannel(CommonGBChannel channel, ErrorCallback callback){ + // 国标通道 + try { + deviceChannelPlayService.play(channel, callback); + } catch (PlayException e) { + callback.run(e.getCode(), e.getMsg(), null); + } catch (Exception e) { + log.error("[点播失败] {}({})", channel.getGbName(), channel.getGbDeviceId(), e); + callback.run(Response.BUSY_HERE, "busy here", null); + } + } + + @Override + public void playProxy(CommonGBChannel channel, ErrorCallback callback){ + // 拉流代理通道 + try { + StreamInfo streamInfo = streamProxyPlayService.start(channel.getStreamProxyId()); + if (streamInfo == null) { + callback.run(Response.BUSY_HERE, "busy here", null); + }else { + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); + } + }catch (Exception e) { + callback.run(Response.BUSY_HERE, "busy here", null); + } + } + + @Override + public void playPush(CommonGBChannel channel, String platformDeviceId, String platformName, ErrorCallback callback){ + // 推流 + try { + streamPushPlayService.start(channel.getStreamPushId(), callback, platformDeviceId, platformName); + }catch (PlayException e) { + callback.run(e.getCode(), e.getMsg(), null); + }catch (Exception e) { + log.error("[点播推流通道失败] 通道: {}({})", channel.getGbName(), channel.getGbDeviceId(), e); + callback.run(Response.BUSY_HERE, "busy here", null); + } + } + + private void playbackGbDeviceChannel(CommonGBChannel channel, Long startTime, Long stopTime, ErrorCallback callback){ + try { + deviceChannelPlayService.playBack(channel, startTime, stopTime, callback); + } catch (PlayException e) { + callback.run(e.getCode(), e.getMsg(), null); + } catch (Exception e) { + callback.run(Response.BUSY_HERE, "busy here", null); + } + } + + private void downloadGbDeviceChannel(CommonGBChannel channel, Long startTime, Long stopTime, Integer downloadSpeed, + ErrorCallback callback){ + try { + deviceChannelPlayService.download(channel, startTime, stopTime, downloadSpeed, callback); + } catch (PlayException e) { + callback.run(e.getCode(), e.getMsg(), null); + } catch (Exception e) { + callback.run(Response.BUSY_HERE, "busy here", null); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelServiceImpl.java new file mode 100644 index 000000000..62be902bd --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelServiceImpl.java @@ -0,0 +1,684 @@ +package com.genersoft.iot.vmp.gb28181.service.impl; + +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.dao.CommonGBChannelMapper; +import com.genersoft.iot.vmp.gb28181.dao.GroupMapper; +import com.genersoft.iot.vmp.gb28181.dao.PlatformChannelMapper; +import com.genersoft.iot.vmp.gb28181.dao.RegionMapper; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; +import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService; +import com.genersoft.iot.vmp.streamPush.bean.StreamPush; +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 lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +import java.util.*; + +@Slf4j +@Service +public class GbChannelServiceImpl implements IGbChannelService { + + @Autowired + private EventPublisher eventPublisher; + + @Autowired + private CommonGBChannelMapper commonGBChannelMapper; + + @Autowired + private PlatformChannelMapper platformChannelMapper; + + @Autowired + private IPlatformChannelService platformChannelService; + + @Autowired + private RegionMapper regionMapper; + + @Autowired + private GroupMapper groupMapper; + + @Override + public CommonGBChannel queryByDeviceId(String gbDeviceId) { + return commonGBChannelMapper.queryByDeviceId(gbDeviceId); + } + + @Override + public int add(CommonGBChannel commonGBChannel) { + if (commonGBChannel.getStreamPushId() != null && commonGBChannel.getStreamPushId() > 0) { + CommonGBChannel commonGBChannelInDb = commonGBChannelMapper.queryByStreamPushId(commonGBChannel.getStreamPushId()); + if (commonGBChannelInDb != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "此推流已经关联通道"); + } + } + if (commonGBChannel.getStreamProxyId() != null && commonGBChannel.getStreamProxyId() > 0) { + CommonGBChannel commonGBChannelInDb = commonGBChannelMapper.queryByStreamProxyId(commonGBChannel.getStreamProxyId()); + if (commonGBChannelInDb != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "此代理已经关联通道"); + } + } + commonGBChannel.setCreateTime(DateUtil.getNow()); + commonGBChannel.setUpdateTime(DateUtil.getNow()); + return commonGBChannelMapper.insert(commonGBChannel); + } + + @Override + @Transactional + public int delete(int gbId) { + // 移除国标级联关联的信息 + try { + platformChannelService.removeChannel(gbId); + }catch (Exception e) { + log.error("[移除通道国标级联共享失败]", e); + } + + CommonGBChannel channel = commonGBChannelMapper.queryById(gbId); + if (channel != null) { + commonGBChannelMapper.delete(gbId); + try { + // 发送通知 + eventPublisher.catalogEventPublish(null, channel, CatalogEvent.DEL); + } catch (Exception e) { + log.warn("[通道移除通知] 发送失败,{}", channel.getGbDeviceId(), e); + } + } + return 1; + } + + @Override + @Transactional + public void delete(Collection ids) { + // 移除国标级联关联的信息 + try { + platformChannelService.removeChannels(new ArrayList<>(ids)); + }catch (Exception e) { + log.error("[移除通道国标级联共享失败]", e); + } + List channelListInDb = commonGBChannelMapper.queryByIds(ids); + if (channelListInDb.isEmpty()) { + return; + } + commonGBChannelMapper.batchDelete(channelListInDb); + } + + @Override + public int update(CommonGBChannel commonGBChannel) { + log.info("[更新通道] 通道ID: {}, ", commonGBChannel.getGbId()); + if (commonGBChannel.getGbId() <= 0) { + log.warn("[更新通道] 未找到数据库ID,更新失败, {}", commonGBChannel.getGbDeviceDbId()); + return 0; + } + commonGBChannel.setUpdateTime(DateUtil.getNow()); + int result = commonGBChannelMapper.update(commonGBChannel); + if (result > 0) { + try { + // 发送通知 + eventPublisher.catalogEventPublish(null, commonGBChannel, CatalogEvent.UPDATE); + } catch (Exception e) { + log.warn("[更新通道通知] 发送失败,{}", commonGBChannel.getGbDeviceId(), e); + } + } + return result; + } + + @Override + public int offline(CommonGBChannel commonGBChannel) { + if (commonGBChannel.getGbId() <= 0) { + log.warn("[通道离线] 未找到数据库ID,更新失败, {}", commonGBChannel.getGbDeviceDbId()); + return 0; + } + int result = commonGBChannelMapper.updateStatusById(commonGBChannel.getGbId(), 0); + if (result > 0) { + try { + // 发送通知 + eventPublisher.catalogEventPublish(null, commonGBChannel, CatalogEvent.OFF); + } catch (Exception e) { + log.warn("[通道离线通知] 发送失败,{}", commonGBChannel.getGbDeviceId(), e); + } + } + return result; + } + + @Override + @Transactional + public int offline(List commonGBChannelList) { + if (commonGBChannelList.isEmpty()) { + log.warn("[多个通道离线] 通道数量为0,更新失败"); + return 0; + } + List onlineChannelList = commonGBChannelMapper.queryInListByStatus(commonGBChannelList, "ON"); + if (onlineChannelList.isEmpty()) { + log.warn("[多个通道离线] 更新失败, 参数内通道已经离线, 无需更新"); + return 0; + } + int limitCount = 1000; + int result = 0; + if (onlineChannelList.size() > limitCount) { + for (int i = 0; i < onlineChannelList.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > onlineChannelList.size()) { + toIndex = onlineChannelList.size(); + } + result += commonGBChannelMapper.updateStatusForListById(onlineChannelList.subList(i, toIndex), "OFF"); + } + } else { + result += commonGBChannelMapper.updateStatusForListById(onlineChannelList, "OFF"); + } + if (result > 0) { + try { + // 发送catalog + eventPublisher.catalogEventPublish(null, onlineChannelList, CatalogEvent.OFF); + } catch (Exception e) { + log.warn("[多个通道离线] 发送失败,数量:{}", onlineChannelList.size(), e); + } + } + return result; + } + + @Override + public int online(CommonGBChannel commonGBChannel) { + if (commonGBChannel.getGbId() <= 0) { + log.warn("[通道上线] 未找到数据库ID,更新失败, {}", commonGBChannel.getGbDeviceDbId()); + return 0; + } + int result = commonGBChannelMapper.updateStatusById(commonGBChannel.getGbId(), 1); + if (result > 0) { + try { + // 发送通知 + eventPublisher.catalogEventPublish(null, commonGBChannel, CatalogEvent.ON); + } catch (Exception e) { + log.warn("[通道上线通知] 发送失败,{}", commonGBChannel.getGbDeviceId(), e); + } + } + return 0; + } + + @Override + @Transactional + public int online(List commonGBChannelList) { + if (commonGBChannelList.isEmpty()) { + log.warn("[多个通道上线] 通道数量为0,更新失败"); + return 0; + } + List offlineChannelList = commonGBChannelMapper.queryInListByStatus(commonGBChannelList, "OFF"); + if (offlineChannelList.isEmpty()) { + log.warn("[多个通道上线] 更新失败, 参数内通道已经上线"); + return 0; + } + // 批量更新 + int limitCount = 1000; + int result = 0; + if (offlineChannelList.size() > limitCount) { + for (int i = 0; i < offlineChannelList.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > offlineChannelList.size()) { + toIndex = offlineChannelList.size(); + } + result += commonGBChannelMapper.updateStatusForListById(offlineChannelList.subList(i, toIndex), "ON"); + } + } else { + result += commonGBChannelMapper.updateStatusForListById(offlineChannelList, "ON"); + } + if (result > 0) { + try { + // 发送catalog + eventPublisher.catalogEventPublish(null, offlineChannelList, CatalogEvent.ON); + } catch (Exception e) { + log.warn("[多个通道上线] 发送失败,数量:{}", offlineChannelList.size(), e); + } + } + + return result; + } + + @Override + @Transactional + public void batchAdd(List commonGBChannels) { + if (commonGBChannels.isEmpty()) { + log.warn("[新增多个通道] 通道数量为0,更新失败"); + return; + } + // 批量保存 + int limitCount = 1000; + int result = 0; + if (commonGBChannels.size() > limitCount) { + for (int i = 0; i < commonGBChannels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > commonGBChannels.size()) { + toIndex = commonGBChannels.size(); + } + result += commonGBChannelMapper.batchAdd(commonGBChannels.subList(i, toIndex)); + } + } else { + result += commonGBChannelMapper.batchAdd(commonGBChannels); + } + log.warn("[新增多个通道] 通道数量为{},成功保存:{}", commonGBChannels.size(), result); + } + + @Override + public void batchUpdate(List commonGBChannels) { + if (commonGBChannels.isEmpty()) { + log.warn("[更新多个通道] 通道数量为0,更新失败"); + return; + } + // 批量保存 + int limitCount = 1000; + int result = 0; + if (commonGBChannels.size() > limitCount) { + for (int i = 0; i < commonGBChannels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > commonGBChannels.size()) { + toIndex = commonGBChannels.size(); + } + result += commonGBChannelMapper.batchUpdate(commonGBChannels.subList(i, toIndex)); + } + } else { + result += commonGBChannelMapper.batchUpdate(commonGBChannels); + } + log.info("[更新多个通道] 通道数量为{},成功保存:{}", commonGBChannels.size(), result); + // 发送通过更新通知 + try { + // 发送通知 + eventPublisher.catalogEventPublish(null, commonGBChannels, CatalogEvent.UPDATE); + } catch (Exception e) { + log.warn("[更新多个通道] 发送失败,{}个", commonGBChannels.size(), e); + } + } + + @Override + @Transactional + public void updateStatus(List commonGBChannels) { + if (commonGBChannels.isEmpty()) { + log.warn("[更新多个通道状态] 通道数量为0,更新失败"); + return; + } + int limitCount = 1000; + int result = 0; + if (commonGBChannels.size() > limitCount) { + for (int i = 0; i < commonGBChannels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > commonGBChannels.size()) { + toIndex = commonGBChannels.size(); + } + result += commonGBChannelMapper.updateStatus(commonGBChannels.subList(i, toIndex)); + } + } else { + result += commonGBChannelMapper.updateStatus(commonGBChannels); + } + log.warn("[更新多个通道状态] 通道数量为{},成功保存:{}", commonGBChannels.size(), result); + // 发送通过更新通知 + try { + // 发送通知 + eventPublisher.catalogEventPublish(null, commonGBChannels, CatalogEvent.UPDATE); + } catch (Exception e) { + log.warn("[更新多个通道] 发送失败,{}个", commonGBChannels.size(), e); + } + } + + + + @Override + public CommonGBChannel getOne(int id) { + return commonGBChannelMapper.queryById(id); + } + + @Override + public List getIndustryCodeList() { + IndustryCodeTypeEnum[] values = IndustryCodeTypeEnum.values(); + List result = new ArrayList<>(values.length); + for (IndustryCodeTypeEnum value : values) { + result.add(IndustryCodeType.getInstance(value)); + } + Collections.sort(result); + return result; + } + + @Override + public List getDeviceTypeList() { + DeviceTypeEnum[] values = DeviceTypeEnum.values(); + List result = new ArrayList<>(values.length); + for (DeviceTypeEnum value : values) { + result.add(DeviceType.getInstance(value)); + } + Collections.sort(result); + return result; + } + + @Override + public List getNetworkIdentificationTypeList() { + NetworkIdentificationTypeEnum[] values = NetworkIdentificationTypeEnum.values(); + List result = new ArrayList<>(values.length); + for (NetworkIdentificationTypeEnum value : values) { + result.add(NetworkIdentificationType.getInstance(value)); + } + Collections.sort(result); + return result; + } + + @Override + public void reset(int id) { + log.info("[重置国标通道] id: {}", id); + CommonGBChannel channel = getOne(id); + if (channel == null) { + log.warn("[重置国标通道] 未找到对应Id的通道: id: {}", id); + throw new ControllerException(ErrorCode.ERROR400); + } + if (channel.getGbDeviceDbId() <= 0) { + log.warn("[重置国标通道] 非国标下级通道无法重置: id: {}", id); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "非国标下级通道无法重置"); + } + // 这个多加一个参数,为了防止将非国标的通道通过此方法清空内容,导致意外发生 + commonGBChannelMapper.reset(id, channel.getGbDeviceDbId(), DateUtil.getNow()); + CommonGBChannel channelNew = getOne(id); + // 发送通过更新通知 + try { + // 发送通知 + eventPublisher.catalogEventPublish(null, channelNew, CatalogEvent.UPDATE); + } catch (Exception e) { + log.warn("[通道移除通知] 发送失败,{}", channelNew.getGbDeviceId(), e); + } + } + + @Override + public PageInfo queryList(int page, int count, String query, Boolean online, Boolean hasCivilCode, + Boolean hasGroup) { + PageHelper.startPage(page, count); + List all = commonGBChannelMapper.queryList(query, online, hasCivilCode, hasGroup); + return new PageInfo<>(all); + } + + @Override + public void removeCivilCode(List allChildren) { + commonGBChannelMapper.removeCivilCode(allChildren); + // TODO 是否需要通知上级, 或者等添加新的行政区划时发送更新通知 + + } + + @Override + public void addChannelToRegion(String civilCode, List channelIds) { + List channelList = commonGBChannelMapper.queryByIds(channelIds); + if (channelList.isEmpty()) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "所有通道Id不存在"); + } + for (CommonGBChannel channel : channelList) { + channel.setGbCivilCode(civilCode); + } + int result = commonGBChannelMapper.updateRegion(civilCode, channelList); + // 发送通知 + if (result > 0) { + try { + // 发送catalog + eventPublisher.catalogEventPublish(null, channelList, CatalogEvent.UPDATE); + } catch (Exception e) { + log.warn("[多个通道添加行政区划] 发送失败,数量:{}", channelList.size(), e); + } + } + } + + @Override + @Transactional + public void deleteChannelToRegion(String civilCode, List channelIds) { + if (!ObjectUtils.isEmpty(civilCode)) { + deleteChannelToRegionByCivilCode(civilCode); + } + if (!ObjectUtils.isEmpty(channelIds)) { + deleteChannelToRegionByChannelIds(channelIds); + } + } + + @Override + public void deleteChannelToRegionByCivilCode(String civilCode) { + List channelList = commonGBChannelMapper.queryByCivilCode(civilCode); + if (channelList.isEmpty()) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "所有通道Id不存在"); + } + int result = commonGBChannelMapper.removeCivilCodeByChannels(channelList); + // TODO 发送通知 +// if (result > 0) { +// try { +// // 发送catalog +// eventPublisher.catalogEventPublish(null, channelList, CatalogEvent.UPDATE); +// }catch (Exception e) { +// log.warn("[多个通道添加行政区划] 发送失败,数量:{}", channelList.size(), e); +// } +// } + } + + @Override + public void deleteChannelToRegionByChannelIds(List channelIds) { + List channelList = commonGBChannelMapper.queryByIds(channelIds); + if (channelList.isEmpty()) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "所有通道Id不存在"); + } + int result = commonGBChannelMapper.removeCivilCodeByChannels(channelList); + // TODO 发送通知 +// if (result > 0) { +// try { +// // 发送catalog +// eventPublisher.catalogEventPublish(null, channelList, CatalogEvent.UPDATE); +// }catch (Exception e) { +// log.warn("[多个通道添加行政区划] 发送失败,数量:{}", channelList.size(), e); +// } +// } + } + + @Override + public void addChannelToRegionByGbDevice(String civilCode, List deviceIds) { + List channelList = commonGBChannelMapper.queryByGbDeviceIds(deviceIds); + if (channelList.isEmpty()) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "所有通道Id不存在"); + } + for (CommonGBChannel channel : channelList) { + channel.setGbCivilCode(civilCode); + } + int result = commonGBChannelMapper.updateRegion(civilCode, channelList); + // 发送通知 + if (result > 0) { + try { + // 发送catalog + eventPublisher.catalogEventPublish(null, channelList, CatalogEvent.UPDATE); + } catch (Exception e) { + log.warn("[多个通道添加行政区划] 发送失败,数量:{}", channelList.size(), e); + } + } + } + + @Override + public void deleteChannelToRegionByGbDevice(List deviceIds) { + List channelList = commonGBChannelMapper.queryByGbDeviceIds(deviceIds); + if (channelList.isEmpty()) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "所有通道Id不存在"); + } + int result = commonGBChannelMapper.removeCivilCodeByChannels(channelList); + } + + @Override + @Transactional + public void removeParentIdByBusinessGroup(String businessGroup) { + List channelList = commonGBChannelMapper.queryByBusinessGroup(businessGroup); + if (channelList.isEmpty()) { + return; + } + int result = commonGBChannelMapper.removeParentIdByChannels(channelList); + List groupList = groupMapper.queryByBusinessGroup(businessGroup); + platformChannelService.checkGroupRemove(channelList, groupList); + + } + + @Override + public void removeParentIdByGroupList(List groupList) { + List channelList = commonGBChannelMapper.queryByGroupList(groupList); + if (channelList.isEmpty()) { + return; + } + commonGBChannelMapper.removeParentIdByChannels(channelList); + platformChannelService.checkGroupRemove(channelList, groupList); + } + + @Override + public void updateBusinessGroup(String oldBusinessGroup, String newBusinessGroup) { + List channelList = commonGBChannelMapper.queryByBusinessGroup(oldBusinessGroup); + Assert.notEmpty(channelList, "旧的业务分组的通道不存在"); + + int result = commonGBChannelMapper.updateBusinessGroupByChannelList(newBusinessGroup, channelList); + if (result > 0) { + for (CommonGBChannel channel : channelList) { + channel.setGbBusinessGroupId(newBusinessGroup); + } + // 发送catalog + try { + eventPublisher.catalogEventPublish(null, channelList, CatalogEvent.UPDATE); + } catch (Exception e) { + log.warn("[多个通道业务分组] 发送失败,数量:{}", channelList.size(), e); + } + } + } + + @Override + public void updateParentIdGroup(String oldParentId, String newParentId) { + List channelList = commonGBChannelMapper.queryByParentId(oldParentId); + if (channelList.isEmpty()) { + return; + } + + int result = commonGBChannelMapper.updateParentIdByChannelList(newParentId, channelList); + if (result > 0) { + for (CommonGBChannel channel : channelList) { + channel.setGbParentId(newParentId); + } + // 发送catalog + try { + eventPublisher.catalogEventPublish(null, channelList, CatalogEvent.UPDATE); + } catch (Exception e) { + log.warn("[多个通道业务分组] 发送失败,数量:{}", channelList.size(), e); + } + } + } + + @Override + @Transactional + public void addChannelToGroup(String parentId, String businessGroup, List channelIds) { + List channelList = commonGBChannelMapper.queryByIds(channelIds); + if (channelList.isEmpty()) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "所有通道Id不存在"); + } + int result = commonGBChannelMapper.updateGroup(parentId, businessGroup, channelList); + for (CommonGBChannel commonGBChannel : channelList) { + commonGBChannel.setGbParentId(parentId); + commonGBChannel.setGbBusinessGroupId(businessGroup); + } + + // 发送通知 + if (result > 0) { + platformChannelService.checkGroupAdd(channelList); + try { + // 发送catalog + eventPublisher.catalogEventPublish(null, channelList, CatalogEvent.UPDATE); + } catch (Exception e) { + log.warn("[多个通道添加行政区划] 发送失败,数量:{}", channelList.size(), e); + } + } + } + + @Override + public void deleteChannelToGroup(String parentId, String businessGroup, List channelIds) { + List channelList = commonGBChannelMapper.queryByIds(channelIds); + if (channelList.isEmpty()) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "所有通道Id不存在"); + } + commonGBChannelMapper.removeParentIdByChannels(channelList); + + Group group = groupMapper.queryOneByDeviceId(parentId, businessGroup); + if (group == null) { + platformChannelService.checkGroupRemove(channelList, null); + }else { + List groupList = new ArrayList<>(); + groupList.add(group); + platformChannelService.checkGroupRemove(channelList, groupList); + } + } + + @Override + @Transactional + public void addChannelToGroupByGbDevice(String parentId, String businessGroup, List deviceIds) { + List channelList = commonGBChannelMapper.queryByGbDeviceIds(deviceIds); + if (channelList.isEmpty()) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "所有通道Id不存在"); + } + for (CommonGBChannel channel : channelList) { + channel.setGbParentId(parentId); + channel.setGbBusinessGroupId(businessGroup); + } + int result = commonGBChannelMapper.updateGroup(parentId, businessGroup, channelList); + + for (CommonGBChannel commonGBChannel : channelList) { + commonGBChannel.setGbParentId(parentId); + commonGBChannel.setGbBusinessGroupId(businessGroup); + } + // 发送通知 + if (result > 0) { + platformChannelService.checkGroupAdd(channelList); + try { + // 发送catalog + eventPublisher.catalogEventPublish(null, channelList, CatalogEvent.UPDATE); + } catch (Exception e) { + log.warn("[多个通道添加行政区划] 发送失败,数量:{}", channelList.size(), e); + } + } + } + + @Override + public void deleteChannelToGroupByGbDevice(List deviceIds) { + List channelList = commonGBChannelMapper.queryByGbDeviceIds(deviceIds); + if (channelList.isEmpty()) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "所有通道Id不存在"); + } + commonGBChannelMapper.removeParentIdByChannels(channelList); + platformChannelService.checkGroupRemove(channelList, null); + } + + @Override + public CommonGBChannel queryOneWithPlatform(Integer platformId, String channelDeviceId) { + // 防止共享的通道编号重复 + List channelList = platformChannelMapper.queryOneWithPlatform(platformId, channelDeviceId); + if (!channelList.isEmpty()) { + return channelList.get(channelList.size() - 1); + }else { + return null; + } + } + + @Override + public void updateCivilCode(String oldCivilCode, String newCivilCode) { + List channelList = commonGBChannelMapper.queryByCivilCode(oldCivilCode); + if (channelList.isEmpty()) { + return; + } + + int result = commonGBChannelMapper.updateCivilCodeByChannelList(newCivilCode, channelList); + if (result > 0) { + for (CommonGBChannel channel : channelList) { + channel.setGbCivilCode(newCivilCode); + } + // 发送catalog + try { + eventPublisher.catalogEventPublish(null, channelList, CatalogEvent.UPDATE); + } catch (Exception e) { + log.warn("[多个通道业务分组] 发送失败,数量:{}", channelList.size(), e); + } + } + } + + @Override + public List queryListByStreamPushList(List streamPushList) { + return commonGBChannelMapper.queryListByStreamPushList(streamPushList); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GroupServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GroupServiceImpl.java new file mode 100644 index 000000000..ebb631cbf --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GroupServiceImpl.java @@ -0,0 +1,250 @@ +package com.genersoft.iot.vmp.gb28181.service.impl; + +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.dao.CommonGBChannelMapper; +import com.genersoft.iot.vmp.gb28181.dao.GroupMapper; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; +import com.genersoft.iot.vmp.gb28181.service.IGroupService; +import com.genersoft.iot.vmp.utils.DateUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 区域管理类 + */ +@Service +@Slf4j +public class GroupServiceImpl implements IGroupService { + + @Autowired + private GroupMapper groupManager; + + @Autowired + private CommonGBChannelMapper commonGBChannelMapper; + + @Autowired + private IGbChannelService gbChannelService; + + @Autowired + private EventPublisher eventPublisher; + + @Override + public void add(Group group) { + Assert.notNull(group, "参数不可为NULL"); + Assert.notNull(group.getDeviceId(), "设备编号不可为NULL"); + Assert.isTrue(group.getDeviceId().trim().length() == 20, "设备编号必须为20位"); + Assert.notNull(group.getName(), "设备编号不可为NULL"); + + GbCode gbCode = GbCode.decode(group.getDeviceId()); + Assert.notNull(gbCode, "设备编号不满足国标定义"); + if ("215".equals(gbCode.getTypeCode())){ + // 添加业务分组 + addBusinessGroup(group); + }else { + Assert.isTrue("216".equals(gbCode.getTypeCode()), "创建虚拟组织时设备编号11-13位应使用216"); + // 添加虚拟组织 + addGroup(group); + } + } + + private void addGroup(Group group) { + // 建立虚拟组织 + Assert.notNull(group.getBusinessGroup(), "所属的业务分组分组不存在"); + Group businessGroup = groupManager.queryBusinessGroup(group.getBusinessGroup()); + Assert.notNull(businessGroup, "所属的业务分组分组不存在"); + if (!ObjectUtils.isEmpty(group.getParentDeviceId())) { + Group parentGroup = groupManager.queryOneByDeviceId(group.getParentDeviceId(), group.getBusinessGroup()); + Assert.notNull(parentGroup, "所属的上级分组分组不存在"); + }else { + group.setParentDeviceId(null); + } + group.setCreateTime(DateUtil.getNow()); + group.setUpdateTime(DateUtil.getNow()); + groupManager.add(group); + } + + private void addBusinessGroup(Group group) { + group.setBusinessGroup(group.getDeviceId()); + group.setCreateTime(DateUtil.getNow()); + group.setUpdateTime(DateUtil.getNow()); + groupManager.addBusinessGroup(group); + } + + private List queryAllChildren(Integer id) { + List children = groupManager.getChildren(id); + if (ObjectUtils.isEmpty(children)) { + return children; + } + for (int i = 0; i < children.size(); i++) { + children.addAll(queryAllChildren(children.get(i).getId())); + } + return children; + } + + @Override + @Transactional + public void update(Group group) { + Assert.isTrue(group.getId()> 0, "更新必须携带分组ID"); + Assert.notNull(group.getDeviceId(), "编号不可为NULL"); + Assert.notNull(group.getBusinessGroup(), "业务分组不可为NULL"); + Group groupInDb = groupManager.queryOne(group.getId()); + Assert.notNull(groupInDb, "分组不存在"); + + group.setName(group.getName()); + group.setUpdateTime(DateUtil.getNow()); + groupManager.update(group); + // 修改他的子节点 + if (!group.getDeviceId().equals(groupInDb.getDeviceId()) + || !group.getBusinessGroup().equals(groupInDb.getBusinessGroup())) { + List groupList = queryAllChildren(groupInDb.getId()); + if (!groupList.isEmpty()) { + int result = groupManager.updateChild(groupInDb.getId(), group); + if (result > 0) { + for (Group chjildGroup : groupList) { + chjildGroup.setParentDeviceId(group.getDeviceId()); + chjildGroup.setBusinessGroup(group.getBusinessGroup()); + // 将变化信息发送通知 + CommonGBChannel channel = CommonGBChannel.build(chjildGroup); + try { + // 发送catalog + eventPublisher.catalogEventPublish(null, channel, CatalogEvent.UPDATE); + }catch (Exception e) { + log.warn("[业务分组/虚拟组织变化] 发送失败,{}", group.getDeviceId(), e); + } + } + } + } + } + // 将变化信息发送通知 + CommonGBChannel channel = CommonGBChannel.build(group); + try { + // 发送catalog + eventPublisher.catalogEventPublish(null, channel, CatalogEvent.UPDATE); + }catch (Exception e) { + log.warn("[业务分组/虚拟组织变化] 发送失败,{}", group.getDeviceId(), e); + } + + // 由于编号变化,会需要处理太多内容以及可能发送大量消息,所以目前更新只只支持重命名 + GbCode decode = GbCode.decode(group.getDeviceId()); + if (!groupInDb.getDeviceId().equals(group.getDeviceId())) { + if (decode.getTypeCode().equals("215")) { + // 业务分组变化。需要将其下的所有业务分组修改 + gbChannelService.updateBusinessGroup(groupInDb.getDeviceId(), group.getDeviceId()); + }else { + // 虚拟组织修改,需要把其下的子节点修改父节点ID + gbChannelService.updateParentIdGroup(groupInDb.getDeviceId(), group.getDeviceId()); + } + } + } + + @Override + public Group queryGroupByDeviceId(String regionDeviceId) { + return null; + } + + @Override + public List queryForTree(String query, Integer parentId) { + + List groupTrees = groupManager.queryForTree(query, parentId); + if (parentId == null) { + return groupTrees; + } + // 查询含有的通道 + Group parentGroup = groupManager.queryOne(parentId); + if (parentGroup != null ) { + List groupTreesForChannel = commonGBChannelMapper.queryForGroupTreeByParentId(query, parentGroup.getDeviceId()); + if (!ObjectUtils.isEmpty(groupTreesForChannel)) { + groupTrees.addAll(groupTreesForChannel); + } + } + return groupTrees; + } + + @Override + public void syncFromChannel() { + + } + + @Override + @Transactional + public boolean delete(int id) { + Group group = groupManager.queryOne(id); + Assert.notNull(group, "分组不存在"); + List groupListForDelete = new ArrayList<>(); + GbCode gbCode = GbCode.decode(group.getDeviceId()); + if (gbCode.getTypeCode().equals("215")) { + List groupList = groupManager.queryByBusinessGroup(group.getDeviceId()); + if (!groupList.isEmpty()) { + groupListForDelete.addAll(groupList); + } + // 业务分组 + gbChannelService.removeParentIdByBusinessGroup(group.getDeviceId()); + }else { + List groupList = queryAllChildren(group.getId()); + if (!groupList.isEmpty()) { + groupListForDelete.addAll(groupList); + } + groupListForDelete.add(group); + gbChannelService.removeParentIdByGroupList(groupListForDelete); + } + groupManager.batchDelete(groupListForDelete); + + for (Group groupForDelete : groupListForDelete) { + // 删除平台关联的分组信息。同时发送通知 + List platformList = groupManager.queryForPlatformByGroupId(groupForDelete.getId()); + if ( !platformList.isEmpty()) { + groupManager.deletePlatformGroup(groupForDelete.getId()); + // 将变化信息发送通知 + CommonGBChannel channel = CommonGBChannel.build(groupForDelete); + for (Platform platform : platformList) { + try { + // 发送catalog + eventPublisher.catalogEventPublish(platform.getId(), channel, CatalogEvent.DEL); + }catch (Exception e) { + log.warn("[业务分组/虚拟组织删除] 发送失败,{}", groupForDelete.getDeviceId(), e); + } + } + } + } + return true; + } + + @Override + @Transactional + public boolean batchAdd(List groupList) { + if (groupList== null || groupList.isEmpty()) { + return false; + } + Map groupMapForVerification = new HashMap<>(); + for (Group group : groupList) { + groupMapForVerification.put(group.getDeviceId(), group); + } + // 查询数据库中已经存在的. + List groupListInDb = groupManager.queryInGroupListByDeviceId(groupList); + if (!groupListInDb.isEmpty()) { + for (Group group : groupListInDb) { + groupMapForVerification.remove(group.getDeviceId()); + } + } + if (!groupMapForVerification.isEmpty()) { + List groupListForAdd = new ArrayList<>(groupMapForVerification.values()); + groupManager.batchAdd(groupListForAdd); + // 更新分组关系 + groupManager.updateParentId(groupListForAdd); + groupManager.updateParentIdWithBusinessGroup(groupListForAdd); + } + + return true; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/InviteStreamServiceImpl.java similarity index 63% rename from src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/InviteStreamServiceImpl.java index e5847f69a..3ca087065 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/InviteStreamServiceImpl.java @@ -1,55 +1,48 @@ -package com.genersoft.iot.vmp.service.impl; +package com.genersoft.iot.vmp.gb28181.service.impl; import com.alibaba.fastjson2.JSON; import com.baomidou.dynamic.datasource.annotation.DS; -import com.genersoft.iot.vmp.common.InviteInfo; -import com.genersoft.iot.vmp.common.InviteSessionStatus; -import com.genersoft.iot.vmp.common.InviteSessionType; -import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent; +import com.genersoft.iot.vmp.common.*; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.dao.DeviceChannelMapper; +import com.genersoft.iot.vmp.gb28181.dao.DeviceMapper; +import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent; -import com.genersoft.iot.vmp.service.IInviteStreamService; import com.genersoft.iot.vmp.service.bean.ErrorCallback; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.utils.redis.RedisUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; +@Slf4j @Service @DS("master") public class InviteStreamServiceImpl implements IInviteStreamService { - private final Logger logger = LoggerFactory.getLogger(InviteStreamServiceImpl.class); - - private final Map>> inviteErrorCallbackMap = new ConcurrentHashMap<>(); + private final Map>> inviteErrorCallbackMap = new ConcurrentHashMap<>(); @Autowired private RedisTemplate redisTemplate; @Autowired - private IVideoManagerStorage storage; + private UserSetting userSetting; - /** - * 流到来的处理 - */ - @Async("taskExecutor") - @org.springframework.context.event.EventListener - public void onApplicationEvent(MediaArrivalEvent event) { -// if ("rtsp".equals(event.getSchema()) && "rtp".equals(event.getApp())) { -// -// } - } + @Autowired + private DeviceMapper deviceMapper; + + @Autowired + private DeviceChannelMapper deviceChannelMapper; /** * 流离开的处理 @@ -61,22 +54,29 @@ public class InviteStreamServiceImpl implements IInviteStreamService { InviteInfo inviteInfo = getInviteInfoByStream(null, event.getStream()); if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) { removeInviteInfo(inviteInfo); - storage.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId()); + Device device = deviceMapper.getDeviceByDeviceId(inviteInfo.getDeviceId()); + if (device != null) { + deviceChannelMapper.stopPlayById(inviteInfo.getChannelId()); + } } } } @Override public void updateInviteInfo(InviteInfo inviteInfo) { - updateInviteInfo(inviteInfo, null); + if (InviteSessionStatus.ready == inviteInfo.getStatus()) { + updateInviteInfo(inviteInfo, Long.valueOf(userSetting.getPlayTimeout()) * 2); + }else { + updateInviteInfo(inviteInfo, null); + } } @Override public void updateInviteInfo(InviteInfo inviteInfo, Long time) { if (inviteInfo == null || (inviteInfo.getDeviceId() == null || inviteInfo.getChannelId() == null)) { - logger.warn("[更新Invite信息],参数不全: {}", JSON.toJSON(inviteInfo)); + log.warn("[更新Invite信息],参数不全: {}", JSON.toJSON(inviteInfo)); return; } - InviteInfo inviteInfoForUpdate = null; + InviteInfo inviteInfoForUpdate; if (InviteSessionStatus.ready == inviteInfo.getStatus()) { if (inviteInfo.getDeviceId() == null @@ -88,10 +88,9 @@ public class InviteStreamServiceImpl implements IInviteStreamService { } inviteInfoForUpdate = inviteInfo; } else { - InviteInfo inviteInfoInRedis = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), - inviteInfo.getChannelId(), inviteInfo.getStream()); + InviteInfo inviteInfoInRedis = getInviteInfo(inviteInfo.getType(), inviteInfo.getChannelId(), inviteInfo.getStream()); if (inviteInfoInRedis == null) { - logger.warn("[更新Invite信息],未从缓存中读取到Invite信息: deviceId: {}, channel: {}, stream: {}", + log.warn("[更新Invite信息],未从缓存中读取到Invite信息: deviceId: {}, channel: {}, stream: {}", inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); return; } @@ -133,7 +132,7 @@ public class InviteStreamServiceImpl implements IInviteStreamService { @Override public InviteInfo updateInviteInfoForStream(InviteInfo inviteInfo, String stream) { - InviteInfo inviteInfoInDb = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); + InviteInfo inviteInfoInDb = getInviteInfo(inviteInfo.getType(), inviteInfo.getChannelId(), inviteInfo.getStream()); if (inviteInfoInDb == null) { return null; } @@ -148,15 +147,20 @@ public class InviteStreamServiceImpl implements IInviteStreamService { if (inviteInfoInDb.getSsrcInfo() != null) { inviteInfoInDb.getSsrcInfo().setStream(stream); } - redisTemplate.opsForValue().set(key, inviteInfoInDb); + if (InviteSessionStatus.ready == inviteInfo.getStatus()) { + redisTemplate.opsForValue().set(key, inviteInfoInDb, userSetting.getPlayTimeout() * 2, TimeUnit.SECONDS); + }else { + redisTemplate.opsForValue().set(key, inviteInfoInDb); + } + return inviteInfoInDb; } @Override - public InviteInfo getInviteInfo(InviteSessionType type, String deviceId, String channelId, String stream) { + public InviteInfo getInviteInfo(InviteSessionType type, Integer channelId, String stream) { String key = VideoManagerConstants.INVITE_PREFIX + ":" + (type != null ? type : "*") + - ":" + (deviceId != null ? deviceId : "*") + + ":*" + ":" + (channelId != null ? channelId : "*") + ":" + (stream != null ? stream : "*") + ":*"; @@ -165,32 +169,51 @@ public class InviteStreamServiceImpl implements IInviteStreamService { return null; } if (scanResult.size() != 1) { - logger.warn("[获取InviteInfo] 发现 key: {}存在多条", key); + log.warn("[获取InviteInfo] 发现 key: {}存在多条", key); } return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0)); } @Override - public InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, String deviceId, String channelId) { - return getInviteInfo(type, deviceId, channelId, null); + public List getAllInviteInfo(InviteSessionType type, Integer channelId, String stream) { + String key = VideoManagerConstants.INVITE_PREFIX + + ":" + (type != null ? type : "*") + + ":*" + + ":" + (channelId != null ? channelId : "*") + + ":" + (stream != null ? stream : "*") + + ":*"; + List scanResult = RedisUtil.scan(redisTemplate, key); + if (scanResult.isEmpty()) { + return new ArrayList<>(); + } + List result = new ArrayList<>(); + for (Object keyObj : scanResult) { + result.add((InviteInfo) redisTemplate.opsForValue().get(keyObj)); + } + return result; + } + + @Override + public InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, Integer channelId) { + return getInviteInfo(type, channelId, null); } @Override public InviteInfo getInviteInfoByStream(InviteSessionType type, String stream) { - return getInviteInfo(type, null, null, stream); + return getInviteInfo(type, null, stream); } @Override - public void removeInviteInfo(InviteSessionType type, String deviceId, String channelId, String stream) { + public void removeInviteInfo(InviteSessionType type, Integer channelId, String stream) { String scanKey = VideoManagerConstants.INVITE_PREFIX + ":" + (type != null ? type : "*") + - ":" + (deviceId != null ? deviceId : "*") + + ":*" + ":" + (channelId != null ? channelId : "*") + ":" + (stream != null ? stream : "*") + ":*"; List scanResult = RedisUtil.scan(redisTemplate, scanKey); - if (scanResult.size() > 0) { + if (!scanResult.isEmpty()) { for (Object keyObj : scanResult) { String key = (String) keyObj; InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(key); @@ -198,35 +221,31 @@ public class InviteStreamServiceImpl implements IInviteStreamService { continue; } redisTemplate.delete(key); - inviteErrorCallbackMap.remove(buildKey(type, deviceId, channelId, inviteInfo.getStream())); + inviteErrorCallbackMap.remove(buildKey(type,channelId, inviteInfo.getStream())); } } } @Override - public void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId) { - removeInviteInfo(inviteSessionType, deviceId, channelId, null); + public void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, Integer channelId) { + removeInviteInfo(inviteSessionType, channelId, null); } @Override public void removeInviteInfo(InviteInfo inviteInfo) { - removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); + removeInviteInfo(inviteInfo.getType(), inviteInfo.getChannelId(), inviteInfo.getStream()); } @Override - public void once(InviteSessionType type, String deviceId, String channelId, String stream, ErrorCallback callback) { - String key = buildKey(type, deviceId, channelId, stream); - List> callbacks = inviteErrorCallbackMap.get(key); - if (callbacks == null) { - callbacks = new CopyOnWriteArrayList<>(); - inviteErrorCallbackMap.put(key, callbacks); - } + public void once(InviteSessionType type, Integer channelId, String stream, ErrorCallback callback) { + String key = buildKey(type, channelId, stream); + List> callbacks = inviteErrorCallbackMap.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>()); callbacks.add(callback); } - private String buildKey(InviteSessionType type, String deviceId, String channelId, String stream) { - String key = type + ":" + deviceId + ":" + channelId; + private String buildKey(InviteSessionType type, Integer channelId, String stream) { + String key = type + ":" + channelId; // 如果ssrc未null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite if (stream != null) { key += (":" + stream); @@ -237,7 +256,12 @@ public class InviteStreamServiceImpl implements IInviteStreamService { @Override public void clearInviteInfo(String deviceId) { - removeInviteInfo(null, deviceId, null, null); + List inviteInfoList = getAllInviteInfo(null, null, null); + for (InviteInfo inviteInfo : inviteInfoList) { + if (inviteInfo.getDeviceId().equals(deviceId)) { + removeInviteInfo(inviteInfo); + } + } } @Override @@ -245,13 +269,16 @@ public class InviteStreamServiceImpl implements IInviteStreamService { int count = 0; String key = VideoManagerConstants.INVITE_PREFIX + ":*:*:*:*:*"; List scanResult = RedisUtil.scan(redisTemplate, key); - if (scanResult.size() == 0) { + if (scanResult.isEmpty()) { return 0; }else { for (Object keyObj : scanResult) { String keyStr = (String) keyObj; InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(keyStr); - if (inviteInfo != null && inviteInfo.getStreamInfo() != null && inviteInfo.getStreamInfo().getMediaServerId().equals(mediaServerId)) { + if (inviteInfo != null + && inviteInfo.getStreamInfo() != null + && inviteInfo.getStreamInfo().getMediaServer() != null + && inviteInfo.getStreamInfo().getMediaServer().getId().equals(mediaServerId)) { if (inviteInfo.getType().equals(InviteSessionType.DOWNLOAD) && inviteInfo.getStreamInfo().getProgress() == 1) { continue; } @@ -263,21 +290,23 @@ public class InviteStreamServiceImpl implements IInviteStreamService { } @Override - public void call(InviteSessionType type, String deviceId, String channelId, String stream, int code, String msg, Object data) { - String key = buildSubStreamKey(type, deviceId, channelId, stream); - List> callbacks = inviteErrorCallbackMap.get(key); - if (callbacks == null) { + public void call(InviteSessionType type, Integer channelId, String stream, int code, String msg, StreamInfo data) { + String key = buildSubStreamKey(type, channelId, stream); + List> callbacks = inviteErrorCallbackMap.get(key); + if (callbacks == null || callbacks.isEmpty()) { return; } - for (ErrorCallback callback : callbacks) { - callback.run(code, msg, data); + for (ErrorCallback callback : callbacks) { + if (callback != null) { + callback.run(code, msg, data); + } } inviteErrorCallbackMap.remove(key); } - private String buildSubStreamKey(InviteSessionType type, String deviceId, String channelId, String stream) { - String key = type + ":" + ":" + deviceId + ":" + channelId; + private String buildSubStreamKey(InviteSessionType type, Integer channelId, String stream) { + String key = type + ":" + channelId; // 如果ssrc为null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite if (stream != null) { key += (":" + stream); @@ -298,7 +327,7 @@ public class InviteStreamServiceImpl implements IInviteStreamService { @Override public InviteInfo updateInviteInfoForSSRC(InviteInfo inviteInfo, String ssrc) { - InviteInfo inviteInfoInDb = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); + InviteInfo inviteInfoInDb = getInviteInfo(inviteInfo.getType(), inviteInfo.getChannelId(), inviteInfo.getStream()); if (inviteInfoInDb == null) { return null; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformChannelServiceImpl.java new file mode 100755 index 000000000..54b43ae64 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformChannelServiceImpl.java @@ -0,0 +1,517 @@ +package com.genersoft.iot.vmp.gb28181.service.impl; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.dao.*; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; +import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; +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.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.*; + +/** + * @author lin + */ +@Slf4j +@Service +@DS("master") +public class PlatformChannelServiceImpl implements IPlatformChannelService { + + @Autowired + private PlatformChannelMapper platformChannelMapper; + + @Autowired + private EventPublisher eventPublisher; + + @Autowired + private GroupMapper groupMapper; + + + @Autowired + private RegionMapper regionMapper; + + @Autowired + private CommonGBChannelMapper commonGBChannelMapper; + + @Autowired + private PlatformMapper platformMapper; + + @Autowired + private ISIPCommanderForPlatform sipCommanderFroPlatform; + + + + + @Override + public PageInfo queryChannelList(int page, int count, String query, Integer channelType, Boolean online, Integer platformId, Boolean hasShare) { + PageHelper.startPage(page, count); + List all = platformChannelMapper.queryForPlatformForWebList(platformId, query, channelType, online, hasShare); + return new PageInfo<>(all); + } + + /** + * 获取通道使用的分组中未分享的 + */ + @Transactional + public Set getGroupNotShareByChannelList(List channelList, Integer platformId) { + // 获取分组中未分享的节点 + Set groupList = groupMapper.queryNotShareGroupForPlatformByChannelList(channelList, platformId); + // 获取这些节点的所有父节点 + if (groupList.isEmpty()) { + return new HashSet<>(); + } + Set allGroup = getAllGroup(groupList); + allGroup.addAll(groupList); + // 获取全部节点中未分享的 + return groupMapper.queryNotShareGroupForPlatformByGroupList(allGroup, platformId); + } + + /** + * 获取通道使用的分组中未分享的 + */ + private Set getRegionNotShareByChannelList(List channelList, Integer platformId) { + // 获取分组中未分享的节点 + Set regionSet = regionMapper.queryNotShareRegionForPlatformByChannelList(channelList, platformId); + // 获取这些节点的所有父节点 + if (regionSet.isEmpty()) { + return new HashSet<>(); + } + Set allRegion = getAllRegion(regionSet); + allRegion.addAll(regionSet); + // 获取全部节点中未分享的 + return regionMapper.queryNotShareRegionForPlatformByRegionList(allRegion, platformId); + } + + + + /** + * 移除空的共享,并返回移除的分组 + */ + @Transactional + public Set deleteEmptyGroup(Set groupSet, Integer platformId) { + Iterator iterator = groupSet.iterator(); + while (iterator.hasNext()) { + Group group = iterator.next(); + // groupSet 为当前通道直接使用的分组,如果已经没有子分组与其他的通道,则可以移除 + // 获取分组子节点 + Set children = platformChannelMapper.queryShareChildrenGroup(group.getId(), platformId); + if (!children.isEmpty()) { + iterator.remove(); + continue; + } + // 获取分组关联的通道 + List channelList = commonGBChannelMapper.queryShareChannelByParentId(group.getDeviceId(), platformId); + if (!channelList.isEmpty()) { + iterator.remove(); + continue; + } + platformChannelMapper.removePlatformGroupById(group.getId(), platformId); + } + // 如果空了,说明没有通道需要处理了 + if (groupSet.isEmpty()) { + return new HashSet<>(); + } + Set parent = platformChannelMapper.queryShareParentGroupByGroupSet(groupSet, platformId); + if (parent.isEmpty()) { + return groupSet; + }else { + Set parentGroupSet = deleteEmptyGroup(parent, platformId); + groupSet.addAll(parentGroupSet); + return groupSet; + } + } + + /** + * 移除空的共享,并返回移除的行政区划 + */ + private Set deleteEmptyRegion(Set regionSet, Integer platformId) { + Iterator iterator = regionSet.iterator(); + while (iterator.hasNext()) { + Region region = iterator.next(); + // groupSet 为当前通道直接使用的分组,如果已经没有子分组与其他的通道,则可以移除 + // 获取分组子节点 + Set children = platformChannelMapper.queryShareChildrenRegion(region.getDeviceId(), platformId); + if (!children.isEmpty()) { + iterator.remove(); + continue; + } + // 获取分组关联的通道 + List channelList = commonGBChannelMapper.queryShareChannelByCivilCode(region.getDeviceId(), platformId); + if (!channelList.isEmpty()) { + iterator.remove(); + continue; + } + platformChannelMapper.removePlatformRegionById(region.getId(), platformId); + } + // 如果空了,说明没有通道需要处理了 + if (regionSet.isEmpty()) { + return new HashSet<>(); + } + Set parent = platformChannelMapper.queryShareParentRegionByRegionSet(regionSet, platformId); + if (parent.isEmpty()) { + return regionSet; + }else { + Set parentGroupSet = deleteEmptyRegion(parent, platformId); + regionSet.addAll(parentGroupSet); + return regionSet; + } + } + + private Set getAllGroup(Set groupList ) { + if (groupList.isEmpty()) { + return new HashSet<>(); + } + Set channelList = groupMapper.queryParentInChannelList(groupList); + if (channelList.isEmpty()) { + return channelList; + } + Set allParentRegion = getAllGroup(channelList); + channelList.addAll(allParentRegion); + return channelList; + } + + private Set getAllRegion(Set regionSet ) { + if (regionSet.isEmpty()) { + return new HashSet<>(); + } + + Set channelList = regionMapper.queryParentInChannelList(regionSet); + if (channelList.isEmpty()) { + return channelList; + } + Set allParentRegion = getAllRegion(channelList); + channelList.addAll(allParentRegion); + return channelList; + } + + @Override + @Transactional + public int addAllChannel(Integer platformId) { + List channelListNotShare = platformChannelMapper.queryNotShare(platformId, null); + Assert.notEmpty(channelListNotShare, "所有通道已共享"); + return addChannelList(platformId, channelListNotShare); + } + + @Override + @Transactional + public int addChannels(Integer platformId, List channelIds) { + List channelListNotShare = platformChannelMapper.queryNotShare(platformId, channelIds); + Assert.notEmpty(channelListNotShare, "通道已共享"); + return addChannelList(platformId, channelListNotShare); + } + + @Transactional + public int addChannelList(Integer platformId, List channelList) { + int result = platformChannelMapper.addChannels(platformId, channelList); + if (result > 0) { + // 查询通道相关的行政区划信息是否共享,如果没共享就添加 + Set regionListNotShare = getRegionNotShareByChannelList(channelList, platformId); + if (!regionListNotShare.isEmpty()) { + int addGroupResult = platformChannelMapper.addPlatformRegion(new ArrayList<>(regionListNotShare), platformId); + if (addGroupResult > 0) { + for (Region region : regionListNotShare) { + // 分组信息排序时需要将顶层排在最后 + channelList.add(0, CommonGBChannel.build(region)); + } + } + } + + // 查询通道相关的分组信息是否共享,如果没共享就添加 + Set groupListNotShare = getGroupNotShareByChannelList(channelList, platformId); + if (!groupListNotShare.isEmpty()) { + int addGroupResult = platformChannelMapper.addPlatformGroup(new ArrayList<>(groupListNotShare), platformId); + if (addGroupResult > 0) { + for (Group group : groupListNotShare) { + // 分组信息排序时需要将顶层排在最后 + channelList.add(0, CommonGBChannel.build(group)); + } + } + } + + // 发送消息 + try { + // 发送catalog + eventPublisher.catalogEventPublish(platformId, channelList, CatalogEvent.ADD); + } catch (Exception e) { + log.warn("[关联通道] 发送失败,数量:{}", channelList.size(), e); + } + } + return result; + } + + @Override + public int removeAllChannel(Integer platformId) { + List channelListShare = platformChannelMapper.queryShare(platformId, null); + Assert.notEmpty(channelListShare, "未共享任何通道"); + int result = platformChannelMapper.removeChannelsWithPlatform(platformId, channelListShare); + if (result > 0) { + // 查询通道相关的分组信息 + Set regionSet = regionMapper.queryByChannelList(channelListShare); + Set deleteRegion = deleteEmptyRegion(regionSet, platformId); + if (!deleteRegion.isEmpty()) { + for (Region region : deleteRegion) { + channelListShare.add(0, CommonGBChannel.build(region)); + } + } + + // 查询通道相关的分组信息 + Set groupSet = groupMapper.queryByChannelList(channelListShare); + Set deleteGroup = deleteEmptyGroup(groupSet, platformId); + if (!deleteGroup.isEmpty()) { + for (Group group : deleteGroup) { + channelListShare.add(0, CommonGBChannel.build(group)); + } + } + // 发送消息 + try { + // 发送catalog + eventPublisher.catalogEventPublish(platformId, channelListShare, CatalogEvent.DEL); + } catch (Exception e) { + log.warn("[移除全部关联通道] 发送失败,数量:{}", channelListShare.size(), e); + } + } + return result; + } + + @Override + @Transactional + public void addChannelByDevice(Integer platformId, List deviceIds) { + List channelList = commonGBChannelMapper.queryByGbDeviceIdsForIds(deviceIds); + addChannels(platformId, channelList); + } + + @Override + @Transactional + public void removeChannelByDevice(Integer platformId, List deviceIds) { + List channelList = commonGBChannelMapper.queryByGbDeviceIdsForIds(deviceIds); + removeChannels(platformId, channelList); + } + + @Transactional + public int removeChannelList(Integer platformId, List channelList) { + int result = platformChannelMapper.removeChannelsWithPlatform(platformId, channelList); + if (result > 0) { + // 查询通道相关的分组信息 + Set regionSet = regionMapper.queryByChannelList(channelList); + Set deleteRegion = deleteEmptyRegion(regionSet, platformId); + if (!deleteRegion.isEmpty()) { + for (Region region : deleteRegion) { + channelList.add(0, CommonGBChannel.build(region)); + } + } + + // 查询通道相关的分组信息 + Set groupSet = groupMapper.queryByChannelList(channelList); + Set deleteGroup = deleteEmptyGroup(groupSet, platformId); + if (!deleteGroup.isEmpty()) { + for (Group group : deleteGroup) { + channelList.add(0, CommonGBChannel.build(group)); + } + } + // 发送消息 + try { + // 发送catalog + eventPublisher.catalogEventPublish(platformId, channelList, CatalogEvent.DEL); + } catch (Exception e) { + log.warn("[移除关联通道] 发送失败,数量:{}", channelList.size(), e); + } + } + return result; + } + + @Override + @Transactional + public int removeChannels(Integer platformId, List channelIds) { + List channelList = platformChannelMapper.queryShare(platformId, channelIds); + if (channelList.isEmpty()) { + return 0; + } + return removeChannelList(platformId, channelList); + } + + @Override + @Transactional + public void removeChannels(List ids) { + List platformList = platformChannelMapper.queryPlatFormListByChannelList(ids); + if (platformList.isEmpty()) { + return; + } + + for (Platform platform : platformList) { + removeChannels(platform.getId(), ids); + } + } + + @Override + @Transactional + public void removeChannel(int channelId) { + List platformList = platformChannelMapper.queryPlatFormListByChannelId(channelId); + if (platformList.isEmpty()) { + return; + } + for (Platform platform : platformList) { + ArrayList ids = new ArrayList<>(); + ids.add(channelId); + removeChannels(platform.getId(), ids); + } + } + + @Override + public List queryByPlatform(Platform platform) { + if (platform == null) { + return null; + } + List commonGBChannelList = commonGBChannelMapper.queryWithPlatform(platform.getId()); + if (commonGBChannelList.isEmpty()) { + return new ArrayList<>(); + } + List channelList = new ArrayList<>(); + // 是否包含平台信息 + if (platform.getCatalogWithPlatform() > 0) { + CommonGBChannel channel = CommonGBChannel.build(platform); + channelList.add(channel); + } + // 关联的行政区划信息 + if (platform.getCatalogWithRegion() > 0) { + // 查询关联平台的行政区划信息 + List regionChannelList = regionMapper.queryByPlatform(platform.getId()); + if (!regionChannelList.isEmpty()) { + channelList.addAll(regionChannelList); + } + } + if (platform.getCatalogWithGroup() > 0) { + // 关联的分组信息 + List groupChannelList = groupMapper.queryForPlatform(platform.getId()); + if (!groupChannelList.isEmpty()) { + channelList.addAll(groupChannelList); + } + } + + channelList.addAll(commonGBChannelList); + return channelList; + } + + @Override + public void pushChannel(Integer platformId) { + Platform platform = platformMapper.query(platformId); + Assert.notNull(platform, "平台不存在"); + List channelList = queryByPlatform(platform); + if (channelList.isEmpty()){ + return; + } + SubscribeInfo subscribeInfo = SubscribeInfo.buildSimulated(platform.getServerGBId(), platform.getServerIp()); + + try { + sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(CatalogEvent.ADD, platform, channelList, subscribeInfo, null); + } catch (InvalidArgumentException | ParseException | NoSuchFieldException | + SipException | IllegalAccessException e) { + log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); + } + } + + @Override + public void updateCustomChannel(PlatformChannel channel) { + platformChannelMapper.updateCustomChannel(channel); + CommonGBChannel commonGBChannel = platformChannelMapper.queryShareChannel(channel.getPlatformId(), channel.getGbId()); + // 发送消息 + try { + // 发送catalog + eventPublisher.catalogEventPublish(channel.getPlatformId(), commonGBChannel, CatalogEvent.UPDATE); + } catch (Exception e) { + log.warn("[自定义通道信息] 发送失败, 平台ID: {}, 通道: {}({})", channel.getPlatformId(), + channel.getGbName(), channel.getGbDeviceDbId(), e); + } + } + + @Override + @Transactional + public void checkGroupRemove(List channelList, List groupList) { + + List channelIds = new ArrayList<>(); + channelList.stream().forEach(commonGBChannel -> { + channelIds.add(commonGBChannel.getGbId()); + }); + // 获取关联这些通道的平台 + List platformList = platformChannelMapper.queryPlatFormListByChannelList(channelIds); + if (platformList.isEmpty()) { + return; + } + for (Platform platform : platformList) { + Set groupSet; + if (groupList == null || groupList.isEmpty()) { + groupSet = platformChannelMapper.queryShareGroup(platform.getId()); + }else { + groupSet = new HashSet<>(groupList); + } + // 清理空的分组并发送消息 + Set deleteGroup = deleteEmptyGroup(groupSet, platform.getId()); + + List channelListForEvent = new ArrayList<>(); + if (!deleteGroup.isEmpty()) { + for (Group group : deleteGroup) { + channelListForEvent.add(0, CommonGBChannel.build(group)); + } + } + // 发送消息 + try { + // 发送catalog + eventPublisher.catalogEventPublish(platform.getId(), channelListForEvent, CatalogEvent.DEL); + } catch (Exception e) { + log.warn("[移除关联通道] 发送失败,数量:{}", channelList.size(), e); + } + } + } + + @Override + @Transactional + public void checkGroupAdd(List channelList) { + List channelIds = new ArrayList<>(); + channelList.stream().forEach(commonGBChannel -> { + channelIds.add(commonGBChannel.getGbId()); + }); + List platformList = platformChannelMapper.queryPlatFormListByChannelList(channelIds); + if (platformList.isEmpty()) { + return; + } + for (Platform platform : platformList) { + + Set addGroup = getGroupNotShareByChannelList(channelList, platform.getId()); + + List channelListForEvent = new ArrayList<>(); + if (!addGroup.isEmpty()) { + for (Group group : addGroup) { + channelListForEvent.add(0, CommonGBChannel.build(group)); + } + platformChannelMapper.addPlatformGroup(addGroup, platform.getId()); + // 发送消息 + try { + // 发送catalog + eventPublisher.catalogEventPublish(platform.getId(), channelListForEvent, CatalogEvent.ADD); + } catch (Exception e) { + log.warn("[移除关联通道] 发送失败,数量:{}", channelList.size(), e); + } + } + } + } + + @Override + public List queryPlatFormListByChannelDeviceId(Integer channelId, List platforms) { + return platformChannelMapper.queryPlatFormListForGBWithGBId(channelId, platforms); + } + + @Override + public CommonGBChannel queryChannelByPlatformIdAndChannelId(Integer platformId, Integer channelId) { + return platformChannelMapper.queryShareChannel(platformId, channelId); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java similarity index 51% rename from src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java index 6554817b3..101712bb9 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java @@ -1,41 +1,43 @@ -package com.genersoft.iot.vmp.service.impl; +package com.genersoft.iot.vmp.gb28181.service.impl; 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; +import com.genersoft.iot.vmp.common.*; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.dao.PlatformChannelMapper; +import com.genersoft.iot.vmp.gb28181.dao.PlatformMapper; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; +import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; +import com.genersoft.iot.vmp.gb28181.service.IPlatformService; import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.event.hook.HookData; import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent; import com.genersoft.iot.vmp.media.event.mediaServer.MediaSendRtpStoppedEvent; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.service.IInviteStreamService; -import com.genersoft.iot.vmp.service.IPlatformService; -import com.genersoft.iot.vmp.service.IPlayService; +import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.service.bean.*; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.dao.GbStreamMapper; -import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper; import com.genersoft.iot.vmp.utils.DateUtil; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import gov.nist.javax.sip.message.SIPResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +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.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; import javax.sdp.*; import javax.sip.InvalidArgumentException; @@ -49,6 +51,7 @@ import java.util.Vector; /** * @author lin */ +@Slf4j @Service @DS("master") public class PlatformServiceImpl implements IPlatformService { @@ -58,10 +61,8 @@ public class PlatformServiceImpl implements IPlatformService { private final static String REGISTER_FAIL_AGAIN_KEY_PREFIX = "platform_register_fail_again_"; private final static String KEEPALIVE_KEY_PREFIX = "platform_keepalive_"; - private final static Logger logger = LoggerFactory.getLogger(PlatformServiceImpl.class); - @Autowired - private ParentPlatformMapper platformMapper; + private PlatformMapper platformMapper; @Autowired private IRedisCatchStorage redisCatchStorage; @@ -81,21 +82,23 @@ public class PlatformServiceImpl implements IPlatformService { @Autowired private SubscribeHolder subscribeHolder; - @Autowired - private GbStreamMapper gbStreamMapper; - @Autowired private UserSetting userSetting; @Autowired - private VideoStreamSessionManager streamSession; - - @Autowired - private IPlayService playService; + private SipInviteSessionManager sessionManager; @Autowired private IInviteStreamService inviteStreamService; + @Autowired + private PlatformChannelMapper platformChannelMapper; + + @Autowired + private IGbChannelService channelService; + + @Autowired + private ISendRtpServerService sendRtpServerService; /** * 流离开的处理 @@ -103,21 +106,20 @@ public class PlatformServiceImpl implements IPlatformService { @Async("taskExecutor") @EventListener public void onApplicationEvent(MediaDepartureEvent event) { - List sendRtpItems = redisCatchStorage.querySendRTPServerByStream(event.getStream()); + List sendRtpItems = sendRtpServerService.queryByStream(event.getStream()); if (!sendRtpItems.isEmpty()) { - for (SendRtpItem sendRtpItem : sendRtpItems) { - if (sendRtpItem != null && sendRtpItem.getApp().equals(event.getApp())) { - String platformId = sendRtpItem.getPlatformId(); - ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId); - + for (SendRtpInfo sendRtpItem : sendRtpItems) { + if (sendRtpItem != null && sendRtpItem.getApp().equals(event.getApp()) && sendRtpItem.isSendToPlatform()) { + String platformId = sendRtpItem.getTargetId(); + Platform platform = platformMapper.getParentPlatByServerGBId(platformId); + CommonGBChannel channel = channelService.getOne(sendRtpItem.getChannelId()); try { - if (platform != null) { - commanderForPlatform.streamByeCmd(platform, sendRtpItem); - redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(), - sendRtpItem.getCallId(), sendRtpItem.getStream()); + if (platform != null && channel != null) { + commanderForPlatform.streamByeCmd(platform, sendRtpItem, channel); + sendRtpServerService.delete(sendRtpItem); } } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 发送BYE: {}", e.getMessage()); + log.error("[命令发送失败] 发送BYE: {}", e.getMessage()); } } } @@ -131,228 +133,238 @@ public class PlatformServiceImpl implements IPlatformService { @Async("taskExecutor") @EventListener public void onApplicationEvent(MediaSendRtpStoppedEvent event) { - List sendRtpItems = redisCatchStorage.querySendRTPServerByStream(event.getStream()); + List sendRtpItems = sendRtpServerService.queryByStream(event.getStream()); if (sendRtpItems != null && !sendRtpItems.isEmpty()) { - for (SendRtpItem sendRtpItem : sendRtpItems) { - ParentPlatform parentPlatform = platformMapper.getParentPlatByServerGBId(sendRtpItem.getPlatformId()); - ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc()); - try { - commanderForPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId()); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + for (SendRtpInfo sendRtpItem : sendRtpItems) { + if (sendRtpItem != null && sendRtpItem.getApp().equals(event.getApp()) && sendRtpItem.isSendToPlatform()) { + Platform platform = platformMapper.getParentPlatByServerGBId(sendRtpItem.getTargetId()); + CommonGBChannel channel = channelService.getOne(sendRtpItem.getChannelId()); + ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc()); + try { + commanderForPlatform.streamByeCmd(platform, sendRtpItem, channel); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + sendRtpServerService.delete(sendRtpItem); } - redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(), - sendRtpItem.getCallId(), sendRtpItem.getStream()); } } } - @Override - public ParentPlatform queryPlatformByServerGBId(String platformGbId) { + public Platform queryPlatformByServerGBId(String platformGbId) { return platformMapper.getParentPlatByServerGBId(platformGbId); } @Override - public PageInfo queryParentPlatformList(int page, int count) { + public PageInfo queryPlatformList(int page, int count, String query) { PageHelper.startPage(page, count); - List all = platformMapper.getParentPlatformList(); + List all = platformMapper.queryList(query); return new PageInfo<>(all); } @Override - public boolean add(ParentPlatform parentPlatform) { - - if (parentPlatform.getCatalogGroup() == 0) { + public boolean add(Platform platform) { + log.info("[国标级联]添加平台 {}", platform.getDeviceGBId()); + if (platform.getCatalogGroup() == 0) { // 每次发送目录的数量默认为1 - parentPlatform.setCatalogGroup(1); + platform.setCatalogGroup(1); } - if (parentPlatform.getAdministrativeDivision() == null) { - // 行政区划默认去编号的前6位 - parentPlatform.setAdministrativeDivision(parentPlatform.getServerGBId().substring(0,6)); - } - parentPlatform.setCatalogId(parentPlatform.getDeviceGBId()); - int result = platformMapper.addParentPlatform(parentPlatform); + int result = platformMapper.add(platform); // 添加缓存 - ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch(); - parentPlatformCatch.setParentPlatform(parentPlatform); - parentPlatformCatch.setId(parentPlatform.getServerGBId()); - parentPlatformCatch.setParentPlatform(parentPlatform); - redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); - if (parentPlatform.isEnable()) { + PlatformCatch platformCatch = new PlatformCatch(); + platformCatch.setPlatform(platform); + platformCatch.setId(platform.getServerGBId()); + redisCatchStorage.updatePlatformCatchInfo(platformCatch); + if (platform.isEnable()) { // 保存时启用就发送注册 // 注册成功时由程序直接调用了online方法 try { - commanderForPlatform.register(parentPlatform, eventResult -> { - logger.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", parentPlatform.getServerGBId()); + commanderForPlatform.register(platform, eventResult -> { + log.info("[国标级联] {}({}),添加向上级注册失败,请确定上级平台可用时重新保存", platform.getName(), platform.getServerGBId()); }, null); } catch (InvalidArgumentException | ParseException | SipException e) { - logger.error("[命令发送失败] 国标级联: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联: {}", e.getMessage()); } } return result > 0; } @Override - public boolean update(ParentPlatform parentPlatform) { - logger.info("[国标级联]更新平台 {}", parentPlatform.getDeviceGBId()); - parentPlatform.setCharacterSet(parentPlatform.getCharacterSet().toUpperCase()); - ParentPlatform parentPlatformOld = platformMapper.getParentPlatById(parentPlatform.getId()); - ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatformOld.getServerGBId()); - parentPlatform.setUpdateTime(DateUtil.getNow()); + public boolean update(Platform platform) { + Assert.isTrue(platform.getId() > 0, "ID必须存在"); + log.info("[国标级联] 更新平台 {}({})", platform.getName(), platform.getDeviceGBId()); + platform.setCharacterSet(platform.getCharacterSet().toUpperCase()); + Platform platformInDb = platformMapper.query(platform.getId()); + PlatformCatch platformCatchOld = redisCatchStorage.queryPlatformCatchInfo(platformInDb.getServerGBId()); + platform.setUpdateTime(DateUtil.getNow()); // 停止心跳定时 - final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatformOld.getServerGBId(); + final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + platformInDb.getServerGBId(); dynamicTask.stop(keepaliveTaskKey); // 停止注册定时 - final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatformOld.getServerGBId(); + final String registerTaskKey = REGISTER_KEY_PREFIX + platformInDb.getServerGBId(); dynamicTask.stop(registerTaskKey); // 注销旧的 try { - if (parentPlatformOld.isStatus() && parentPlatformCatchOld != null) { - logger.info("保存平台{}时发现旧平台在线,发送注销命令", parentPlatformOld.getServerGBId()); - commanderForPlatform.unregister(parentPlatformOld, parentPlatformCatchOld.getSipTransactionInfo(), null, eventResult -> { - logger.info("[国标级联] 注销成功, 平台:{}", parentPlatformOld.getServerGBId()); + if (platformInDb.isStatus() && platformCatchOld != null) { + log.info("保存平台{}时发现旧平台在线,发送注销命令", platformInDb.getServerGBId()); + commanderForPlatform.unregister(platformInDb, platformCatchOld.getSipTransactionInfo(), null, eventResult -> { + log.info("[国标级联] 注销成功, 平台:{}", platformInDb.getServerGBId()); }); } } catch (InvalidArgumentException | ParseException | SipException e) { - logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); } // 更新数据库 - if (parentPlatform.getCatalogGroup() == 0) { - parentPlatform.setCatalogGroup(1); - } - if (parentPlatform.getAdministrativeDivision() == null) { - parentPlatform.setAdministrativeDivision(parentPlatform.getAdministrativeDivision()); + if (platform.getCatalogGroup() == 0) { + platform.setCatalogGroup(1); } - platformMapper.updateParentPlatform(parentPlatform); + platformMapper.update(platform); // 更新redis - redisCatchStorage.delPlatformCatchInfo(parentPlatformOld.getServerGBId()); - ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch(); - parentPlatformCatch.setParentPlatform(parentPlatform); - parentPlatformCatch.setId(parentPlatform.getServerGBId()); - redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + redisCatchStorage.delPlatformCatchInfo(platformInDb.getServerGBId()); + PlatformCatch platformCatch = new PlatformCatch(); + platformCatch.setPlatform(platform); + platformCatch.setId(platform.getServerGBId()); + redisCatchStorage.updatePlatformCatchInfo(platformCatch); // 注册 - if (parentPlatform.isEnable()) { + if (platform.isEnable()) { // 保存时启用就发送注册 // 注册成功时由程序直接调用了online方法 try { - logger.info("[国标级联] 平台注册 {}", parentPlatform.getDeviceGBId()); - commanderForPlatform.register(parentPlatform, eventResult -> { - logger.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", parentPlatform.getServerGBId()); + log.info("[国标级联] 平台注册 {}", platform.getDeviceGBId()); + commanderForPlatform.register(platform, eventResult -> { + log.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", platform.getServerGBId()); }, null); } catch (InvalidArgumentException | ParseException | SipException e) { - logger.error("[命令发送失败] 国标级联: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联: {}", e.getMessage()); } } - return false; } + private void unregister(Platform platform) { + // 停止心跳定时 + final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + platform.getServerGBId(); + dynamicTask.stop(keepaliveTaskKey); + // 停止注册定时 + final String registerTaskKey = REGISTER_KEY_PREFIX + platform.getServerGBId(); + dynamicTask.stop(registerTaskKey); + + PlatformCatch platformCatchOld = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId()); + // 注销旧的 + try { + if (platform.isStatus()) { + commanderForPlatform.unregister(platform, platformCatchOld.getSipTransactionInfo(), null, eventResult -> { + log.info("[国标级联] 注销命令发送成功,平台:{}", platform.getServerGBId()); + }); + } + } catch (InvalidArgumentException | ParseException | SipException e) { + log.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); + } + } + + private void register(Platform platform) { + + } + @Override - public void online(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo) { - logger.info("[国标级联]:{}, 平台上线", parentPlatform.getServerGBId()); - final String registerFailAgainTaskKey = REGISTER_FAIL_AGAIN_KEY_PREFIX + parentPlatform.getServerGBId(); + public void online(Platform platform, SipTransactionInfo sipTransactionInfo) { + log.info("[国标级联]:{}, 平台上线", platform.getServerGBId()); + final String registerFailAgainTaskKey = REGISTER_FAIL_AGAIN_KEY_PREFIX + platform.getServerGBId(); dynamicTask.stop(registerFailAgainTaskKey); - platformMapper.updateParentPlatformStatus(parentPlatform.getServerGBId(), true); - ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); - if (parentPlatformCatch == null) { - parentPlatformCatch = new ParentPlatformCatch(); - parentPlatformCatch.setParentPlatform(parentPlatform); - parentPlatformCatch.setId(parentPlatform.getServerGBId()); - parentPlatform.setStatus(true); - parentPlatformCatch.setParentPlatform(parentPlatform); + platformMapper.updateStatus(platform.getServerGBId(), true); + PlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId()); + if (platformCatch == null) { + platformCatch = new PlatformCatch(); + platformCatch.setPlatform(platform); + platformCatch.setId(platform.getServerGBId()); + platform.setStatus(true); + platformCatch.setPlatform(platform); } - parentPlatformCatch.getParentPlatform().setStatus(true); - parentPlatformCatch.setSipTransactionInfo(sipTransactionInfo); - redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + platformCatch.getPlatform().setStatus(true); + platformCatch.setSipTransactionInfo(sipTransactionInfo); + redisCatchStorage.updatePlatformCatchInfo(platformCatch); - final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId(); + final String registerTaskKey = REGISTER_KEY_PREFIX + platform.getServerGBId(); if (!dynamicTask.isAlive(registerTaskKey)) { - logger.info("[国标级联]:{}, 添加定时注册任务", parentPlatform.getServerGBId()); + log.info("[国标级联]:{}, 添加定时注册任务", platform.getServerGBId()); // 添加注册任务 dynamicTask.startCron(registerTaskKey, // 注册失败(注册成功时由程序直接调用了online方法) - ()-> registerTask(parentPlatform, sipTransactionInfo), - parentPlatform.getExpires() * 1000); + ()-> registerTask(platform, sipTransactionInfo), + platform.getExpires() * 1000); } - final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId(); + final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + platform.getServerGBId(); if (!dynamicTask.contains(keepaliveTaskKey)) { - logger.info("[国标级联]:{}, 添加定时心跳任务", parentPlatform.getServerGBId()); + log.info("[国标级联]:{}, 添加定时心跳任务", platform.getServerGBId()); // 添加心跳任务 dynamicTask.startCron(keepaliveTaskKey, ()-> { try { - commanderForPlatform.keepalive(parentPlatform, eventResult -> { + commanderForPlatform.keepalive(platform, eventResult -> { // 心跳失败 if (eventResult.type != SipSubscribe.EventResultType.timeout) { - logger.warn("[国标级联]发送心跳收到错误,code: {}, msg: {}", eventResult.statusCode, eventResult.msg); + log.warn("[国标级联]发送心跳收到错误,code: {}, msg: {}", eventResult.statusCode, eventResult.msg); } // 心跳失败 - ParentPlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); + PlatformCatch platformCatchForNow = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId()); // 此时是第三次心跳超时, 平台离线 - if (platformCatch.getKeepAliveReply() == 2) { + if (platformCatchForNow.getKeepAliveReply() == 2) { // 设置平台离线,并重新注册 - logger.info("[国标级联] 三次心跳失败, 平台{}({})离线", parentPlatform.getName(), parentPlatform.getServerGBId()); - offline(parentPlatform, false); + log.info("[国标级联] 三次心跳失败, 平台{}({})离线", platform.getName(), platform.getServerGBId()); + offline(platform, false); }else { - platformCatch.setKeepAliveReply(platformCatch.getKeepAliveReply() + 1); - redisCatchStorage.updatePlatformCatchInfo(platformCatch); + platformCatchForNow.setKeepAliveReply(platformCatchForNow.getKeepAliveReply() + 1); + redisCatchStorage.updatePlatformCatchInfo(platformCatchForNow); } }, eventResult -> { // 心跳成功 // 清空之前的心跳超时计数 - ParentPlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); - if (platformCatch != null && platformCatch.getKeepAliveReply() > 0) { - platformCatch.setKeepAliveReply(0); - redisCatchStorage.updatePlatformCatchInfo(platformCatch); + PlatformCatch platformCatchForNow = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId()); + if (platformCatchForNow != null && platformCatchForNow.getKeepAliveReply() > 0) { + platformCatchForNow.setKeepAliveReply(0); + redisCatchStorage.updatePlatformCatchInfo(platformCatchForNow); } - logger.info("[发送心跳] 国标级联 发送心跳, code: {}, msg: {}", eventResult.statusCode, eventResult.msg); + log.info("[发送心跳] 国标级联 发送心跳, code: {}, msg: {}", eventResult.statusCode, eventResult.msg); }); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 发送心跳: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 发送心跳: {}", e.getMessage()); } }, - (parentPlatform.getKeepTimeout())*1000); + (platform.getKeepTimeout())*1000); } - if (parentPlatform.isAutoPushChannel()) { - if (subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId()) == null) { - logger.info("[国标级联]:{}, 添加自动通道推送模拟订阅信息", parentPlatform.getServerGBId()); - addSimulatedSubscribeInfo(parentPlatform); + if (platform.getAutoPushChannel() != null && platform.getAutoPushChannel()) { + if (subscribeHolder.getCatalogSubscribe(platform.getServerGBId()) == null) { + log.info("[国标级联]:{}, 添加自动通道推送模拟订阅信息", platform.getServerGBId()); + addSimulatedSubscribeInfo(platform); + } }else { - SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId()); + SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(platform.getServerGBId()); if (catalogSubscribe != null && catalogSubscribe.getExpires() == -1) { - subscribeHolder.removeCatalogSubscribe(parentPlatform.getServerGBId()); + subscribeHolder.removeCatalogSubscribe(platform.getServerGBId()); } } } @Override - public void addSimulatedSubscribeInfo(ParentPlatform parentPlatform) { + public void addSimulatedSubscribeInfo(Platform platform) { // 自动添加一条模拟的订阅信息 - SubscribeInfo subscribeInfo = new SubscribeInfo(); - subscribeInfo.setId(parentPlatform.getServerGBId()); - subscribeInfo.setExpires(-1); - subscribeInfo.setEventType("Catalog"); - int random = (int) Math.floor(Math.random() * 10000); - subscribeInfo.setEventId(random + ""); - subscribeInfo.setSimulatedCallId(UUID.randomUUID().toString().replace("-", "") + "@" + parentPlatform.getServerIP()); - subscribeInfo.setSimulatedFromTag(UUID.randomUUID().toString().replace("-", "")); - subscribeInfo.setSimulatedToTag(UUID.randomUUID().toString().replace("-", "")); - subscribeHolder.putCatalogSubscribe(parentPlatform.getServerGBId(), subscribeInfo); + subscribeHolder.putCatalogSubscribe(platform.getServerGBId(), + SubscribeInfo.buildSimulated(platform.getServerGBId(), platform.getServerIp())); } - private void registerTask(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo){ + private void registerTask(Platform platform, SipTransactionInfo sipTransactionInfo){ try { // 不在同一个会话中续订则每次全新注册 if (!userSetting.isRegisterKeepIntDialog()) { @@ -360,79 +372,82 @@ public class PlatformServiceImpl implements IPlatformService { } if (sipTransactionInfo == null) { - logger.info("[国标级联] 平台:{}注册即将到期,开始重新注册", parentPlatform.getServerGBId()); + log.info("[国标级联] 平台:{}注册即将到期,开始重新注册", platform.getServerGBId()); }else { - logger.info("[国标级联] 平台:{}注册即将到期,开始续订", parentPlatform.getServerGBId()); + log.info("[国标级联] 平台:{}注册即将到期,开始续订", platform.getServerGBId()); } - commanderForPlatform.register(parentPlatform, sipTransactionInfo, eventResult -> { - logger.info("[国标级联] 平台:{}注册失败,{}:{}", parentPlatform.getServerGBId(), + commanderForPlatform.register(platform, sipTransactionInfo, eventResult -> { + log.info("[国标级联] 平台:{}注册失败,{}:{}", platform.getServerGBId(), eventResult.statusCode, eventResult.msg); - offline(parentPlatform, false); + if (platform.isStatus()) { + offline(platform, false); + } }, null); } catch (Exception e) { - logger.error("[命令发送失败] 国标级联定时注册: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联定时注册: {}", e.getMessage()); } } @Override - public void offline(ParentPlatform parentPlatform, boolean stopRegister) { - logger.info("[平台离线]:{}", parentPlatform.getServerGBId()); - ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); - parentPlatformCatch.setKeepAliveReply(0); - parentPlatformCatch.setRegisterAliveReply(0); - ParentPlatform parentPlatformInCatch = parentPlatformCatch.getParentPlatform(); - parentPlatformInCatch.setStatus(false); - parentPlatformCatch.setParentPlatform(parentPlatformInCatch); - redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); - platformMapper.updateParentPlatformStatus(parentPlatform.getServerGBId(), false); + public void offline(Platform platform, boolean stopRegister) { + log.info("[平台离线]:{}({})", platform.getName(), platform.getServerGBId()); + PlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId()); + platformCatch.setKeepAliveReply(0); + platformCatch.setRegisterAliveReply(0); + Platform catchPlatform = platformCatch.getPlatform(); + catchPlatform.setStatus(false); + platformCatch.setPlatform(catchPlatform); + redisCatchStorage.updatePlatformCatchInfo(platformCatch); + platformMapper.updateStatus(platform.getServerGBId(), false); // 停止所有推流 - logger.info("[平台离线] {}, 停止所有推流", parentPlatform.getServerGBId()); - stopAllPush(parentPlatform.getServerGBId()); + log.info("[平台离线] {}({}), 停止所有推流", platform.getName(), platform.getServerGBId()); + stopAllPush(platform.getServerGBId()); // 清除注册定时 - logger.info("[平台离线] {}, 停止定时注册任务", parentPlatform.getServerGBId()); - final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId(); + log.info("[平台离线] {}({}), 停止定时注册任务", platform.getName(), platform.getServerGBId()); + final String registerTaskKey = REGISTER_KEY_PREFIX + platform.getServerGBId(); if (dynamicTask.contains(registerTaskKey)) { dynamicTask.stop(registerTaskKey); } // 清除心跳定时 - logger.info("[平台离线] {}, 停止定时发送心跳任务", parentPlatform.getServerGBId()); - final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId(); + log.info("[平台离线] {}({}), 停止定时发送心跳任务", platform.getName(), platform.getServerGBId()); + final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + platform.getServerGBId(); if (dynamicTask.contains(keepaliveTaskKey)) { // 清除心跳任务 dynamicTask.stop(keepaliveTaskKey); } // 停止订阅回复 - SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId()); + SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(platform.getServerGBId()); if (catalogSubscribe != null) { if (catalogSubscribe.getExpires() > 0) { - logger.info("[平台离线] {}, 停止目录订阅回复", parentPlatform.getServerGBId()); - subscribeHolder.removeCatalogSubscribe(parentPlatform.getServerGBId()); + log.info("[平台离线] {}({}), 停止目录订阅回复", platform.getName(), platform.getServerGBId()); + subscribeHolder.removeCatalogSubscribe(platform.getServerGBId()); } } - logger.info("[平台离线] {}, 停止移动位置订阅回复", parentPlatform.getServerGBId()); - subscribeHolder.removeMobilePositionSubscribe(parentPlatform.getServerGBId()); + + log.info("[平台离线] {}({}), 停止移动位置订阅回复", platform.getName(), platform.getServerGBId()); + subscribeHolder.removeMobilePositionSubscribe(platform.getServerGBId()); // 发起定时自动重新注册 if (!stopRegister) { // 设置为60秒自动尝试重新注册 - final String registerFailAgainTaskKey = REGISTER_FAIL_AGAIN_KEY_PREFIX + parentPlatform.getServerGBId(); - ParentPlatform platform = platformMapper.getParentPlatById(parentPlatform.getId()); - if (platform.isEnable()) { + final String registerFailAgainTaskKey = REGISTER_FAIL_AGAIN_KEY_PREFIX + platform.getServerGBId(); + Platform platformInDb = platformMapper.query(platform.getId()); + if (platformInDb.isEnable()) { dynamicTask.startCron(registerFailAgainTaskKey, - ()-> registerTask(platform, null), + ()-> registerTask(platformInDb, null), userSetting.getRegisterAgainAfterTime() * 1000); } } } private void stopAllPush(String platformId) { - List sendRtpItems = redisCatchStorage.querySendRTPServer(platformId); + List sendRtpItems = sendRtpServerService.queryForPlatform(platformId); if (sendRtpItems != null && sendRtpItems.size() > 0) { - for (SendRtpItem sendRtpItem : sendRtpItems) { + for (SendRtpInfo sendRtpItem : sendRtpItems) { ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc()); - redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(), null, null); + sendRtpServerService.delete(sendRtpItem); MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); mediaServerService.stopSendRtp(mediaInfo, sendRtpItem.getApp(), sendRtpItem.getStream(), null); } @@ -440,39 +455,37 @@ public class PlatformServiceImpl implements IPlatformService { } @Override - public void login(ParentPlatform parentPlatform) { - final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId(); + public void login(Platform platform) { + final String registerTaskKey = REGISTER_KEY_PREFIX + platform.getServerGBId(); try { - commanderForPlatform.register(parentPlatform, eventResult1 -> { - logger.info("[国标级联] {},开始定时发起注册,间隔为1分钟", parentPlatform.getServerGBId()); + commanderForPlatform.register(platform, eventResult1 -> { + log.info("[国标级联] {},开始定时发起注册,间隔为1分钟", platform.getServerGBId()); // 添加注册任务 dynamicTask.startCron(registerTaskKey, // 注册失败(注册成功时由程序直接调用了online方法) - ()->logger.info("[国标级联] {},平台离线后持续发起注册,失败", parentPlatform.getServerGBId()), + ()-> log.info("[国标级联] {}({}),平台离线后持续发起注册,失败", platform.getName(), platform.getServerGBId()), 60*1000); }, null); } catch (InvalidArgumentException | ParseException | SipException e) { - logger.error("[命令发送失败] 国标级联注册: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联注册: {}", e.getMessage()); } } @Override public void sendNotifyMobilePosition(String platformId) { - ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId); + Platform platform = platformMapper.getParentPlatByServerGBId(platformId); if (platform == null) { return; } SubscribeInfo subscribe = subscribeHolder.getMobilePositionSubscribe(platform.getServerGBId()); if (subscribe != null) { - // TODO 暂时只处理视频流的回复,后续增加对国标设备的支持 - List gbStreams = gbStreamMapper.queryGbStreamListInPlatform(platform.getServerGBId(), userSetting.isUsePushingAsStatus()); - if (gbStreams.size() == 0) { + List channelList = platformChannelMapper.queryShare(platform.getId(), null); + if (channelList.isEmpty()) { return; } - for (DeviceChannel deviceChannel : gbStreams) { - String gbId = deviceChannel.getChannelId(); - GPSMsgInfo gpsMsgInfo = redisCatchStorage.getGpsMsgInfo(gbId); + for (CommonGBChannel channel : channelList) { + GPSMsgInfo gpsMsgInfo = redisCatchStorage.getGpsMsgInfo(channel.getGbDeviceId()); // 无最新位置不发送 if (gpsMsgInfo != null) { // 经纬度都为0不发送 @@ -481,10 +494,10 @@ public class PlatformServiceImpl implements IPlatformService { } // 发送GPS消息 try { - commanderForPlatform.sendNotifyMobilePosition(platform, gpsMsgInfo, subscribe); + commanderForPlatform.sendNotifyMobilePosition(platform, gpsMsgInfo, channel, subscribe); } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | IllegalAccessException e) { - logger.error("[命令发送失败] 国标级联 移动位置通知: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 移动位置通知: {}", e.getMessage()); } } } @@ -492,18 +505,18 @@ public class PlatformServiceImpl implements IPlatformService { } @Override - public void broadcastInvite(ParentPlatform platform, String channelId, MediaServer mediaServerItem, HookSubscribe.Event hookEvent, + public void broadcastInvite(Platform platform, CommonGBChannel channel, MediaServer mediaServerItem, HookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, InviteTimeOutCallback timeoutCallback) throws InvalidArgumentException, ParseException, SipException { if (mediaServerItem == null) { - logger.info("[国标级联] 语音喊话未找到可用的zlm. platform: {}", platform.getServerGBId()); + log.info("[国标级联] 语音喊话未找到可用的zlm. platform: {}", platform.getServerGBId()); return; } - InviteInfo inviteInfoForOld = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, platform.getServerGBId(), channelId); + InviteInfo inviteInfoForOld = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getGbId()); if (inviteInfoForOld != null && inviteInfoForOld.getStreamInfo() != null) { // 如果zlm不存在这个流,则删除数据即可 - MediaServer mediaServerItemForStreamInfo = mediaServerService.getOne(inviteInfoForOld.getStreamInfo().getMediaServerId()); + MediaServer mediaServerItemForStreamInfo = mediaServerService.getOne(inviteInfoForOld.getStreamInfo().getMediaServer().getId()); if (mediaServerItemForStreamInfo != null) { Boolean ready = mediaServerService.isStreamReady(mediaServerItemForStreamInfo, inviteInfoForOld.getStreamInfo().getApp(), inviteInfoForOld.getStreamInfo().getStream()); if (!ready) { @@ -523,7 +536,7 @@ public class PlatformServiceImpl implements IPlatformService { String streamId = null; if (mediaServerItem.isRtpEnable()) { - streamId = String.format("%s_%s", platform.getServerGBId(), channelId); + streamId = String.format("%s_%s", platform.getServerGBId(), channel.getGbDeviceId()); } // 默认不进行SSRC校验, TODO 后续可改为配置 boolean ssrcCheck = false; @@ -537,7 +550,7 @@ public class PlatformServiceImpl implements IPlatformService { } SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, ssrcCheck, false, null, true, false, false, tcpMode); if (ssrcInfo == null || ssrcInfo.getPort() < 0) { - logger.info("[国标级联] 发起语音喊话 开启端口监听失败, platform: {}, channel: {}", platform.getServerGBId(), channelId); + log.info("[国标级联] 发起语音喊话 开启端口监听失败, platform: {}, channel: {}", platform.getServerGBId(), channel.getGbDeviceId()); SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult<>(); eventResult.statusCode = -1; eventResult.msg = "端口监听失败"; @@ -545,46 +558,46 @@ public class PlatformServiceImpl implements IPlatformService { errorEvent.response(eventResult); return; } - logger.info("[国标级联] 语音喊话,发起Invite消息 deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", - platform.getServerGBId(), channelId, ssrcInfo.getPort(), userSetting.getBroadcastForPlatform(), ssrcInfo.getSsrc(), ssrcCheck); + log.info("[国标级联] 语音喊话,发起Invite消息 deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", + platform.getServerGBId(), channel.getGbDeviceId(), ssrcInfo.getPort(), userSetting.getBroadcastForPlatform(), ssrcInfo.getSsrc(), ssrcCheck); // 初始化redis中的invite消息状态 - InviteInfo inviteInfo = InviteInfo.getInviteInfo(platform.getServerGBId(), channelId, ssrcInfo.getStream(), ssrcInfo, + InviteInfo inviteInfo = InviteInfo.getInviteInfo(platform.getServerGBId(), channel.getGbId(), ssrcInfo.getStream(), ssrcInfo, mediaServerItem.getSdpIp(), ssrcInfo.getPort(), userSetting.getBroadcastForPlatform(), InviteSessionType.BROADCAST, InviteSessionStatus.ready); inviteStreamService.updateInviteInfo(inviteInfo); String timeOutTaskKey = UUID.randomUUID().toString(); dynamicTask.startDelay(timeOutTaskKey, () -> { // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况 - InviteInfo inviteInfoForBroadcast = inviteStreamService.getInviteInfo(InviteSessionType.BROADCAST, platform.getServerGBId(), channelId, null); + InviteInfo inviteInfoForBroadcast = inviteStreamService.getInviteInfo(InviteSessionType.BROADCAST, channel.getGbId(), null); if (inviteInfoForBroadcast == null) { - logger.info("[国标级联] 发起语音喊话 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", platform.getServerGBId(), channelId, ssrcInfo.getPort(), ssrcInfo.getSsrc()); + log.info("[国标级联] 发起语音喊话 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", platform.getServerGBId(), channel.getGbDeviceId(), ssrcInfo.getPort(), ssrcInfo.getSsrc()); // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 try { - commanderForPlatform.streamByeCmd(platform, channelId, ssrcInfo.getStream(), null, null); + commanderForPlatform.streamByeCmd(platform, channel, ssrcInfo.getStream(), null, null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { - logger.error("[点播超时], 发送BYE失败 {}", e.getMessage()); + log.error("[点播超时], 发送BYE失败 {}", e.getMessage()); } finally { timeoutCallback.run(1, "收流超时"); mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); - streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getStream()); mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); } } }, userSetting.getPlayTimeout()); - commanderForPlatform.broadcastInviteCmd(platform, channelId, mediaServerItem, ssrcInfo, (hookData)->{ - logger.info("[国标级联] 发起语音喊话 收到上级推流 deviceId: {}, channelId: {}", platform.getServerGBId(), channelId); + commanderForPlatform.broadcastInviteCmd(platform, channel, mediaServerItem, ssrcInfo, (hookData)->{ + log.info("[国标级联] 发起语音喊话 收到上级推流 deviceId: {}, channelId: {}", platform.getServerGBId(), channel.getGbDeviceId()); dynamicTask.stop(timeOutTaskKey); // hook响应 - playService.onPublishHandlerForPlay(hookData.getMediaServer(), hookData.getMediaInfo(), platform.getServerGBId(), channelId); + onPublishHandlerForBroadcast(hookData.getMediaServer(), hookData.getMediaInfo(), platform, channel); // 收到流 if (hookEvent != null) { hookEvent.response(hookData); } }, event -> { - inviteOKHandler(event, ssrcInfo, tcpMode, ssrcCheck, mediaServerItem, platform, channelId, timeOutTaskKey, + inviteOKHandler(event, ssrcInfo, tcpMode, ssrcCheck, mediaServerItem, platform, channel, timeOutTaskKey, null, inviteInfo, InviteSessionType.BROADCAST); // // 收到200OK 检测ssrc是否有变化,防止上级自定义了ssrc // ResponseEvent responseEvent = (ResponseEvent) event.event; @@ -633,13 +646,24 @@ public class PlatformServiceImpl implements IPlatformService { }); } + public void onPublishHandlerForBroadcast(MediaServer mediaServerItem, MediaInfo mediaInfo, Platform platform, CommonGBChannel channel) { + StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(mediaServerItem, mediaInfo.getApp(), mediaInfo.getStream(), mediaInfo, null); + streamInfo.setChannelId(channel.getGbId()); + + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getGbId()); + if (inviteInfo != null) { + inviteInfo.setStatus(InviteSessionStatus.ok); + inviteInfo.setStreamInfo(streamInfo); + inviteStreamService.updateInviteInfo(inviteInfo); + } + } + private void inviteOKHandler(SipSubscribe.EventResult eventResult, SSRCInfo ssrcInfo, int tcpMode, boolean ssrcCheck, MediaServer mediaServerItem, - ParentPlatform platform, String channelId, String timeOutTaskKey, ErrorCallback callback, + Platform platform, CommonGBChannel channel, String timeOutTaskKey, ErrorCallback callback, InviteInfo inviteInfo, InviteSessionType inviteSessionType){ inviteInfo.setStatus(InviteSessionStatus.ok); ResponseEvent responseEvent = (ResponseEvent) eventResult.event; String contentString = new String(responseEvent.getResponse().getRawContent()); - System.out.println(contentString); String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); // 兼容回复的消息中缺少ssrc(y字段)的情况 if (ssrcInResponse == null) { @@ -650,44 +674,44 @@ public class PlatformServiceImpl implements IPlatformService { if (mediaServerItem.isRtpEnable()) { // 多端口 if (tcpMode == 2) { - tcpActiveHandler(platform, channelId, contentString, mediaServerItem, tcpMode, ssrcCheck, + tcpActiveHandler(platform, channel, contentString, mediaServerItem, tcpMode, ssrcCheck, timeOutTaskKey, ssrcInfo, callback); } }else { // 单端口 if (tcpMode == 2) { - logger.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); + log.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); } } }else { - logger.info("[Invite 200OK] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); + log.info("[Invite 200OK] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); // ssrc 不一致 if (mediaServerItem.isRtpEnable()) { // 多端口 if (ssrcCheck) { // ssrc检验 // 更新ssrc - logger.info("[Invite 200OK] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); + log.info("[Invite 200OK] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); // 释放ssrc mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse); if (!result) { try { - logger.warn("[Invite 200OK] 更新ssrc失败,停止喊话 {}/{}", platform.getServerGBId(), channelId); - commanderForPlatform.streamByeCmd(platform, channelId, ssrcInfo.getStream(), null, null); + log.warn("[Invite 200OK] 更新ssrc失败,停止喊话 {}/{}", platform.getServerGBId(), channel.getGbDeviceId()); + commanderForPlatform.streamByeCmd(platform, channel, ssrcInfo.getStream(), null, null); } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { - logger.error("[命令发送失败] 停止播放, 发送BYE: {}", e.getMessage()); + log.error("[命令发送失败] 停止播放, 发送BYE: {}", e.getMessage()); } dynamicTask.stop(timeOutTaskKey); // 释放ssrc mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getStream()); callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), "下级自定义了ssrc,重新设置收流信息失败", null); - inviteStreamService.call(inviteSessionType, platform.getServerGBId(), channelId, null, + inviteStreamService.call(inviteSessionType, channel.getGbId(), null, InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), "下级自定义了ssrc,重新设置收流信息失败", null); @@ -697,10 +721,10 @@ public class PlatformServiceImpl implements IPlatformService { inviteInfo.setStream(ssrcInfo.getStream()); if (tcpMode == 2) { if (mediaServerItem.isRtpEnable()) { - tcpActiveHandler(platform, channelId, contentString, mediaServerItem, tcpMode, ssrcCheck, + tcpActiveHandler(platform, channel, contentString, mediaServerItem, tcpMode, ssrcCheck, timeOutTaskKey, ssrcInfo, callback); }else { - logger.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); + log.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); } } inviteStreamService.updateInviteInfo(inviteInfo); @@ -711,10 +735,10 @@ public class PlatformServiceImpl implements IPlatformService { inviteInfo.setStream(ssrcInfo.getStream()); if (tcpMode == 2) { if (mediaServerItem.isRtpEnable()) { - tcpActiveHandler(platform, channelId, contentString, mediaServerItem, tcpMode, ssrcCheck, + tcpActiveHandler(platform, channel, contentString, mediaServerItem, tcpMode, ssrcCheck, timeOutTaskKey, ssrcInfo, callback); }else { - logger.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); + log.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); } } inviteStreamService.updateInviteInfo(inviteInfo); @@ -723,20 +747,26 @@ public class PlatformServiceImpl implements IPlatformService { if (ssrcInResponse != null) { // 单端口 // 重新订阅流上线 - SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(inviteInfo.getDeviceId(), - inviteInfo.getChannelId(), null, inviteInfo.getStream()); - streamSession.remove(inviteInfo.getDeviceId(), - inviteInfo.getChannelId(), inviteInfo.getStream()); + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(inviteInfo.getStream()); + sessionManager.removeByStream(inviteInfo.getStream()); inviteStreamService.updateInviteInfoForSSRC(inviteInfo, ssrcInResponse); - streamSession.put(platform.getServerGBId(), channelId, ssrcTransaction.getCallId(), - inviteInfo.getStream(), ssrcInResponse, mediaServerItem.getId(), (SIPResponse) responseEvent.getResponse(), inviteSessionType); + + ssrcTransaction.setPlatformId(platform.getServerGBId()); + ssrcTransaction.setChannelId(channel.getGbId()); + ssrcTransaction.setStream(inviteInfo.getStream()); + ssrcTransaction.setSsrc(ssrcInResponse); + ssrcTransaction.setMediaServerId(mediaServerItem.getId()); + ssrcTransaction.setSipTransactionInfo(new SipTransactionInfo((SIPResponse) responseEvent.getResponse())); + ssrcTransaction.setType(inviteSessionType); + + sessionManager.put(ssrcTransaction); } } } } - private void tcpActiveHandler(ParentPlatform platform, String channelId, String contentString, + private void tcpActiveHandler(Platform platform, CommonGBChannel channel, String contentString, MediaServer mediaServerItem, int tcpMode, boolean ssrcCheck, String timeOutTaskKey, SSRCInfo ssrcInfo, ErrorCallback callback){ if (tcpMode != 2) { @@ -763,45 +793,112 @@ public class PlatformServiceImpl implements IPlatformService { break; } } - logger.info("[TCP主动连接对方] serverGbId: {}, channelId: {}, 连接对方的地址:{}:{}, SSRC: {}, SSRC校验:{}", - platform.getServerGBId(), channelId, sdp.getConnection().getAddress(), port, ssrcInfo.getSsrc(), ssrcCheck); + log.info("[TCP主动连接对方] serverGbId: {}, channelId: {}, 连接对方的地址:{}:{}, SSRC: {}, SSRC校验:{}", + platform.getServerGBId(), channel.getGbDeviceId(), sdp.getConnection().getAddress(), port, ssrcInfo.getSsrc(), ssrcCheck); Boolean result = mediaServerService.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream()); - logger.info("[TCP主动连接对方] 结果: {}", result); + log.info("[TCP主动连接对方] 结果: {}", result); } catch (SdpException e) { - logger.error("[TCP主动连接对方] serverGbId: {}, channelId: {}, 解析200OK的SDP信息失败", platform.getServerGBId(), channelId, e); + log.error("[TCP主动连接对方] serverGbId: {}, channelId: {}, 解析200OK的SDP信息失败", platform.getServerGBId(), channel.getGbDeviceId(), e); dynamicTask.stop(timeOutTaskKey); mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); // 释放ssrc mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getStream()); callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); - inviteStreamService.call(InviteSessionType.PLAY, platform.getServerGBId(), channelId, null, + inviteStreamService.call(InviteSessionType.PLAY, channel.getGbId(), null, InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); } } @Override - public void stopBroadcast(ParentPlatform platform, DeviceChannel channel, String stream, boolean sendBye, MediaServer mediaServerItem) { + public void stopBroadcast(Platform platform, CommonGBChannel channel, String stream, boolean sendBye, MediaServer mediaServerItem) { try { if (sendBye) { - commanderForPlatform.streamByeCmd(platform, channel.getChannelId(), stream, null, null); + commanderForPlatform.streamByeCmd(platform, channel, stream, null, null); } } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { - logger.warn("[消息发送失败] 停止语音对讲, 平台:{},通道:{}", platform.getId(), channel.getChannelId() ); + log.warn("[消息发送失败] 停止语音对讲, 平台:{},通道:{}", platform.getId(), channel.getGbDeviceId() ); } finally { mediaServerService.closeRTPServer(mediaServerItem, stream); - InviteInfo inviteInfo = inviteStreamService.getInviteInfo(null, platform.getServerGBId(), channel.getChannelId(), stream); + InviteInfo inviteInfo = inviteStreamService.getInviteInfo(null, channel.getGbId(), stream); if (inviteInfo != null) { // 释放ssrc mediaServerService.releaseSsrc(mediaServerItem.getId(), inviteInfo.getSsrcInfo().getSsrc()); inviteStreamService.removeInviteInfo(inviteInfo); } - streamSession.remove(platform.getServerGBId(), channel.getChannelId(), stream); + sessionManager.removeByStream(stream); } } + + @Override + public Platform queryOne(Integer platformId) { + return platformMapper.query(platformId); + } + + @Override + public List queryEnablePlatformList() { + return platformMapper.queryEnablePlatformList(); + } + + @Override + @Transactional + public void delete(Integer platformId, CommonCallback callback) { + Platform platform = platformMapper.query(platformId); + Assert.notNull(platform, "平台不存在"); + // 发送离线消息,无论是否成功都删除缓存 + PlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId()); + if (platformCatch != null) { + String key = UUID.randomUUID().toString(); + dynamicTask.startDelay(key, ()->{ + deletePlatformInfo(platform); + if (callback != null) { + callback.run(null); + } + }, 2000); + try { + commanderForPlatform.unregister(platform, platformCatch.getSipTransactionInfo(), (event -> { + dynamicTask.stop(key); + // 移除平台相关的信息 + deletePlatformInfo(platform); + if (callback != null) { + callback.run(null); + } + }), (event -> { + dynamicTask.stop(key); + // 移除平台相关的信息 + deletePlatformInfo(platform); + if (callback != null) { + callback.run(null); + } + })); + } catch (InvalidArgumentException | ParseException | SipException e) { + log.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); + } + }else { + deletePlatformInfo(platform); + if (callback != null) { + callback.run(null); + } + } + + } + + @Transactional + public void deletePlatformInfo(Platform platform) { + // 删除关联的通道 + platformChannelMapper.removeChannelsByPlatformId(platform.getId()); + // 删除关联的分组 + platformChannelMapper.removePlatformGroupsByPlatformId(platform.getId()); + // 删除关联的行政区划 + platformChannelMapper.removePlatformRegionByPlatformId(platform.getId()); + // 删除redis缓存 + redisCatchStorage.delPlatformCatchInfo(platform.getServerGBId()); + // 删除平台信息 + platformMapper.delete(platform.getId()); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java new file mode 100755 index 000000000..04fdd0b97 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java @@ -0,0 +1,1683 @@ +package com.genersoft.iot.vmp.gb28181.service.impl; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.genersoft.iot.vmp.common.InviteInfo; +import com.genersoft.iot.vmp.common.*; +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.exception.ServiceException; +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.controller.bean.AudioBroadcastEvent; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.gb28181.service.*; +import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; +import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.media.bean.MediaInfo; +import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.media.bean.RecordInfo; +import com.genersoft.iot.vmp.media.event.hook.Hook; +import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; +import com.genersoft.iot.vmp.media.event.hook.HookType; +import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent; +import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent; +import com.genersoft.iot.vmp.media.event.media.MediaNotFoundEvent; +import com.genersoft.iot.vmp.media.service.IMediaServerService; +import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; +import com.genersoft.iot.vmp.service.IReceiveRtpServerService; +import com.genersoft.iot.vmp.service.ISendRtpServerService; +import com.genersoft.iot.vmp.service.bean.*; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.utils.CloudRecordUtils; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.StreamContent; +import gov.nist.javax.sip.message.SIPResponse; +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.stereotype.Service; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +import javax.sdp.*; +import javax.sip.InvalidArgumentException; +import javax.sip.ResponseEvent; +import javax.sip.SipException; +import javax.sip.header.CallIdHeader; +import javax.sip.message.Response; +import java.io.File; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.Vector; + +@SuppressWarnings(value = {"rawtypes", "unchecked"}) +@Slf4j +@Service +@DS("master") +public class PlayServiceImpl implements IPlayService { + + @Autowired + private ISIPCommander cmder; + + @Autowired + private AudioBroadcastManager audioBroadcastManager; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private ISIPCommanderForPlatform sipCommanderFroPlatform; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IInviteStreamService inviteStreamService; + + @Autowired + private HookSubscribe subscribe; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private SipInviteSessionManager sessionManager; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private ISIPCommanderForPlatform commanderForPlatform; + + @Autowired + private SSRCFactory ssrcFactory; + + @Autowired + private IPlatformService platformService; + + @Autowired + private IGbChannelService channelService; + + @Autowired + private ISendRtpServerService sendRtpServerService; + + @Autowired + private IReceiveRtpServerService receiveRtpServerService; + + @Autowired + private ICloudRecordService cloudRecordService; + + /** + * 流到来的处理 + */ + @Async("taskExecutor") + @org.springframework.context.event.EventListener + public void onApplicationEvent(MediaArrivalEvent event) { + if ("broadcast".equals(event.getApp()) || "talk".equals(event.getApp())) { + if (event.getStream().indexOf("_") > 0) { + String[] streamArray = event.getStream().split("_"); + if (streamArray.length == 2) { + String deviceId = streamArray[0]; + String channelId = streamArray[1]; + Device device = deviceService.getDeviceByDeviceId(deviceId); + DeviceChannel channel = deviceChannelService.getOne(deviceId, channelId); + if (device == null) { + log.info("[语音对讲/喊话] 未找到设备:{}", deviceId); + return; + } + if (channel == null) { + log.info("[语音对讲/喊话] 未找到通道:{}", channelId); + return; + } + if ("broadcast".equals(event.getApp())) { + if (audioBroadcastManager.exit(channel.getId())) { + stopAudioBroadcast(device, channel); + } + // 开启语音对讲通道 + try { + audioBroadcastCmd(device, channel, event.getMediaServer(), + event.getApp(), event.getStream(), 60, false, (msg) -> { + log.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId); + }); + } catch (InvalidArgumentException | ParseException | SipException e) { + log.error("[命令发送失败] 语音对讲: {}", e.getMessage()); + } + }else if ("talk".equals(event.getApp())) { + // 开启语音对讲通道 + talkCmd(device, channel, event.getMediaServer(), event.getStream(), (msg) -> { + log.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId); + }); + } + } + } + } + + + } + + /** + * 流离开的处理 + */ + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(MediaDepartureEvent event) { + List sendRtpInfos = sendRtpServerService.queryByStream(event.getStream()); + if (!sendRtpInfos.isEmpty()) { + for (SendRtpInfo sendRtpInfo : sendRtpInfos) { + if (sendRtpInfo != null && sendRtpInfo.isSendToPlatform() && sendRtpInfo.getApp().equals(event.getApp())) { + String platformId = sendRtpInfo.getTargetId(); + Device device = deviceService.getDeviceByDeviceId(platformId); + DeviceChannel channel = deviceChannelService.getOneById(sendRtpInfo.getChannelId()); + try { + if (device != null && channel != null) { + cmder.streamByeCmd(device, channel.getDeviceId(), event.getStream(), sendRtpInfo.getCallId()); + if (sendRtpInfo.getPlayType().equals(InviteStreamType.BROADCAST) + || sendRtpInfo.getPlayType().equals(InviteStreamType.TALK)) { + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(channel.getId()); + if (audioBroadcastCatch != null) { + // 来自上级平台的停止对讲 + log.info("[停止对讲] 来自上级,平台:{}, 通道:{}", sendRtpInfo.getTargetId(), sendRtpInfo.getChannelId()); + audioBroadcastManager.del(sendRtpInfo.getChannelId()); + } + } + } + } catch (SipException | InvalidArgumentException | ParseException | + SsrcTransactionNotFoundException e) { + log.error("[命令发送失败] 发送BYE: {}", e.getMessage()); + } + } + } + } + + if ("broadcast".equals(event.getApp()) || "talk".equals(event.getApp())) { + if (event.getStream().indexOf("_") > 0) { + String[] streamArray = event.getStream().split("_"); + if (streamArray.length == 2) { + String deviceId = streamArray[0]; + String channelId = streamArray[1]; + Device device = deviceService.getDeviceByDeviceId(deviceId); + if (device == null) { + log.info("[语音对讲/喊话] 未找到设备:{}", deviceId); + return; + } + DeviceChannel channel = deviceChannelService.getOne(deviceId, channelId); + if (channel == null) { + log.info("[语音对讲/喊话] 未找到通道:{}", channelId); + return; + } + if ("broadcast".equals(event.getApp())) { + stopAudioBroadcast(device, channel); + }else if ("talk".equals(event.getApp())) { + stopTalk(device, channel, false); + } + } + } + }else if ("rtp".equals(event.getApp())) { + // 释放ssrc + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, event.getStream()); + if (inviteInfo != null && inviteInfo.getStatus() == InviteSessionStatus.ok + && inviteInfo.getStreamInfo() != null && inviteInfo.getSsrcInfo() != null) { + // 发送bye + stop(inviteInfo); + } + + } + } + + /** + * 流未找到的处理 + */ + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(MediaNotFoundEvent event) { + if (!"rtp".equals(event.getApp())) { + return; + } + String[] s = event.getStream().split("_"); + if ((s.length != 2 && s.length != 4)) { + return; + } + String deviceId = s[0]; + String channelId = s[1]; + Device device = redisCatchStorage.getDevice(deviceId); + if (device == null || !device.isOnLine()) { + return; + } + DeviceChannel deviceChannel = deviceChannelService.getOne(deviceId, channelId); + if (deviceChannel == null) { + return; + } + if (s.length == 2) { + log.info("[ZLM HOOK] 预览流未找到, 发起自动点播:{}->{}->{}/{}", event.getMediaServer().getId(), event.getSchema(), event.getApp(), event.getStream()); + play(event.getMediaServer(), deviceId, channelId, null, null); + } else if (s.length == 4) { + // 此时为录像回放, 录像回放格式为> 设备ID_通道ID_开始时间_结束时间 + String startTimeStr = s[2]; + String endTimeStr = s[3]; + if (startTimeStr == null || endTimeStr == null || startTimeStr.length() != 14 || endTimeStr.length() != 14) { + return; + } + String startTime = DateUtil.urlToyyyy_MM_dd_HH_mm_ss(startTimeStr); + String endTime = DateUtil.urlToyyyy_MM_dd_HH_mm_ss(endTimeStr); + log.info("[ZLM HOOK] 回放流未找到, 发起自动点播:{}->{}->{}/{}-{}-{}", + event.getMediaServer().getId(), event.getSchema(), + event.getApp(), event.getStream(), + startTime, endTime + ); + + playBack(event.getMediaServer(), device, deviceChannel, startTime, endTime, null); + } + } + + + @Override + public SSRCInfo play(MediaServer mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback callback) { + if (mediaServerItem == null) { + log.warn("[点播] 未找到可用的zlm deviceId: {},channelId:{}", deviceId, channelId); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm"); + } + Device device = redisCatchStorage.getDevice(deviceId); + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE") && !mediaServerItem.isRtpEnable()) { + log.warn("[点播] 单端口收流时不支持TCP主动方式收流 deviceId: {},channelId:{}", deviceId, channelId); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "单端口收流时不支持TCP主动方式收流"); + } + DeviceChannel channel = deviceChannelService.getOneForSource(deviceId, channelId); + if (channel == null) { + log.warn("[点播] 未找到通道 deviceId: {},channelId:{}", deviceId, channelId); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到通道"); + } + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); + if (inviteInfo != null ) { + if (inviteInfo.getStreamInfo() == null) { + // 释放生成的ssrc,使用上一次申请的 + ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc); + // 点播发起了但是尚未成功, 仅注册回调等待结果即可 + inviteStreamService.once(InviteSessionType.PLAY, channel.getId(), null, callback); + log.info("[点播开始] 已经请求中,等待结果, deviceId: {}, channelId: {}", device.getDeviceId(), channelId); + return inviteInfo.getSsrcInfo(); + }else { + StreamInfo streamInfo = inviteInfo.getStreamInfo(); + String streamId = streamInfo.getStream(); + if (streamId == null) { + callback.run(InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), "点播失败, redis缓存streamId等于null", null); + inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, + InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), + "点播失败, redis缓存streamId等于null", + null); + return inviteInfo.getSsrcInfo(); + } + MediaServer mediaInfo = streamInfo.getMediaServer(); + Boolean ready = mediaServerService.isStreamReady(mediaInfo, "rtp", streamId); + if (ready != null && ready) { + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); + inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, + InviteErrorCode.SUCCESS.getCode(), + InviteErrorCode.SUCCESS.getMsg(), + streamInfo); + log.info("[点播已存在] 直接返回, deviceId: {}, channelId: {}", device.getDeviceId(), channelId); + return inviteInfo.getSsrcInfo(); + }else { + // 点播发起了但是尚未成功, 仅注册回调等待结果即可 + inviteStreamService.once(InviteSessionType.PLAY, channel.getId(), null, callback); + deviceChannelService.stopPlay(channel.getId()); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); + } + } + } + + return play(mediaServerItem, device, channel, ssrc, callback); + } + + private SSRCInfo play(MediaServer mediaServerItem, Device device, DeviceChannel channel, String ssrc, + ErrorCallback callback) { + if (mediaServerItem == null ) { + if (callback != null) { + callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), + InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), + null); + } + return null; + } + String streamId = String.format("%s_%s", device.getDeviceId(), channel.getDeviceId()); + int tcpMode = device.getStreamMode().equals("TCP-ACTIVE")? 2: (device.getStreamMode().equals("TCP-PASSIVE")? 1:0); + RTPServerParam rtpServerParam = new RTPServerParam(); + rtpServerParam.setMediaServerItem(mediaServerItem); + rtpServerParam.setStreamId(streamId); + rtpServerParam.setPresetSsrc(ssrc); + rtpServerParam.setSsrcCheck(device.isSsrcCheck()); + rtpServerParam.setPlayback(false); + rtpServerParam.setPort(0); + rtpServerParam.setTcpMode(tcpMode); + rtpServerParam.setOnlyAuto(false); + rtpServerParam.setDisableAudio(!channel.isHasAudio()); + SSRCInfo ssrcInfo = receiveRtpServerService.openRTPServer(rtpServerParam, (code, msg, result) -> { + + if (code == InviteErrorCode.SUCCESS.getCode() && result != null && result.getHookData() != null) { + // hook响应 + StreamInfo streamInfo = onPublishHandlerForPlay(result.getHookData().getMediaServer(), result.getHookData().getMediaInfo(), device, channel); + if (streamInfo == null){ + if (callback != null) { + callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + } + inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + return; + } + if (callback != null) { + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); + } + inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, + InviteErrorCode.SUCCESS.getCode(), + InviteErrorCode.SUCCESS.getMsg(), + streamInfo); + + log.info("[点播成功] deviceId: {}, channelId:{}, 码流类型:{}", device.getDeviceId(), channel.getDeviceId(), + channel.getStreamIdentification()); + snapOnPlay(result.getHookData().getMediaServer(), device.getDeviceId(), channel.getDeviceId(), streamId); + }else { + if (callback != null) { + callback.run(code, msg, null); + } + inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, code, msg, null); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(streamId); + if (ssrcTransaction != null) { + try { + cmder.streamByeCmd(device, channel.getDeviceId(), streamId, null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + log.error("[点播超时], 发送BYE失败 {}", e.getMessage()); + } finally { + sessionManager.removeByStream(streamId); + } + } + } + }); + if (ssrcInfo == null || ssrcInfo.getPort() <= 0) { + log.info("[点播端口/SSRC]获取失败,deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channel.getDeviceId(), ssrcInfo); + callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "获取端口或者ssrc失败", null); + inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), + null); + return null; + } + log.info("[点播开始] deviceId: {}, channelId: {},码流类型:{}, 收流端口: {}, 码流:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", + device.getDeviceId(), channel.getDeviceId(), channel.getStreamIdentification(), ssrcInfo.getPort(), ssrcInfo.getStream(), + device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); + + // 初始化redis中的invite消息状态 + InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, + mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, + InviteSessionStatus.ready); + inviteStreamService.updateInviteInfo(inviteInfo); + + try { + cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channel, (eventResult) -> { + // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题 + InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channel, callback, inviteInfo, InviteSessionType.PLAY); + }, (event) -> { + log.info("[点播失败]{}:{} deviceId: {}, channelId:{}",event.statusCode, event.msg, device.getDeviceId(), channel.getDeviceId()); + receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); + + sessionManager.removeByStream(ssrcInfo.getStream()); + if (callback != null) { + callback.run(event.statusCode, event.msg, null); + } + inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, + event.statusCode, event.msg, null); + + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); + }, userSetting.getPlayTimeout().longValue()); + } catch (InvalidArgumentException | SipException | ParseException e) { + log.error("[命令发送失败] 点播消息: {}", e.getMessage()); + receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); + sessionManager.removeByStream(ssrcInfo.getStream()); + if (callback != null) { + callback.run(InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); + } + inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); + + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); + } + return ssrcInfo; + } + + + private void talk(MediaServer mediaServerItem, Device device, DeviceChannel channel, String stream, + HookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, + Runnable timeoutCallback, AudioBroadcastEvent audioEvent) { + + String playSsrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId()); + + if (playSsrc == null) { + audioEvent.call("ssrc已经用尽"); + return; + } + SendRtpInfo sendRtpInfo; + try { + sendRtpInfo = sendRtpServerService.createSendRtpInfo(mediaServerItem, null, null, playSsrc, device.getDeviceId(), "talk", stream, + channel.getId(), true, false); + }catch (PlayException e) { + log.info("[语音对讲]开始 获取发流端口失败 deviceId: {}, channelId: {},", device.getDeviceId(), channel.getDeviceId()); + return; + } + + + sendRtpInfo.setOnlyAudio(true); + sendRtpInfo.setPt(8); + sendRtpInfo.setStatus(1); + sendRtpInfo.setTcpActive(false); + sendRtpInfo.setUsePs(false); + sendRtpInfo.setReceiveStream(stream + "_talk"); + + String callId = SipUtils.getNewCallId(); + log.info("[语音对讲]开始 deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channel.getDeviceId(), sendRtpInfo.getLocalPort(), device.getStreamMode(), sendRtpInfo.getSsrc(), false); + // 超时处理 + String timeOutTaskKey = UUID.randomUUID().toString(); + dynamicTask.startDelay(timeOutTaskKey, () -> { + + log.info("[语音对讲] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channel.getDeviceId(), sendRtpInfo.getPort(), sendRtpInfo.getSsrc()); + timeoutCallback.run(); + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 + try { + cmder.streamByeCmd(device, channel.getDeviceId(), stream, null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + log.error("[语音对讲]超时, 发送BYE失败 {}", e.getMessage()); + } finally { + timeoutCallback.run(); + mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpInfo.getSsrc()); + sessionManager.removeByStream(sendRtpInfo.getStream()); + } + }, userSetting.getPlayTimeout()); + + try { + mediaServerService.startSendRtpPassive(mediaServerItem, sendRtpInfo, userSetting.getPlayTimeout() * 1000); + }catch (ControllerException e) { + mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpInfo.getSsrc()); + log.info("[语音对讲]失败 deviceId: {}, channelId: {}", device.getDeviceId(), channel.getDeviceId()); + audioEvent.call("失败, " + e.getMessage()); + // 查看是否已经建立了通道,存在则发送bye + stopTalk(device, channel); + } + + + // 查看设备是否已经在推流 + try { + cmder.talkStreamCmd(mediaServerItem, sendRtpInfo, device, channel, callId, (hookData) -> { + log.info("[语音对讲] 流已生成, 开始推流: " + hookData); + dynamicTask.stop(timeOutTaskKey); + // TODO 暂不做处理 + }, (hookData) -> { + log.info("[语音对讲] 设备开始推流: " + hookData); + dynamicTask.stop(timeOutTaskKey); + + }, (event) -> { + dynamicTask.stop(timeOutTaskKey); + + if (event.event instanceof ResponseEvent) { + ResponseEvent responseEvent = (ResponseEvent) event.event; + if (responseEvent.getResponse() instanceof SIPResponse) { + SIPResponse response = (SIPResponse) responseEvent.getResponse(); + sendRtpInfo.setFromTag(response.getFromTag()); + sendRtpInfo.setToTag(response.getToTag()); + sendRtpInfo.setCallId(response.getCallIdHeader().getCallId()); + sendRtpServerService.update(sendRtpInfo); + + SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), sendRtpInfo.getChannelId(), "talk", + sendRtpInfo.getStream(), sendRtpInfo.getSsrc(), sendRtpInfo.getMediaServerId(), + response, InviteSessionType.TALK); + + sessionManager.put(ssrcTransaction); + } else { + log.error("[语音对讲]收到的消息错误,response不是SIPResponse"); + } + } else { + log.error("[语音对讲]收到的消息错误,event不是ResponseEvent"); + } + + }, (event) -> { + dynamicTask.stop(timeOutTaskKey); + mediaServerService.closeRTPServer(mediaServerItem, sendRtpInfo.getStream()); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpInfo.getSsrc()); + sessionManager.removeByStream(sendRtpInfo.getStream()); + errorEvent.response(event); + }, userSetting.getPlayTimeout().longValue()); + } catch (InvalidArgumentException | SipException | ParseException e) { + + log.error("[命令发送失败] 对讲消息: {}", e.getMessage()); + dynamicTask.stop(timeOutTaskKey); + mediaServerService.closeRTPServer(mediaServerItem, sendRtpInfo.getStream()); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpInfo.getSsrc()); + + sessionManager.removeByStream(sendRtpInfo.getStream()); + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(); + eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent; + eventResult.statusCode = -1; + eventResult.msg = "命令发送失败"; + errorEvent.response(eventResult); + } +// } + + } + + private void tcpActiveHandler(Device device, DeviceChannel channel, String contentString, + MediaServer mediaServerItem, SSRCInfo ssrcInfo, ErrorCallback callback){ + if (!device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { + return; + } + + String substring; + if (contentString.indexOf("y=") > 0) { + substring = contentString.substring(0, contentString.indexOf("y=")); + }else { + substring = contentString; + } + try { + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); + int port = -1; + Vector mediaDescriptions = sdp.getMediaDescriptions(true); + for (Object description : mediaDescriptions) { + MediaDescription mediaDescription = (MediaDescription) description; + Media media = mediaDescription.getMedia(); + + Vector mediaFormats = media.getMediaFormats(false); + if (mediaFormats.contains("96")) { + port = media.getMediaPort(); + break; + } + } + log.info("[TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channel.getDeviceId(), sdp.getConnection().getAddress(), port, device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); + Boolean result = mediaServerService.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream()); + log.info("[TCP主动连接对方] 结果: {}" , result); + if (!result) { + // 主动连接失败,结束流程, 清理数据 + receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); + sessionManager.removeByStream(ssrcInfo.getStream()); + callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); + inviteStreamService.call(InviteSessionType.BROADCAST, channel.getId(), null, + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); + } + } catch (SdpException e) { + log.error("[TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channel.getDeviceId(), e); + receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); + + sessionManager.removeByStream(ssrcInfo.getStream()); + + callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); + inviteStreamService.call(InviteSessionType.BROADCAST, channel.getId(), null, + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); + } + } + + /** + * 点播成功时调用截图. + * + * @param mediaServerItemInuse media + * @param deviceId 设备 ID + * @param channelId 通道 ID + * @param stream ssrc + */ + private void snapOnPlay(MediaServer mediaServerItemInuse, String deviceId, String channelId, String stream) { + String streamUrl; + if (mediaServerItemInuse.getRtspPort() != 0) { + streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", stream); + } else { + streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.mp4", mediaServerItemInuse.getHttpPort(), "rtp", stream); + } + String path = "snap"; + String fileName = deviceId + "_" + channelId + ".jpg"; + // 请求截图 + log.info("[请求截图]: " + fileName); + mediaServerService.getSnap(mediaServerItemInuse, streamUrl, 15, 1, path, fileName); + } + + public StreamInfo onPublishHandlerForPlay(MediaServer mediaServerItem, MediaInfo mediaInfo, Device device, DeviceChannel channel) { + StreamInfo streamInfo = null; + streamInfo = onPublishHandler(mediaServerItem, mediaInfo, device, channel); + if (streamInfo != null) { + deviceChannelService.startPlay(channel.getId(), streamInfo.getStream()); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); + if (inviteInfo != null) { + inviteInfo.setStatus(InviteSessionStatus.ok); + inviteInfo.setStreamInfo(streamInfo); + inviteStreamService.updateInviteInfo(inviteInfo); + } + } + return streamInfo; + + } + + private StreamInfo onPublishHandlerForPlayback(MediaServer mediaServerItem, MediaInfo mediaInfo, Device device, + DeviceChannel channel, String startTime, String endTime) { + StreamInfo streamInfo = onPublishHandler(mediaServerItem, mediaInfo, device, channel); + if (streamInfo != null) { + streamInfo.setStartTime(startTime); + streamInfo.setEndTime(endTime); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, mediaInfo.getStream()); + if (inviteInfo != null) { + inviteInfo.setStatus(InviteSessionStatus.ok); + inviteInfo.setStreamInfo(streamInfo); + inviteStreamService.updateInviteInfo(inviteInfo); + } + + } + return streamInfo; + } + + @Override + public MediaServer getNewMediaServerItem(Device device) { + if (device == null) { + return null; + } + MediaServer mediaServerItem; + if (ObjectUtils.isEmpty(device.getMediaServerId()) || "auto".equals(device.getMediaServerId())) { + mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(null); + } else { + mediaServerItem = mediaServerService.getOne(device.getMediaServerId()); + } + if (mediaServerItem == null) { + log.warn("点播时未找到可使用的ZLM..."); + } + return mediaServerItem; + } + + @Override + public void playBack(Device device, DeviceChannel channel, String startTime, + String endTime, ErrorCallback callback) { + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备不存在"); + } + if (channel == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "通道不存在"); + } + MediaServer newMediaServerItem = getNewMediaServerItem(device); + if (newMediaServerItem == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的节点"); + } + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE") && ! newMediaServerItem.isRtpEnable()) { + log.warn("[录像回放] 单端口收流时不支持TCP主动方式收流 deviceId: {},channelId:{}", device.getDeviceId(), channel.getDeviceId()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "单端口收流时不支持TCP主动方式收流"); + } + + playBack(newMediaServerItem, device, channel, startTime, endTime, callback); + } + + private void playBack(MediaServer mediaServerItem, + Device device, DeviceChannel channel, String startTime, + String endTime, ErrorCallback callback) { + + String startTimeStr = startTime.replace("-", "") + .replace(":", "") + .replace(" ", ""); + String endTimeTimeStr = endTime.replace("-", "") + .replace(":", "") + .replace(" ", ""); + + String stream = device.getDeviceId() + "_" + channel.getDeviceId() + "_" + startTimeStr + "_" + endTimeTimeStr; + int tcpMode = device.getStreamMode().equals("TCP-ACTIVE")? 2: (device.getStreamMode().equals("TCP-PASSIVE")? 1:0); + + RTPServerParam rtpServerParam = new RTPServerParam(); + rtpServerParam.setMediaServerItem(mediaServerItem); + rtpServerParam.setStreamId(stream); + rtpServerParam.setSsrcCheck(device.isSsrcCheck()); + rtpServerParam.setPlayback(true); + rtpServerParam.setPort(0); + rtpServerParam.setTcpMode(tcpMode); + rtpServerParam.setOnlyAuto(false); + rtpServerParam.setDisableAudio(!channel.isHasAudio()); + SSRCInfo ssrcInfo = receiveRtpServerService.openRTPServer(rtpServerParam, (code, msg, result) -> { + if (code == InviteErrorCode.SUCCESS.getCode() && result != null && result.getHookData() != null) { + // hook响应 + StreamInfo streamInfo = onPublishHandlerForPlayback(result.getHookData().getMediaServer(), result.getHookData().getMediaInfo(), device, channel, startTime, endTime); + if (streamInfo == null) { + log.warn("设备回放API调用失败!"); + callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + return; + } + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); + log.info("[录像回放] 成功 deviceId: {}, channelId: {}, 开始时间: {}, 结束时间: {}", device.getDeviceId(), channel.getGbDeviceId(), startTime, endTime); + }else { + if (callback != null) { + callback.run(code, msg, null); + } + inviteStreamService.call(InviteSessionType.PLAYBACK, channel.getId(), null, code, msg, null); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAYBACK, channel.getId()); + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(stream); + if (ssrcTransaction != null) { + try { + cmder.streamByeCmd(device, channel.getDeviceId(), stream, null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + log.error("[录像回放] 发送BYE失败 {}", e.getMessage()); + } finally { + sessionManager.removeByStream(stream); + } + } + } + }); + if (ssrcInfo == null || ssrcInfo.getPort() <= 0) { + log.info("[回放端口/SSRC]获取失败,deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channel.getDeviceId(), ssrcInfo); + if (callback != null) { + callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "获取端口或者ssrc失败", null); + } + inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), + null); + return; + } + + log.info("[录像回放] deviceId: {}, channelId: {}, 开始时间: {}, 结束时间: {}, 收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", + device.getDeviceId(), channel.getGbDeviceId(), startTime, endTime, ssrcInfo.getPort(), device.getStreamMode(), + ssrcInfo.getSsrc(), device.isSsrcCheck()); + // 初始化redis中的invite消息状态 + InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, + mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAYBACK, + InviteSessionStatus.ready); + inviteStreamService.updateInviteInfo(inviteInfo); + + try { + cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channel, startTime, endTime, + eventResult -> { + // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题 + InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channel, + callback, inviteInfo, InviteSessionType.PLAYBACK); + }, eventResult -> { + log.info("[录像回放] 失败,{} {}", eventResult.statusCode, eventResult.msg); + if (callback != null) { + callback.run(eventResult.statusCode, eventResult.msg, null); + } + + receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); + sessionManager.removeByStream(ssrcInfo.getStream()); + inviteStreamService.removeInviteInfo(inviteInfo); + }, userSetting.getPlayTimeout().longValue()); + } catch (InvalidArgumentException | SipException | ParseException e) { + log.error("[命令发送失败] 录像回放: {}", e.getMessage()); + if (callback != null) { + callback.run(InviteErrorCode.FAIL.getCode(), e.getMessage(), null); + } + receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); + sessionManager.removeByStream(ssrcInfo.getStream()); + inviteStreamService.removeInviteInfo(inviteInfo); + } + } + + + private void InviteOKHandler(SipSubscribe.EventResult eventResult, SSRCInfo ssrcInfo, MediaServer mediaServerItem, + Device device, DeviceChannel channel, ErrorCallback callback, + InviteInfo inviteInfo, InviteSessionType inviteSessionType){ + inviteInfo.setStatus(InviteSessionStatus.ok); + ResponseEvent responseEvent = (ResponseEvent) eventResult.event; + String contentString = new String(responseEvent.getResponse().getRawContent()); + String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); + // 兼容回复的消息中缺少ssrc(y字段)的情况 + if (ssrcInResponse == null) { + ssrcInResponse = ssrcInfo.getSsrc(); + } + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { + // ssrc 一致 + if (mediaServerItem.isRtpEnable()) { + // 多端口 + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { + tcpActiveHandler(device, channel, contentString, mediaServerItem, ssrcInfo, callback); + } + }else { + // 单端口 + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { + log.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); + } + + } + }else { + log.info("[Invite 200OK] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); + // ssrc 不一致 + if (mediaServerItem.isRtpEnable()) { + // 多端口 + if (device.isSsrcCheck()) { + // ssrc检验 + // 更新ssrc + log.info("[Invite 200OK] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse); + if (!result) { + try { + log.warn("[Invite 200OK] 更新ssrc失败,停止点播 {}/{}", device.getDeviceId(), channel.getDeviceId()); + cmder.streamByeCmd(device, channel.getDeviceId(), ssrcInfo.getStream(), null, null); + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { + log.error("[命令发送失败] 停止播放, 发送BYE: {}", e.getMessage()); + } + + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + sessionManager.removeByStream(ssrcInfo.getStream()); + + callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), + "下级自定义了ssrc,重新设置收流信息失败", null); + inviteStreamService.call(inviteSessionType, channel.getId(), null, + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), + "下级自定义了ssrc,重新设置收流信息失败", null); + + }else { + ssrcInfo.setSsrc(ssrcInResponse); + inviteInfo.setSsrcInfo(ssrcInfo); + inviteInfo.setStream(ssrcInfo.getStream()); + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { + if (mediaServerItem.isRtpEnable()) { + tcpActiveHandler(device, channel, contentString, mediaServerItem, ssrcInfo, callback); + }else { + log.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); + } + } + inviteStreamService.updateInviteInfo(inviteInfo); + } + } + }else { + if (ssrcInResponse != null) { + // 单端口 + // 重新订阅流上线 + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(inviteInfo.getStream()); + sessionManager.removeByStream(inviteInfo.getStream()); + inviteStreamService.updateInviteInfoForSSRC(inviteInfo, ssrcInResponse); + ssrcTransaction.setDeviceId(device.getDeviceId()); + ssrcTransaction.setChannelId(ssrcTransaction.getChannelId()); + ssrcTransaction.setCallId(ssrcTransaction.getCallId()); + ssrcTransaction.setSsrc(ssrcInResponse); + ssrcTransaction.setMediaServerId(mediaServerItem.getId()); + ssrcTransaction.setSipTransactionInfo(new SipTransactionInfo((SIPResponse) responseEvent.getResponse())); + ssrcTransaction.setType(inviteSessionType); + + sessionManager.put(ssrcTransaction); + } + } + } + } + + @Override + public void download(Device device, DeviceChannel channel, String startTime, String endTime, int downloadSpeed, ErrorCallback callback) { + + MediaServer newMediaServerItem = this.getNewMediaServerItem(device); + if (newMediaServerItem == null) { + callback.run(InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getCode(), + InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getMsg(), + null); + return; + } + + download(newMediaServerItem, device, channel, startTime, endTime, downloadSpeed, callback); + } + + + private void download(MediaServer mediaServerItem, Device device, DeviceChannel channel, String startTime, String endTime, int downloadSpeed, ErrorCallback callback) { + if (mediaServerItem == null ) { + callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), + InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), + null); + return; + } + + int tcpMode = device.getStreamMode().equals("TCP-ACTIVE")? 2: (device.getStreamMode().equals("TCP-PASSIVE")? 1:0); + // 录像下载不使用固定流地址,固定流地址会导致如果开始时间与结束时间一致时文件错误的叠加在一起 + RTPServerParam rtpServerParam = new RTPServerParam(); + rtpServerParam.setMediaServerItem(mediaServerItem); + rtpServerParam.setSsrcCheck(device.isSsrcCheck()); + rtpServerParam.setPlayback(true); + rtpServerParam.setPort(0); + rtpServerParam.setTcpMode(tcpMode); + rtpServerParam.setOnlyAuto(false); + rtpServerParam.setDisableAudio(!channel.isHasAudio()); + SSRCInfo ssrcInfo = receiveRtpServerService.openRTPServer(rtpServerParam, (code, msg, result) -> { + if (code == InviteErrorCode.SUCCESS.getCode() && result != null && result.getHookData() != null) { + // hook响应 + StreamInfo streamInfo = onPublishHandlerForDownload(mediaServerItem, result.getHookData().getMediaInfo(), device, channel, startTime, endTime); + if (streamInfo == null) { + log.warn("[录像下载] 获取流地址信息失败"); + callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); + return; + } + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); + log.info("[录像下载] 调用成功 deviceId: {}, channelId: {}, 开始时间: {}, 结束时间: {}", device.getDeviceId(), channel, startTime, endTime); + }else { + if (callback != null) { + callback.run(code, msg, null); + } + inviteStreamService.call(InviteSessionType.DOWNLOAD, channel.getId(), null, code, msg, null); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.DOWNLOAD, channel.getId()); + if (result != null && result.getSsrcInfo() != null) { + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(result.getSsrcInfo().getStream()); + if (ssrcTransaction != null) { + try { + cmder.streamByeCmd(device, channel.getDeviceId(), ssrcTransaction.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + log.error("[录像下载] 发送BYE失败 {}", e.getMessage()); + } finally { + sessionManager.removeByStream(ssrcTransaction.getStream()); + } + } + } + } + }); + if (ssrcInfo == null || ssrcInfo.getPort() <= 0) { + log.info("[录像下载端口/SSRC]获取失败,deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channel.getDeviceId(), ssrcInfo); + if (callback != null) { + callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "获取端口或者ssrc失败", null); + } + inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), + null); + return; + } + log.info("[录像下载] deviceId: {}, channelId: {}, 下载速度:{}, 收流端口:{}, 收流模式:{}, SSRC: {}({}), SSRC校验:{}", + device.getDeviceId(), channel.getDeviceId(), downloadSpeed, ssrcInfo.getPort(), device.getStreamMode(), + ssrcInfo.getSsrc(), String.format("%08x", Long.parseLong(ssrcInfo.getSsrc())).toUpperCase(), + device.isSsrcCheck()); + + // 初始化redis中的invite消息状态 + InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, + mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.DOWNLOAD, + InviteSessionStatus.ready); + + inviteStreamService.updateInviteInfo(inviteInfo); + try { + cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channel, startTime, endTime, downloadSpeed, + eventResult -> { + // 对方返回错误 + callback.run(InviteErrorCode.FAIL.getCode(), String.format("录像下载失败, 错误码: %s, %s", eventResult.statusCode, eventResult.msg), null); + receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); + sessionManager.removeByStream(ssrcInfo.getStream()); + inviteStreamService.removeInviteInfo(inviteInfo); + }, eventResult ->{ + // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题 + InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channel, + callback, inviteInfo, InviteSessionType.DOWNLOAD); + + // 注册录像回调事件,录像下载结束后写入下载地址 + HookSubscribe.Event hookEventForRecord = (hookData) -> { + log.info("[录像下载] 收到录像写入磁盘消息: , {}/{}-{}", + inviteInfo.getDeviceId(), inviteInfo.getChannelId(), ssrcInfo.getStream()); + log.info("[录像下载] 收到录像写入磁盘消息内容: " + hookData); + RecordInfo recordInfo = hookData.getRecordInfo(); + String filePath = recordInfo.getFilePath(); + DownloadFileInfo downloadFileInfo = CloudRecordUtils.getDownloadFilePath(mediaServerItem, filePath); + InviteInfo inviteInfoForNew = inviteStreamService.getInviteInfo(inviteInfo.getType() + , inviteInfo.getChannelId(), inviteInfo.getStream()); + if (inviteInfoForNew != null && inviteInfoForNew.getStreamInfo() != null) { + inviteInfoForNew.getStreamInfo().setDownLoadFilePath(downloadFileInfo); + // 不可以马上移除会导致后续接口拿不到下载地址 + inviteStreamService.updateInviteInfo(inviteInfoForNew, 60*15L); + } + }; + Hook hook = Hook.getInstance(HookType.on_record_mp4, "rtp", ssrcInfo.getStream(), mediaServerItem.getId()); + // 设置过期时间,下载失败时自动处理订阅数据 + hook.setExpireTime(System.currentTimeMillis() + 24 * 60 * 60 * 1000); + subscribe.addSubscribe(hook, hookEventForRecord); + }, userSetting.getPlayTimeout().longValue()); + } catch (InvalidArgumentException | SipException | ParseException e) { + log.error("[命令发送失败] 录像下载: {}", e.getMessage()); + callback.run(InviteErrorCode.FAIL.getCode(),e.getMessage(), null); + receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); + sessionManager.removeByStream(ssrcInfo.getStream()); + inviteStreamService.removeInviteInfo(inviteInfo); + } + } + + @Override + public StreamInfo getDownLoadInfo(Device device, DeviceChannel channel, String stream) { + InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, channel.getId(), stream); + if (inviteInfo == null) { + String app = "rtp"; + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream); + if (streamAuthorityInfo != null) { + List allList = cloudRecordService.getAllList(null, app, stream, null, null, null, streamAuthorityInfo.getCallId(), null); + if (allList.isEmpty()) { + log.warn("[获取下载进度] 未查询到录像下载的信息 {}/{}-{}", device.getDeviceId(), channel.getDeviceId(), stream); + return null; + } + String filePath = allList.get(0).getFilePath(); + if (filePath == null) { + log.warn("[获取下载进度] 未查询到录像下载的文件路径 {}/{}-{}", device.getDeviceId(), channel.getDeviceId(), stream); + return null; + } + String mediaServerId = allList.get(0).getMediaServerId(); + MediaServer mediaServer = mediaServerService.getOne(mediaServerId); + if (mediaServer == null) { + log.warn("[获取下载进度] 未查询到录像下载的节点信息 {}/{}-{}", device.getDeviceId(), channel.getDeviceId(), stream); + return null; + } + log.warn("[获取下载进度] 发现下载已经结束,直接从数据库获取到文件 {}/{}-{}", device.getDeviceId(), channel.getDeviceId(), stream); + DownloadFileInfo downloadFileInfo = CloudRecordUtils.getDownloadFilePath(mediaServer, filePath); + StreamInfo streamInfo = new StreamInfo(); + streamInfo.setDownLoadFilePath(downloadFileInfo); + streamInfo.setApp(app); + streamInfo.setStream(stream); + streamInfo.setServerId(mediaServerId); + streamInfo.setProgress(1.0); + return streamInfo; + } + } + + if (inviteInfo == null || inviteInfo.getStreamInfo() == null) { + log.warn("[获取下载进度] 未查询到录像下载的信息 {}/{}-{}", device.getDeviceId(), channel.getDeviceId(), stream); + return null; + } + + if (inviteInfo.getStreamInfo().getProgress() == 1) { + return inviteInfo.getStreamInfo(); + } + + // 获取当前已下载时长 + MediaServer mediaServerItem = inviteInfo.getStreamInfo().getMediaServer(); + if (mediaServerItem == null) { + log.warn("[获取下载进度] 查询录像信息时发现节点不存在"); + return null; + } + String app = "rtp"; + Long duration = mediaServerService.updateDownloadProcess(mediaServerItem, app, stream); + if (duration == null || 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(MediaServer mediaServerItemInuse, MediaInfo mediaInfo, Device device, DeviceChannel channel, String startTime, String endTime) { + StreamInfo streamInfo = onPublishHandler(mediaServerItemInuse, mediaInfo, device, channel); + if (streamInfo != null) { + streamInfo.setProgress(0); + streamInfo.setStartTime(startTime); + streamInfo.setEndTime(endTime); + InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, channel.getId(), streamInfo.getStream()); + if (inviteInfo != null) { + log.info("[录像下载] 更新invite消息中的stream信息"); + inviteInfo.setStatus(InviteSessionStatus.ok); + inviteInfo.setStreamInfo(streamInfo); + inviteStreamService.updateInviteInfo(inviteInfo); + } + } + return streamInfo; + } + + + public StreamInfo onPublishHandler(MediaServer mediaServerItem, MediaInfo mediaInfo, Device device, DeviceChannel channel) { + StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(mediaServerItem, "rtp", mediaInfo.getStream(), mediaInfo, null); + streamInfo.setDeviceId(device.getDeviceId()); + streamInfo.setChannelId(channel.getId()); + return streamInfo; + } + + + @Override + public void zlmServerOffline(String mediaServerId) { + // 处理正在向上推流的上级平台 + List sendRtpInfos = sendRtpServerService.queryAll(); + if (!sendRtpInfos.isEmpty()) { + for (SendRtpInfo sendRtpInfo : sendRtpInfos) { + if (sendRtpInfo.getMediaServerId().equals(mediaServerId) && sendRtpInfo.isSendToPlatform()) { + Platform platform = platformService.queryPlatformByServerGBId(sendRtpInfo.getTargetId()); + CommonGBChannel channel = channelService.getOne(sendRtpInfo.getChannelId()); + try { + sipCommanderFroPlatform.streamByeCmd(platform, sendRtpInfo, channel); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + } + } + } + // 处理正在观看的国标设备 + List allSsrc = sessionManager.getAll(); + if (allSsrc.size() > 0) { + for (SsrcTransaction ssrcTransaction : allSsrc) { + if (ssrcTransaction.getMediaServerId().equals(mediaServerId)) { + Device device = deviceService.getDeviceByDeviceId(ssrcTransaction.getDeviceId()); + if (device == null) { + continue; + } + DeviceChannel deviceChannel = deviceChannelService.getOneById(ssrcTransaction.getChannelId()); + if (deviceChannel == null) { + continue; + } + try { + cmder.streamByeCmd(device, deviceChannel.getDeviceId(), + ssrcTransaction.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + log.error("[zlm离线]为正在使用此zlm的设备, 发送BYE失败 {}", e.getMessage()); + } + } + } + } + } + + @Override + public AudioBroadcastResult audioBroadcast(Device device, DeviceChannel deviceChannel, Boolean broadcastMode) { + // TODO 必须多端口模式才支持语音喊话鹤语音对讲 + if (device == null || deviceChannel == null) { + return null; + } + log.info("[语音喊话] device: {}, channel: {}", device.getDeviceId(), deviceChannel.getDeviceId()); + MediaServer mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(null); + if (broadcastMode == null) { + broadcastMode = true; + } + String app = broadcastMode?"broadcast":"talk"; + String stream = device.getDeviceId() + "_" + deviceChannel.getDeviceId(); + AudioBroadcastResult audioBroadcastResult = new AudioBroadcastResult(); + audioBroadcastResult.setApp(app); + audioBroadcastResult.setStream(stream); + audioBroadcastResult.setStreamInfo(new StreamContent(mediaServerService.getStreamInfoByAppAndStream(mediaServerItem, app, stream, null, null, null, false))); + audioBroadcastResult.setCodec("G.711"); + return audioBroadcastResult; + } + + @Override + public boolean audioBroadcastCmd(Device device, DeviceChannel channel, MediaServer mediaServerItem, String app, String stream, int timeout, boolean isFromPlatform, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException { + Assert.notNull(device, "设备不存在"); + Assert.notNull(channel, "通道不存在"); + log.info("[语音喊话] device: {}, channel: {}", device.getDeviceId(), channel.getDeviceId()); + DeviceChannel deviceChannel = deviceChannelService.getOne(device.getDeviceId(), channel.getDeviceId()); + if (deviceChannel == null) { + log.warn("开启语音广播的时候未找到通道: {}", channel.getDeviceId()); + event.call("开启语音广播的时候未找到通道"); + return false; + } + // 查询通道使用状态 + if (audioBroadcastManager.exit(deviceChannel.getId())) { + SendRtpInfo sendRtpInfo = sendRtpServerService.queryByChannelId(channel.getId(), device.getDeviceId()); + if (sendRtpInfo != null && sendRtpInfo.isOnlyAudio()) { + // 查询流是否存在,不存在则认为是异常状态 + Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, sendRtpInfo.getApp(), sendRtpInfo.getStream()); + if (streamReady) { + log.warn("语音广播已经开启: {}", channel.getDeviceId()); + event.call("语音广播已经开启"); + return false; + } else { + stopAudioBroadcast(device, channel); + } + } + } + + // 发送通知 + cmder.audioBroadcastCmd(device, channel.getDeviceId(), eventResultForOk -> { + // 发送成功 + AudioBroadcastCatch audioBroadcastCatch = new AudioBroadcastCatch(device.getDeviceId(), channel.getId(), mediaServerItem, app, stream, event, AudioBroadcastCatchStatus.Ready, isFromPlatform); + audioBroadcastManager.update(audioBroadcastCatch); + // 等待invite消息, 超时则结束 + String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId(); + if (!SipUtils.isFrontEnd(device.getDeviceId())) { + key += audioBroadcastCatch.getChannelId(); + } + dynamicTask.startDelay(key, ()->{ + log.info("[语音广播]等待invite消息超时:{}/{}", device.getDeviceId(), channel.getDeviceId()); + stopAudioBroadcast(device, channel); + }, 10*1000); + }, eventResultForError -> { + // 发送失败 + log.error("语音广播发送失败: {}:{}", channel.getDeviceId(), eventResultForError.msg); + event.call("语音广播发送失败"); + stopAudioBroadcast(device, channel); + }); + return true; + } + + @Override + public boolean audioBroadcastInUse(Device device, DeviceChannel channel) { + if (audioBroadcastManager.exit(channel.getId())) { + SendRtpInfo sendRtpInfo = sendRtpServerService.queryByChannelId(channel.getId(), device.getDeviceId()); + if (sendRtpInfo != null && sendRtpInfo.isOnlyAudio()) { + // 查询流是否存在,不存在则认为是异常状态 + MediaServer mediaServerServiceOne = mediaServerService.getOne(sendRtpInfo.getMediaServerId()); + Boolean streamReady = mediaServerService.isStreamReady(mediaServerServiceOne, sendRtpInfo.getApp(), sendRtpInfo.getStream()); + if (streamReady) { + log.warn("语音广播通道使用中: {}", channel.getDeviceId()); + return true; + } + } + } + return false; + } + + + @Override + public void stopAudioBroadcast(Device device, DeviceChannel channel) { + log.info("[停止对讲] 设备:{}, 通道:{}", device.getDeviceId(), channel.getDeviceId()); + List audioBroadcastCatchList = new ArrayList<>(); + if (channel == null) { + audioBroadcastCatchList.addAll(audioBroadcastManager.getByDeviceId(device.getDeviceId())); + } else { + audioBroadcastCatchList.addAll(audioBroadcastManager.getByDeviceId(device.getDeviceId())); + } + if (!audioBroadcastCatchList.isEmpty()) { + for (AudioBroadcastCatch audioBroadcastCatch : audioBroadcastCatchList) { + if (audioBroadcastCatch == null) { + continue; + } + SendRtpInfo sendRtpInfo = sendRtpServerService.queryByChannelId(channel.getId(), device.getDeviceId()); + if (sendRtpInfo != null) { + sendRtpServerService.delete(sendRtpInfo); + MediaServer mediaServer = mediaServerService.getOne(sendRtpInfo.getMediaServerId()); + mediaServerService.stopSendRtp(mediaServer, sendRtpInfo.getApp(), sendRtpInfo.getStream(), null); + try { + cmder.streamByeCmdForDeviceInvite(device, channel.getDeviceId(), audioBroadcastCatch.getSipTransactionInfo(), null); + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + log.error("[消息发送失败] 发送语音喊话BYE失败"); + } + } + + audioBroadcastManager.del(channel.getId()); + } + } + } + + @Override + public void zlmServerOnline(String mediaServerId) { + } + + @Override + public void pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException { + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); + if (null == inviteInfo || inviteInfo.getStreamInfo() == null) { + log.warn("streamId不存在!"); + throw new ServiceException("streamId不存在"); + } + inviteInfo.getStreamInfo().setPause(true); + inviteStreamService.updateInviteInfo(inviteInfo); + MediaServer mediaServerItem = inviteInfo.getStreamInfo().getMediaServer(); + if (null == mediaServerItem) { + log.warn("mediaServer 不存在!"); + throw new ServiceException("mediaServer不存在"); + } + // zlm 暂停RTP超时检查 + // 使用zlm中的流ID + String streamKey = inviteInfo.getStream(); + if (!mediaServerItem.isRtpEnable()) { + streamKey = Long.toHexString(Long.parseLong(inviteInfo.getSsrcInfo().getSsrc())).toUpperCase(); + } + Boolean result = mediaServerService.pauseRtpCheck(mediaServerItem, streamKey); + if (!result) { + throw new ServiceException("暂停RTP接收失败"); + } + Device device = deviceService.getDeviceByDeviceId(inviteInfo.getDeviceId()); + DeviceChannel channel = deviceChannelService.getOneById(inviteInfo.getChannelId()); + cmder.playPauseCmd(device, channel, inviteInfo.getStreamInfo()); + } + + @Override + public void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException { + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); + if (null == inviteInfo || inviteInfo.getStreamInfo() == null) { + log.warn("streamId不存在!"); + throw new ServiceException("streamId不存在"); + } + inviteInfo.getStreamInfo().setPause(false); + inviteStreamService.updateInviteInfo(inviteInfo); + MediaServer mediaServerItem = inviteInfo.getStreamInfo().getMediaServer(); + if (null == mediaServerItem) { + log.warn("mediaServer 不存在!"); + throw new ServiceException("mediaServer不存在"); + } + // zlm 暂停RTP超时检查 + // 使用zlm中的流ID + String streamKey = inviteInfo.getStream(); + if (!mediaServerItem.isRtpEnable()) { + streamKey = Long.toHexString(Long.parseLong(inviteInfo.getSsrcInfo().getSsrc())).toUpperCase(); + } + boolean result = mediaServerService.resumeRtpCheck(mediaServerItem, streamKey); + if (!result) { + throw new ServiceException("继续RTP接收失败"); + } + Device device = deviceService.getDeviceByDeviceId(inviteInfo.getDeviceId()); + DeviceChannel channel = deviceChannelService.getOneById(inviteInfo.getChannelId()); + cmder.playResumeCmd(device, channel, inviteInfo.getStreamInfo()); + } + + @Override + public void startPushStream(SendRtpInfo sendRtpInfo, DeviceChannel channel, SIPResponse sipResponse, Platform platform, CallIdHeader callIdHeader) { + // 开始发流 + MediaServer mediaInfo = mediaServerService.getOne(sendRtpInfo.getMediaServerId()); + + if (mediaInfo != null) { + try { + if (sendRtpInfo.isTcpActive()) { + mediaServerService.startSendRtpPassive(mediaInfo, sendRtpInfo, null); + } else { + mediaServerService.startSendRtp(mediaInfo, sendRtpInfo); + } + redisCatchStorage.sendPlatformStartPlayMsg(sendRtpInfo, channel, platform); + }catch (ControllerException e) { + log.error("RTP推流失败: {}", e.getMessage()); + startSendRtpStreamFailHand(sendRtpInfo, platform, callIdHeader); + return; + } + + log.info("RTP推流成功[ {}/{} ],{}, ", sendRtpInfo.getApp(), sendRtpInfo.getStream(), + sendRtpInfo.isTcpActive()?"被动发流": sendRtpInfo.getIp() + ":" + sendRtpInfo.getPort()); + + } + } + + @Override + public void startSendRtpStreamFailHand(SendRtpInfo sendRtpInfo, Platform platform, CallIdHeader callIdHeader) { + if (sendRtpInfo.isOnlyAudio()) { + Device device = deviceService.getDeviceByDeviceId(sendRtpInfo.getTargetId()); + DeviceChannel deviceChannel = deviceChannelService.getOneById(sendRtpInfo.getChannelId()); + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpInfo.getChannelId()); + if (audioBroadcastCatch != null) { + try { + cmder.streamByeCmd(device, deviceChannel.getDeviceId(), audioBroadcastCatch.getSipTransactionInfo(), null); + } catch (SipException | ParseException | InvalidArgumentException | + SsrcTransactionNotFoundException exception) { + log.error("[命令发送失败] 停止语音对讲: {}", exception.getMessage()); + } + } + } else { + if (platform != null) { + // 向上级平台 + CommonGBChannel channel = channelService.getOne(sendRtpInfo.getChannelId()); + try { + commanderForPlatform.streamByeCmd(platform, sendRtpInfo, channel); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + } + + } + } + + @Override + public void talkCmd(Device device, DeviceChannel channel, MediaServer mediaServerItem, String stream, AudioBroadcastEvent event) { + if (device == null || channel == null) { + return; + } + // TODO 必须多端口模式才支持语音喊话鹤语音对讲 + log.info("[语音对讲] device: {}, channel: {}", device.getDeviceId(), channel.getDeviceId()); + // 查询通道使用状态 + if (audioBroadcastManager.exit(channel.getId())) { + SendRtpInfo sendRtpInfo = sendRtpServerService.queryByChannelId(channel.getId(), device.getDeviceId()); + if (sendRtpInfo != null && sendRtpInfo.isOnlyAudio()) { + // 查询流是否存在,不存在则认为是异常状态 + MediaServer mediaServer = mediaServerService.getOne(sendRtpInfo.getMediaServerId()); + Boolean streamReady = mediaServerService.isStreamReady(mediaServer, sendRtpInfo.getApp(), sendRtpInfo.getStream()); + if (streamReady) { + log.warn("[语音对讲] 正在语音广播,无法开启语音通话: {}", channel.getDeviceId()); + event.call("正在语音广播"); + return; + } else { + stopAudioBroadcast(device, channel); + } + } + } + + SendRtpInfo sendRtpInfo = sendRtpServerService.queryByChannelId(channel.getId(), device.getDeviceId()); + if (sendRtpInfo != null) { + MediaServer mediaServer = mediaServerService.getOne(sendRtpInfo.getMediaServerId()); + Boolean streamReady = mediaServerService.isStreamReady(mediaServer, "rtp", sendRtpInfo.getReceiveStream()); + if (streamReady) { + log.warn("[语音对讲] 进行中: {}", channel.getDeviceId()); + event.call("语音对讲进行中"); + return; + } else { + stopTalk(device, channel); + } + } + + talk(mediaServerItem, device, channel, stream, (hookData) -> { + log.info("[语音对讲] 收到设备发来的流"); + }, eventResult -> { + log.warn("[语音对讲] 失败,{}/{}, 错误码 {} {}", device.getDeviceId(), channel.getDeviceId(), eventResult.statusCode, eventResult.msg); + event.call("失败,错误码 " + eventResult.statusCode + ", " + eventResult.msg); + }, () -> { + log.warn("[语音对讲] 失败,{}/{} 超时", device.getDeviceId(), channel.getDeviceId()); + event.call("失败,超时 "); + stopTalk(device, channel); + }, errorMsg -> { + log.warn("[语音对讲] 失败,{}/{} {}", device.getDeviceId(), channel.getDeviceId(), errorMsg); + event.call(errorMsg); + stopTalk(device, channel); + }); + } + + private void stopTalk(Device device, DeviceChannel channel) { + stopTalk(device, channel, null); + } + + @Override + public void stopTalk(Device device, DeviceChannel channel, Boolean streamIsReady) { + log.info("[语音对讲] 停止, {}/{}", device.getDeviceId(), channel.getDeviceId()); + SendRtpInfo sendRtpInfo = sendRtpServerService.queryByChannelId(channel.getId(), device.getDeviceId()); + if (sendRtpInfo == null) { + log.info("[语音对讲] 停止失败, 未找到发送信息,可能已经停止"); + return; + } + // 停止向设备推流 + String mediaServerId = sendRtpInfo.getMediaServerId(); + if (mediaServerId == null) { + return; + } + + MediaServer mediaServer = mediaServerService.getOne(mediaServerId); + + if (streamIsReady == null || streamIsReady) { + mediaServerService.stopSendRtp(mediaServer, sendRtpInfo.getApp(), sendRtpInfo.getStream(), sendRtpInfo.getSsrc()); + } + + ssrcFactory.releaseSsrc(mediaServerId, sendRtpInfo.getSsrc()); + + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(sendRtpInfo.getStream()); + if (ssrcTransaction != null) { + try { + cmder.streamByeCmd(device, channel.getDeviceId(), sendRtpInfo.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + log.info("[语音对讲] 停止消息发送失败,可能已经停止"); + } + } + sendRtpServerService.deleteByChannel(channel.getId(), device.getDeviceId()); + } + + @Override + public void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback) { + Device device = deviceService.getDeviceByDeviceId(deviceId); + Assert.notNull(device, "设备不存在"); + DeviceChannel channel = deviceChannelService.getOne(deviceId, channelId); + Assert.notNull(channel, "通道不存在"); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); + if (inviteInfo != null) { + if (inviteInfo.getStreamInfo() != null) { + // 已存在线直接截图 + MediaServer mediaServerItemInuse = inviteInfo.getStreamInfo().getMediaServer(); + String streamUrl; + if (mediaServerItemInuse.getRtspPort() != 0) { + streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", inviteInfo.getStreamInfo().getStream()); + }else { + streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.mp4", mediaServerItemInuse.getHttpPort(), "rtp", inviteInfo.getStreamInfo().getStream()); + } + String path = "snap"; + // 请求截图 + log.info("[请求截图]: " + fileName); + mediaServerService.getSnap(mediaServerItemInuse, streamUrl, 15, 1, path, fileName); + File snapFile = new File(path + File.separator + fileName); + if (snapFile.exists()) { + errorCallback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), snapFile.getAbsoluteFile()); + }else { + errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); + } + return; + } + } + + MediaServer newMediaServerItem = getNewMediaServerItem(device); + play(newMediaServerItem, deviceId, channelId, null, (code, msg, data)->{ + if (code == InviteErrorCode.SUCCESS.getCode()) { + InviteInfo inviteInfoForPlay = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); + if (inviteInfoForPlay != null && inviteInfoForPlay.getStreamInfo() != null) { + getSnap(deviceId, channelId, fileName, errorCallback); + }else { + errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); + } + }else { + errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); + } + }); + } + + @Override + public void stop(InviteSessionType type, Device device, DeviceChannel channel, String stream) { + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(type, stream); + if (inviteInfo == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "点播未找到"); + } + if (InviteSessionStatus.ok == inviteInfo.getStatus()) { + try { + log.info("[停止点播/回放/下载] {}/{}", device.getDeviceId(), channel.getDeviceId()); + cmder.streamByeCmd(device, channel.getDeviceId(), inviteInfo.getStream(), null, null); + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { + log.error("[命令发送失败] 停止点播/回放/下载, 发送BYE: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } + inviteStreamService.removeInviteInfoByDeviceAndChannel(inviteInfo.getType(), channel.getId()); + if (inviteInfo.getType() == InviteSessionType.PLAY) { + deviceChannelService.stopPlay(channel.getId()); + } + if (inviteInfo.getStreamInfo() != null) { + receiveRtpServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServer(), inviteInfo.getSsrcInfo()); + } + } + + @Override + public void stop(InviteInfo inviteInfo) { + Assert.notNull(inviteInfo, "参数异常"); + DeviceChannel channel = deviceChannelService.getOneForSourceById(inviteInfo.getChannelId()); + if (channel == null) { + log.warn("[停止点播] 发现通道不存在"); + return; + } + Device device = deviceService.getDevice(channel.getDeviceDbId()); + if (device == null) { + log.warn("[停止点播] 发现设备不存在"); + return; + } + if (InviteSessionStatus.ok == inviteInfo.getStatus()) { + try { + log.info("[停止点播/回放/下载] {}/{}", device.getDeviceId(), channel.getDeviceId()); + cmder.streamByeCmd(device, channel.getDeviceId(), inviteInfo.getStream(), null, null); + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { + log.warn("[命令发送失败] 停止点播/回放/下载, 发送BYE: {}", e.getMessage()); + } + } + inviteStreamService.removeInviteInfoByDeviceAndChannel(inviteInfo.getType(), channel.getId()); + if (inviteInfo.getType() == InviteSessionType.PLAY) { + deviceChannelService.stopPlay(channel.getId()); + } + if (inviteInfo.getStreamInfo() != null) { + receiveRtpServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServer(), inviteInfo.getSsrcInfo()); + } + } + + @Override + public void play(CommonGBChannel channel, ErrorCallback callback) { + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); + if (device == null) { + log.warn("[点播] 未找到通道{}的设备信息", channel); + throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); + } + MediaServer mediaServer = getNewMediaServerItem(device); + if (mediaServer == null) { + log.warn("[点播] 未找到可用媒体节点"); + throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); + } + DeviceChannel deviceChannel = deviceChannelService.getOneById(channel.getGbId()); + play(mediaServer, device, deviceChannel, null, callback); + } + + @Override + public void playBack(CommonGBChannel channel, Long startTime, Long stopTime, ErrorCallback callback) { + if (startTime == null || stopTime == null) { + throw new PlayException(Response.BAD_REQUEST, "bad request"); + } + // 国标通道 + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); + if (device == null) { + log.warn("[点播] 未找到通道{}的设备信息", channel); + throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); + } + DeviceChannel deviceChannel = deviceChannelService.getOneById(channel.getGbId()); + if (deviceChannel == null) { + log.warn("[点播] 未找到通道{}", channel.getGbDeviceId()); + throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); + } + String startTimeStr = DateUtil.timestampTo_yyyy_MM_dd_HH_mm_ss(startTime); + String stopTimeStr = DateUtil.timestampTo_yyyy_MM_dd_HH_mm_ss(stopTime); + playBack(device, deviceChannel, startTimeStr, stopTimeStr, callback); + } + + @Override + public void download(CommonGBChannel channel, Long startTime, Long stopTime, Integer downloadSpeed, ErrorCallback callback) { + if (startTime == null || stopTime == null || downloadSpeed == null) { + throw new PlayException(Response.BAD_REQUEST, "bad request"); + } + // 国标通道 + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); + if (device == null) { + log.warn("[点播] 未找到通道{}的设备信息", channel); + throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); + } + DeviceChannel deviceChannel = deviceChannelService.getOneById(channel.getGbId()); + if (deviceChannel == null) { + log.warn("[点播] 未找到通道{}", channel.getGbDeviceId()); + throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); + } + String startTimeStr = DateUtil.timestampTo_yyyy_MM_dd_HH_mm_ss(startTime); + String stopTimeStr = DateUtil.timestampTo_yyyy_MM_dd_HH_mm_ss(stopTime); + download(device, deviceChannel, startTimeStr, stopTimeStr, downloadSpeed, callback); + + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/RegionServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/RegionServiceImpl.java new file mode 100644 index 000000000..9fb2fdc80 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/RegionServiceImpl.java @@ -0,0 +1,227 @@ +package com.genersoft.iot.vmp.gb28181.service.impl; + +import com.genersoft.iot.vmp.common.CivilCodePo; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.Region; +import com.genersoft.iot.vmp.gb28181.bean.RegionTree; +import com.genersoft.iot.vmp.gb28181.dao.CommonGBChannelMapper; +import com.genersoft.iot.vmp.gb28181.dao.RegionMapper; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; +import com.genersoft.iot.vmp.gb28181.service.IRegionService; +import com.genersoft.iot.vmp.utils.CivilCodeUtil; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +import java.util.*; + +/** + * 区域管理类 + */ +@Service +public class RegionServiceImpl implements IRegionService { + + + private static final Logger log = LoggerFactory.getLogger(RegionServiceImpl.class); + @Autowired + private RegionMapper regionMapper; + + @Autowired + private CommonGBChannelMapper commonGBChannelMapper; + + @Autowired + private IGbChannelService gbChannelService; + + @Autowired + private EventPublisher eventPublisher; + + @Override + public void add(Region region) { + Assert.hasLength(region.getName(), "名称必须存在"); + Assert.hasLength(region.getDeviceId(), "国标编号必须存在"); + if (ObjectUtils.isEmpty(region.getParentDeviceId()) || ObjectUtils.isEmpty(region.getParentDeviceId().trim())) { + region.setParentDeviceId(null); + } + region.setCreateTime(DateUtil.getNow()); + region.setUpdateTime(DateUtil.getNow()); + try { + regionMapper.add(region); + }catch (DuplicateKeyException e){ + throw new ControllerException(ErrorCode.ERROR100.getCode(), "此行政区划已存在"); + } + + } + + @Override + @Transactional + public boolean deleteByDeviceId(Integer regionDeviceId) { + Region region = regionMapper.queryOne(regionDeviceId); + // 获取所有子节点 + List allChildren = getAllChildren(regionDeviceId); + allChildren.add(region); + // 设置使用这些节点的通道的civilCode为null, + gbChannelService.removeCivilCode(allChildren); + regionMapper.batchDelete(allChildren); + return true; + } + + private List getAllChildren(Integer deviceId) { + if (deviceId == null) { + return new ArrayList<>(); + } + List children = regionMapper.getChildren(deviceId); + if (ObjectUtils.isEmpty(children)) { + return children; + } + List regions = new ArrayList<>(children); + for (Region region : children) { + if (region.getDeviceId().length() < 8) { + regions.addAll(getAllChildren(region.getId())); + } + } + return regions; + } + + @Override + public PageInfo query(String query, int page, int count) { + PageHelper.startPage(page, count); + List regionList = regionMapper.query(query, null); + return new PageInfo<>(regionList); + } + + @Override + @Transactional + public void update(Region region) { + Assert.notNull(region.getDeviceId(), "编号不可为NULL"); + Assert.notNull(region.getName(), "名称不可为NULL"); + Region regionInDb = regionMapper.queryOne(region.getId()); + Assert.notNull(regionInDb, "待更新行政区划在数据库中不存在"); + if (!regionInDb.getDeviceId().equals(region.getDeviceId())) { + Region regionNewInDb = regionMapper.queryByDeviceId(region.getDeviceId()); + Assert.isNull(regionNewInDb, "此行政区划已存在"); + // 编号发生变化,把分配了这个行政区划的通道全部更新,并发送数据 + gbChannelService.updateCivilCode(regionInDb.getDeviceId(), region.getDeviceId()); + // 子节点信息更新 + regionMapper.updateChild(region.getId(), region.getDeviceId()); + } + regionMapper.update(region); + // 发送变化通知 + try { + // 发送catalog + eventPublisher.catalogEventPublish(null, CommonGBChannel.build(region), CatalogEvent.UPDATE); + }catch (Exception e) { + log.warn("[行政区划变化] 发送失败,{}", region.getDeviceId(), e); + } + } + + @Override + public List getAllChild(String parent) { + List allChild = CivilCodeUtil.INSTANCE.getAllChild(parent); + Collections.sort(allChild); + return allChild; + } + + @Override + public Region queryRegionByDeviceId(String regionDeviceId) { + return null; + } + + @Override + public List queryForTree(String query, Integer parent) { + List regionList = regionMapper.queryForTree(query, parent); + if (parent != null) { + Region parentRegion = regionMapper.queryOne(parent); + if (parentRegion != null) { + List channelList = commonGBChannelMapper.queryForRegionTreeByCivilCode(query, parentRegion.getDeviceId()); + regionList.addAll(channelList); + } + } + return regionList; + } + + @Override + public void syncFromChannel() { + // 获取未初始化的行政区划节点 + List civilCodeList = regionMapper.getUninitializedCivilCode(); + if (civilCodeList.isEmpty()) { + return; + } + List regionList = new ArrayList<>(); + // 收集节点的父节点,用于验证哪些节点的父节点不存在,方便一并存入 + Map regionMapForVerification = new HashMap<>(); + civilCodeList.forEach(civilCode->{ + CivilCodePo civilCodePo = CivilCodeUtil.INSTANCE.getCivilCodePo(civilCode); + if (civilCodePo != null) { + Region region = Region.getInstance(civilCodePo); + regionList.add(region); + // 获取全部的父节点 + List civilCodePoList = CivilCodeUtil.INSTANCE.getAllParentCode(civilCode); + if (!civilCodePoList.isEmpty()) { + for (CivilCodePo codePo : civilCodePoList) { + regionMapForVerification.put(codePo.getCode(), Region.getInstance(codePo)); + } + } + } + }); + if (regionList.isEmpty()){ + return; + } + if (!regionMapForVerification.isEmpty()) { + // 查询数据库中已经存在的. + List civilCodesInDb = regionMapper.queryInList(regionMapForVerification.keySet()); + if (!civilCodesInDb.isEmpty()) { + for (String code : civilCodesInDb) { + regionMapForVerification.remove(code); + } + } + } + for (Region region : regionList) { + regionMapForVerification.put(region.getDeviceId(), region); + } + + regionMapper.batchAdd(new ArrayList<>(regionMapForVerification.values())); + } + + @Override + public boolean delete(int id) { + return regionMapper.delete(id) > 0; + } + + @Override + @Transactional + public boolean batchAdd(List regionList) { + if (regionList== null || regionList.isEmpty()) { + return false; + } + Map regionMapForVerification = new HashMap<>(); + for (Region region : regionList) { + regionMapForVerification.put(region.getDeviceId(), region); + } + // 查询数据库中已经存在的. + List regionListInDb = regionMapper.queryInRegionListByDeviceId(regionList); + if (!regionListInDb.isEmpty()) { + for (Region region : regionListInDb) { + regionMapForVerification.remove(region.getDeviceId()); + } + } + if (!regionMapForVerification.isEmpty()) { + List regions = new ArrayList<>(regionMapForVerification.values()); + regionMapper.batchAdd(regions); + regionMapper.updateParentId(regions); + } + + return true; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/AudioBroadcastManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/AudioBroadcastManager.java index 24eadbace..57067f176 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/AudioBroadcastManager.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/AudioBroadcastManager.java @@ -2,105 +2,58 @@ package com.genersoft.iot.vmp.gb28181.session; import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch; -import com.genersoft.iot.vmp.gb28181.utils.SipUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; -import java.util.stream.Stream; /** * 语音广播消息管理类 * @author lin */ +@Slf4j @Component public class AudioBroadcastManager { - private final static Logger logger = LoggerFactory.getLogger(AudioBroadcastManager.class); - @Autowired private SipConfig config; - public static Map data = new ConcurrentHashMap<>(); + public static Map data = new ConcurrentHashMap<>(); public void update(AudioBroadcastCatch audioBroadcastCatch) { - if (SipUtils.isFrontEnd(audioBroadcastCatch.getDeviceId())) { - audioBroadcastCatch.setChannelId(audioBroadcastCatch.getDeviceId()); - data.put(audioBroadcastCatch.getDeviceId(), audioBroadcastCatch); - }else { - data.put(audioBroadcastCatch.getDeviceId() + audioBroadcastCatch.getChannelId(), audioBroadcastCatch); - } + data.put(audioBroadcastCatch.getChannelId(), audioBroadcastCatch); } - public void del(String deviceId, String channelId) { - if (SipUtils.isFrontEnd(deviceId)) { - data.remove(deviceId); - }else { - data.remove(deviceId + channelId); - } + public void del(Integer channelId) { + data.remove(channelId); } - public void delByDeviceId(String deviceId) { - for (String key : data.keySet()) { - if (key.startsWith(deviceId)) { - data.remove(key); - } - } - } - public List getAll(){ Collection values = data.values(); return new ArrayList<>(values); } - public boolean exit(String deviceId, String channelId) { - for (String key : data.keySet()) { - if (SipUtils.isFrontEnd(deviceId)) { - return key.equals(deviceId); - }else { - return key.equals(deviceId + channelId); - } - } - return false; + public boolean exit(Integer channelId) { + return data.containsKey(channelId); } - public AudioBroadcastCatch get(String deviceId, String channelId) { - AudioBroadcastCatch audioBroadcastCatch; - if (SipUtils.isFrontEnd(deviceId)) { - audioBroadcastCatch = data.get(deviceId); - }else { - audioBroadcastCatch = data.get(deviceId + channelId); - } - if (audioBroadcastCatch == null) { - Stream allAudioBroadcastCatchStreamForDevice = data.values().stream().filter( - audioBroadcastCatchItem -> Objects.equals(audioBroadcastCatchItem.getDeviceId(), deviceId)); - List audioBroadcastCatchList = allAudioBroadcastCatchStreamForDevice.collect(Collectors.toList()); - if (audioBroadcastCatchList.size() == 1 && Objects.equals(config.getId(), channelId)) { - audioBroadcastCatch = audioBroadcastCatchList.get(0); - } - } - - return audioBroadcastCatch; + public AudioBroadcastCatch get(Integer channelId) { + return data.get(channelId); } - public List get(String deviceId) { + public List getByDeviceId(String deviceId) { List audioBroadcastCatchList= new ArrayList<>(); - if (SipUtils.isFrontEnd(deviceId)) { - if (data.get(deviceId) != null) { - audioBroadcastCatchList.add(data.get(deviceId)); - } - }else { - for (String key : data.keySet()) { - if (key.startsWith(deviceId)) { - audioBroadcastCatchList.add(data.get(key)); - } + for (AudioBroadcastCatch broadcastCatch : data.values()) { + if (broadcastCatch.getDeviceId().equals(deviceId)) { + audioBroadcastCatchList.add(broadcastCatch); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java index 38cdf7c45..baa9e2f21 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java @@ -1,10 +1,7 @@ package com.genersoft.iot.vmp.gb28181.session; -import com.genersoft.iot.vmp.gb28181.bean.CatalogData; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.SyncStatus; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -20,7 +17,7 @@ public class CatalogDataCatch { public static Map data = new ConcurrentHashMap<>(); @Autowired - private IVideoManagerStorage storager; + private IDeviceChannelService deviceChannelService; public void addReady(Device device, int sn ) { CatalogData catalogData = data.get(device.getDeviceId()); @@ -35,7 +32,8 @@ public class CatalogDataCatch { } } - public void put(String deviceId, int sn, int total, Device device, List deviceChannelList) { + public void put(String deviceId, int sn, int total, Device device, List deviceChannelList, + List regionList, List groupList) { CatalogData catalogData = data.get(deviceId); if (catalogData == null) { catalogData = new CatalogData(); @@ -43,6 +41,8 @@ public class CatalogDataCatch { catalogData.setTotal(total); catalogData.setDevice(device); catalogData.setChannelList(deviceChannelList); + catalogData.setRegionListList(regionList); + catalogData.setGroupListListList(groupList); catalogData.setStatus(CatalogData.CatalogDataStatus.runIng); catalogData.setLastTime(Instant.now()); data.put(deviceId, catalogData); @@ -54,12 +54,31 @@ public class CatalogDataCatch { catalogData.setTotal(total); catalogData.setDevice(device); catalogData.setStatus(CatalogData.CatalogDataStatus.runIng); - catalogData.getChannelList().addAll(deviceChannelList); + + if (deviceChannelList != null && !deviceChannelList.isEmpty()) { + if (catalogData.getChannelList() != null) { + catalogData.getChannelList().addAll(deviceChannelList); + } + } + if (regionList != null && !regionList.isEmpty()) { + if (catalogData.getRegionListList() != null) { + catalogData.getRegionListList().addAll(regionList); + }else { + catalogData.setRegionListList(regionList); + } + } + if (groupList != null && !groupList.isEmpty()) { + if (catalogData.getGroupListListList() != null) { + catalogData.getGroupListListList().addAll(groupList); + }else { + catalogData.setGroupListListList(groupList); + } + } catalogData.setLastTime(Instant.now()); } } - public List get(String deviceId) { + public List getDeviceChannelList(String deviceId) { CatalogData catalogData = data.get(deviceId); if (catalogData == null) { return null; @@ -67,6 +86,22 @@ public class CatalogDataCatch { return catalogData.getChannelList(); } + public List getRegionList(String deviceId) { + CatalogData catalogData = data.get(deviceId); + if (catalogData == null) { + return null; + } + return catalogData.getRegionListList(); + } + + public List getGroupList(String deviceId) { + CatalogData catalogData = data.get(deviceId); + if (catalogData == null) { + return null; + } + return catalogData.getGroupListListList(); + } + public int getTotal(String deviceId) { CatalogData catalogData = data.get(deviceId); if (catalogData == null) { @@ -113,9 +148,9 @@ public class CatalogDataCatch { // 超过五秒收不到消息任务超时, 只更新这一部分数据, 收到数据与声明的总数一致,则重置通道数据,数据不全则只对收到的数据做更新操作 if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.runIng)) { if (catalogData.getTotal() == catalogData.getChannelList().size()) { - storager.resetChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList()); + deviceChannelService.resetChannels(catalogData.getDevice().getId(), catalogData.getChannelList()); }else { - storager.updateChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList()); + deviceChannelService.updateChannels(catalogData.getDevice(), catalogData.getChannelList()); } String errorMsg = "更新成功,共" + catalogData.getTotal() + "条,已更新" + catalogData.getChannelList().size() + "条"; catalogData.setErrorMsg(errorMsg); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SipInviteSessionManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SipInviteSessionManager.java new file mode 100755 index 000000000..202c7fb64 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SipInviteSessionManager.java @@ -0,0 +1,101 @@ +package com.genersoft.iot.vmp.gb28181.session; + +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; +import com.genersoft.iot.vmp.utils.redis.RedisUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * 视频流session管理器,管理视频预览、预览回放的通信句柄 + */ +@Component +public class SipInviteSessionManager { + + @Autowired + private UserSetting userSetting; + + @Autowired + private RedisTemplate redisTemplate; + + /** + * 添加一个点播/回放的事务信息 + */ + public void put(SsrcTransaction ssrcTransaction){ + redisTemplate.opsForValue().set(VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId() + + ":" + ssrcTransaction.getStream(), ssrcTransaction); + + redisTemplate.opsForValue().set(VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() + + ":" + ssrcTransaction.getCallId(), ssrcTransaction); + } + + public SsrcTransaction getSsrcTransactionByStream(String stream){ + String key = VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId() + ":" + stream; + return (SsrcTransaction)redisTemplate.opsForValue().get(key); + } + + public SsrcTransaction getSsrcTransactionByCallId(String callId){ + String key = VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() + ":" + callId; + return (SsrcTransaction)redisTemplate.opsForValue().get(key); + } + + public List getSsrcTransactionByDeviceId(String deviceId){ + String key = VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() + ":*"; + + List scanResult = RedisUtil.scan(redisTemplate, key); + if (scanResult.isEmpty()) { + return new ArrayList<>(); + } + List result = new ArrayList<>(); + for (Object keyObj : scanResult) { + SsrcTransaction ssrcTransaction = (SsrcTransaction)redisTemplate.opsForValue().get(keyObj); + if (ssrcTransaction != null && ssrcTransaction.getDeviceId().equals(deviceId)) { + result.add(ssrcTransaction); + } + } + return result; + + } + + public void removeByStream(String stream) { + SsrcTransaction ssrcTransaction = getSsrcTransactionByStream(stream); + if (ssrcTransaction == null ) { + return; + } + redisTemplate.delete(VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId() + ":" + stream); + if (ssrcTransaction.getCallId() != null) { + redisTemplate.delete(VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() + ":" + ssrcTransaction.getCallId()); + } + } + + public void removeByCallId(String callId) { + SsrcTransaction ssrcTransaction = getSsrcTransactionByCallId(callId); + if (ssrcTransaction == null ) { + return; + } + redisTemplate.delete(VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() + ":" + callId); + if (ssrcTransaction.getStream() != null) { + redisTemplate.delete(VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId() + ":" + ssrcTransaction.getStream()); + } + } + + public List getAll() { + String key = VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() + ":*"; + + List scanResult = RedisUtil.scan(redisTemplate, key); + if (scanResult.isEmpty()) { + return new ArrayList<>(); + } + List result = new ArrayList<>(); + for (Object keyObj : scanResult) { + SsrcTransaction ssrcTransaction = (SsrcTransaction)redisTemplate.opsForValue().get(keyObj); + result.add(ssrcTransaction); + } + return result; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java deleted file mode 100755 index cb2caeca6..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.session; - -import com.genersoft.iot.vmp.common.InviteSessionType; -import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; -import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; -import com.genersoft.iot.vmp.utils.JsonUtil; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; -import gov.nist.javax.sip.message.SIPResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Component; -import org.springframework.util.ObjectUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * 视频流session管理器,管理视频预览、预览回放的通信句柄 - */ -@Component -public class VideoStreamSessionManager { - - @Autowired - private UserSetting userSetting; - - @Autowired - private RedisTemplate redisTemplate; - - /** - * 添加一个点播/回放的事务信息 - * 后续可以通过流Id/callID - * @param deviceId 设备ID - * @param channelId 通道ID - * @param callId 一次请求的CallID - * @param stream 流名称 - * @param mediaServerId 所使用的流媒体ID - * @param response 回复 - */ - public void put(String deviceId, String channelId, String callId, String stream, String ssrc, String mediaServerId, SIPResponse response, InviteSessionType type){ - SsrcTransaction ssrcTransaction = new SsrcTransaction(); - ssrcTransaction.setDeviceId(deviceId); - ssrcTransaction.setChannelId(channelId); - ssrcTransaction.setStream(stream); - ssrcTransaction.setSipTransactionInfo(new SipTransactionInfo(response)); - ssrcTransaction.setCallId(callId); - ssrcTransaction.setSsrc(ssrc); - ssrcTransaction.setMediaServerId(mediaServerId); - ssrcTransaction.setType(type); - - redisTemplate.opsForValue().set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() - + ":" + deviceId + ":" + channelId + ":" + callId + ":" + stream, ssrcTransaction); - } - - public SsrcTransaction getSsrcTransaction(String deviceId, String channelId, String callId, String stream){ - - if (ObjectUtils.isEmpty(deviceId)) { - deviceId ="*"; - } - if (ObjectUtils.isEmpty(channelId)) { - channelId ="*"; - } - if (ObjectUtils.isEmpty(callId)) { - callId ="*"; - } - if (ObjectUtils.isEmpty(stream)) { - stream ="*"; - } - String key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + ":" + deviceId + ":" + channelId + ":" + callId+ ":" + stream; - List scanResult = RedisUtil.scan(redisTemplate, key); - if (scanResult.size() == 0) { - return null; - } - 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 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 getSsrcTransactionForAll(String deviceId, String channelId, String callId, String stream){ - if (ObjectUtils.isEmpty(deviceId)) { - deviceId ="*"; - } - if (ObjectUtils.isEmpty(channelId)) { - channelId ="*"; - } - if (ObjectUtils.isEmpty(callId)) { - callId ="*"; - } - if (ObjectUtils.isEmpty(stream)) { - stream ="*"; - } - String key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + ":" + deviceId + ":" + channelId + ":" + callId+ ":" + stream; - List scanResult = RedisUtil.scan(redisTemplate, key); - if (scanResult.size() == 0) { - return null; - } - List result = new ArrayList<>(); - for (Object keyObj : scanResult) { - result.add((SsrcTransaction)redisTemplate.opsForValue().get(keyObj)); - } - return result; - } - - public String getMediaServerId(String deviceId, String channelId, String stream){ - SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream); - if (ssrcTransaction == null) { - return null; - } - return ssrcTransaction.getMediaServerId(); - } - - public String getSSRC(String deviceId, String channelId, String stream){ - SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream); - if (ssrcTransaction == null) { - return null; - } - return ssrcTransaction.getSsrc(); - } - - public void remove(String deviceId, String channelId, String stream) { - List ssrcTransactionList = getSsrcTransactionForAll(deviceId, channelId, null, stream); - if (ssrcTransactionList == null || ssrcTransactionList.isEmpty()) { - return; - } - for (SsrcTransaction ssrcTransaction : ssrcTransactionList) { - redisTemplate.delete(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + ":" - + deviceId + ":" + channelId + ":" + ssrcTransaction.getCallId() + ":" + ssrcTransaction.getStream()); - } - } - - public void removeByCallId(String deviceId, String channelId, String callId) { - SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, null); - if (ssrcTransaction == null ) { - return; - } - redisTemplate.delete(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + ":" - + deviceId + ":" + channelId + ":" + ssrcTransaction.getCallId() + ":" + ssrcTransaction.getStream()); - } - - - public List getAllSsrc() { - List ssrcTransactionKeys = RedisUtil.scan(redisTemplate, String.format("%s_*_*_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetting.getServerId())); - List result= new ArrayList<>(); - for (Object ssrcTransactionKey : ssrcTransactionKeys) { - String key = (String) ssrcTransactionKey; - SsrcTransaction ssrcTransaction = JsonUtil.redisJsonToObject(redisTemplate, key, SsrcTransaction.class); - result.add(ssrcTransaction); - } - return result; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java b/src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java index e2b2223fd..102f9d0c9 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java @@ -1,20 +1,19 @@ package com.genersoft.iot.vmp.gb28181.task; -import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; +import com.genersoft.iot.vmp.gb28181.service.IPlatformService; import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.service.IDeviceService; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.service.IPlatformService; -import com.genersoft.iot.vmp.service.impl.PlatformServiceImpl; +import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.core.annotation.Order; @@ -32,22 +31,17 @@ import java.util.Map; * 系统启动时控制设备 * @author lin */ +@Slf4j @Component @Order(value=14) public class SipRunner implements CommandLineRunner { - @Autowired - private IVideoManagerStorage storager; - @Autowired private IRedisCatchStorage redisCatchStorage; @Autowired private SSRCFactory ssrcFactory; - @Autowired - private UserSetting userSetting; - @Autowired private IDeviceService deviceService; @@ -57,10 +51,14 @@ public class SipRunner implements CommandLineRunner { @Autowired private IPlatformService platformService; + @Autowired + private IGbChannelService channelService; + @Autowired private ISIPCommanderForPlatform commanderForPlatform; - private final static Logger logger = LoggerFactory.getLogger(PlatformServiceImpl.class); + @Autowired + private ISendRtpServerService sendRtpServerService; @Override public void run(String... args) throws Exception { @@ -78,11 +76,11 @@ public class SipRunner implements CommandLineRunner { // 清理redis // 清理数据库不存在但是redis中存在的数据 List devicesInDb = deviceService.getAll(); - if (devicesInDb.size() == 0) { + if (devicesInDb.isEmpty()) { redisCatchStorage.removeAllDevice(); }else { List devicesInRedis = redisCatchStorage.getAllDevices(); - if (devicesInRedis.size() > 0) { + if (!devicesInRedis.isEmpty()) { Map deviceMapInDb = new HashMap<>(); devicesInDb.parallelStream().forEach(device -> { deviceMapInDb.put(device.getDeviceId(), device); @@ -97,21 +95,26 @@ public class SipRunner implements CommandLineRunner { // 查找国标推流 - List sendRtpItems = redisCatchStorage.queryAllSendRTPServer(); - if (sendRtpItems.size() > 0) { - for (SendRtpItem sendRtpItem : sendRtpItems) { + List sendRtpItems = redisCatchStorage.queryAllSendRTPServer(); + if (!sendRtpItems.isEmpty()) { + for (SendRtpInfo sendRtpItem : sendRtpItems) { MediaServer mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId()); - redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(),sendRtpItem.getChannelId(), sendRtpItem.getCallId(),sendRtpItem.getStream()); + CommonGBChannel channel = channelService.getOne(sendRtpItem.getChannelId()); + if (channel == null){ + continue; + } + sendRtpServerService.delete(sendRtpItem); if (mediaServerItem != null) { ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc()); boolean stopResult = mediaServerService.initStopSendRtp(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc()); if (stopResult) { - ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId()); + Platform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getTargetId()); + if (platform != null) { try { - commanderForPlatform.streamByeCmd(platform, sendRtpItem.getCallId()); + commanderForPlatform.streamByeCmd(platform, sendRtpItem, channel); } catch (InvalidArgumentException | ParseException | SipException e) { - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/CatalogSubscribeTask.java b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/CatalogSubscribeTask.java index d9270bbf0..e3f191242 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/CatalogSubscribeTask.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/CatalogSubscribeTask.java @@ -6,8 +6,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import gov.nist.javax.sip.message.SIPRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import javax.sip.DialogState; import javax.sip.InvalidArgumentException; @@ -20,15 +19,15 @@ import java.text.ParseException; * 目录订阅任务 * @author lin */ +@Slf4j public class CatalogSubscribeTask implements ISubscribeTask { - private final Logger logger = LoggerFactory.getLogger(CatalogSubscribeTask.class); - private Device device; + private final Device device; private final ISIPCommander sipCommander; private SIPRequest request; - private DynamicTask dynamicTask; + private final DynamicTask dynamicTask; - private String taskKey = "catalog-subscribe-timeout"; + private final String taskKey = "catalog-subscribe-timeout"; public CatalogSubscribeTask(Device device, ISIPCommander sipCommander, DynamicTask dynamicTask) { @@ -47,22 +46,22 @@ public class CatalogSubscribeTask implements ISubscribeTask { sipRequest = sipCommander.catalogSubscribe(device, request, eventResult -> { ResponseEvent event = (ResponseEvent) eventResult.event; // 成功 - logger.info("[目录订阅]成功: {}", device.getDeviceId()); + log.info("[目录订阅]成功: {}", device.getDeviceId()); ToHeader toHeader = (ToHeader)event.getResponse().getHeader(ToHeader.NAME); try { this.request.getToHeader().setTag(toHeader.getTag()); } catch (ParseException e) { - logger.info("[目录订阅]成功: 但为request设置ToTag失败"); + log.info("[目录订阅]成功: 但为request设置ToTag失败"); this.request = null; } },eventResult -> { this.request = null; // 失败 - logger.warn("[目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); + log.warn("[目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); dynamicTask.startDelay(taskKey, CatalogSubscribeTask.this, 2000); }); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 目录订阅: {}", e.getMessage()); + log.error("[命令发送失败] 目录订阅: {}", e.getMessage()); } if (sipRequest != null) { @@ -79,7 +78,7 @@ public class CatalogSubscribeTask implements ISubscribeTask { * COMPLETED-> Completed Dialog状态-已完成 * TERMINATED-> Terminated Dialog状态-终止 */ - logger.info("取消目录订阅时dialog状态为{}", DialogState.CONFIRMED); + log.info("取消目录订阅时dialog状态为{}", DialogState.CONFIRMED); if (dynamicTask.get(taskKey) != null) { dynamicTask.stop(taskKey); } @@ -89,20 +88,20 @@ public class CatalogSubscribeTask implements ISubscribeTask { ResponseEvent event = (ResponseEvent) eventResult.event; if (event.getResponse().getRawContent() != null) { // 成功 - logger.info("[取消目录订阅]成功: {}", device.getDeviceId()); + log.info("[取消目录订阅]成功: {}", device.getDeviceId()); }else { // 成功 - logger.info("[取消目录订阅]成功: {}", device.getDeviceId()); + log.info("[取消目录订阅]成功: {}", device.getDeviceId()); } if (callback != null) { callback.run(event.getResponse().getRawContent() != null); } },eventResult -> { // 失败 - logger.warn("[取消目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); + log.warn("[取消目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); }); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 取消目录订阅: {}", e.getMessage()); + log.error("[命令发送失败] 取消目录订阅: {}", e.getMessage()); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeTask.java b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeTask.java index 9fed0793b..646b31eac 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeTask.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeTask.java @@ -6,8 +6,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import gov.nist.javax.sip.message.SIPRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import javax.sip.InvalidArgumentException; import javax.sip.ResponseEvent; @@ -19,14 +18,14 @@ import java.text.ParseException; * 移动位置订阅的定时更新 * @author lin */ +@Slf4j public class MobilePositionSubscribeTask implements ISubscribeTask { - private final Logger logger = LoggerFactory.getLogger(MobilePositionSubscribeTask.class); - private Device device; - private ISIPCommander sipCommander; + private final Device device; + private final ISIPCommander sipCommander; private SIPRequest request; - private DynamicTask dynamicTask; - private String taskKey = "mobile-position-subscribe-timeout"; + private final DynamicTask dynamicTask; + private final String taskKey = "mobile-position-subscribe-timeout"; public MobilePositionSubscribeTask(Device device, ISIPCommander sipCommander, DynamicTask dynamicTask) { this.device = device; @@ -43,23 +42,23 @@ public class MobilePositionSubscribeTask implements ISubscribeTask { try { sipRequest = sipCommander.mobilePositionSubscribe(device, request, eventResult -> { // 成功 - logger.info("[移动位置订阅]成功: {}", device.getDeviceId()); + log.info("[移动位置订阅]成功: {}", device.getDeviceId()); ResponseEvent event = (ResponseEvent) eventResult.event; ToHeader toHeader = (ToHeader)event.getResponse().getHeader(ToHeader.NAME); try { this.request.getToHeader().setTag(toHeader.getTag()); } catch (ParseException e) { - logger.info("[移动位置订阅]成功: 为request设置ToTag失败"); + log.info("[移动位置订阅]成功: 为request设置ToTag失败"); this.request = null; } },eventResult -> { this.request = null; // 失败 - logger.warn("[移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); + log.warn("[移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); dynamicTask.startDelay(taskKey, MobilePositionSubscribeTask.this, 2000); }); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 移动位置订阅: {}", e.getMessage()); + log.error("[命令发送失败] 移动位置订阅: {}", e.getMessage()); } if (sipRequest != null) { this.request = sipRequest; @@ -85,20 +84,20 @@ public class MobilePositionSubscribeTask implements ISubscribeTask { ResponseEvent event = (ResponseEvent) eventResult.event; if (event.getResponse().getRawContent() != null) { // 成功 - logger.info("[取消移动位置订阅]成功: {}", device.getDeviceId()); + log.info("[取消移动位置订阅]成功: {}", device.getDeviceId()); }else { // 成功 - logger.info("[取消移动位置订阅]成功: {}", device.getDeviceId()); + log.info("[取消移动位置订阅]成功: {}", device.getDeviceId()); } if (callback != null) { callback.run(event.getResponse().getRawContent() != null); } },eventResult -> { // 失败 - logger.warn("[取消移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); + log.warn("[取消移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); }); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 取消移动位置订阅: {}", e.getMessage()); + log.error("[命令发送失败] 取消移动位置订阅: {}", e.getMessage()); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java index 52356c1df..5c2037578 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java @@ -2,21 +2,19 @@ package com.genersoft.iot.vmp.gb28181.transmit; import com.genersoft.iot.vmp.gb28181.event.EventPublisher; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.gb28181.event.sip.SipEvent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; import com.genersoft.iot.vmp.gb28181.transmit.event.response.ISIPResponseProcessor; import com.genersoft.iot.vmp.gb28181.transmit.event.timeout.ITimeoutProcessor; -import gov.nist.javax.sip.message.SIPRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import gov.nist.javax.sip.message.SIPResponse; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import javax.sip.*; -import javax.sip.header.*; -import javax.sip.message.Request; +import javax.sip.header.CallIdHeader; import javax.sip.message.Response; -import java.net.InetAddress; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -25,11 +23,10 @@ import java.util.concurrent.ConcurrentHashMap; * @author: panlinlin * @date: 2021年11月5日 下午15:32 */ +@Slf4j @Component public class SIPProcessorObserver implements ISIPProcessorObserver { - private final static Logger logger = LoggerFactory.getLogger(SIPProcessorObserver.class); - private static Map requestProcessorMap = new ConcurrentHashMap<>(); private static Map responseProcessorMap = new ConcurrentHashMap<>(); private static ITimeoutProcessor timeoutProcessor; @@ -76,7 +73,7 @@ public class SIPProcessorObserver implements ISIPProcessorObserver { String method = requestEvent.getRequest().getMethod(); ISIPRequestProcessor sipRequestProcessor = requestProcessorMap.get(method); if (sipRequestProcessor == null) { - logger.warn("不支持方法{}的request", method); + log.warn("不支持方法{}的request", method); // TODO 回复错误玛 return; } @@ -91,40 +88,40 @@ public class SIPProcessorObserver implements ISIPProcessorObserver { @Override @Async("taskExecutor") public void processResponse(ResponseEvent responseEvent) { - Response response = responseEvent.getResponse(); + SIPResponse response = (SIPResponse)responseEvent.getResponse(); int status = response.getStatusCode(); // Success if (((status >= Response.OK) && (status < Response.MULTIPLE_CHOICES)) || status == Response.UNAUTHORIZED) { - CSeqHeader cseqHeader = (CSeqHeader) responseEvent.getResponse().getHeader(CSeqHeader.NAME); - String method = cseqHeader.getMethod(); - ISIPResponseProcessor sipRequestProcessor = responseProcessorMap.get(method); - if (sipRequestProcessor != null) { - sipRequestProcessor.process(responseEvent); - } - if (status != Response.UNAUTHORIZED && responseEvent.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) { - CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME); + if (status != Response.UNAUTHORIZED && responseEvent.getResponse() != null && !sipSubscribe.isEmpty() ) { + CallIdHeader callIdHeader = response.getCallIdHeader(); if (callIdHeader != null) { - SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId()); - if (subscribe != null) { - SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(responseEvent); - sipSubscribe.removeOkSubscribe(callIdHeader.getCallId()); - subscribe.response(eventResult); + SipEvent sipEvent = sipSubscribe.getSubscribe(callIdHeader.getCallId()); + if (sipEvent != null && sipEvent.getOkEvent() != null) { + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult<>(responseEvent); + sipSubscribe.removeSubscribe(callIdHeader.getCallId()); + sipEvent.getOkEvent().response(eventResult); } } } + ISIPResponseProcessor sipRequestProcessor = responseProcessorMap.get(response.getCSeqHeader().getMethod()); + if (sipRequestProcessor != null) { + sipRequestProcessor.process(responseEvent); + } } else if ((status >= Response.TRYING) && (status < Response.OK)) { // 增加其它无需回复的响应,如101、180等 + // 更新sip订阅的时间 +// sipSubscribe.updateTimeout(response.getCallIdHeader().getCallId()); } else { - logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()); - if (responseEvent.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) { + log.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()); + if (responseEvent.getResponse() != null && !sipSubscribe.isEmpty() ) { CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME); if (callIdHeader != null) { - SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); - if (subscribe != null) { + SipEvent sipEvent = sipSubscribe.getSubscribe(callIdHeader.getCallId()); + if (sipEvent != null && sipEvent.getErrorEvent() != null) { SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(responseEvent); - subscribe.response(eventResult); - sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId()); + sipSubscribe.removeSubscribe(callIdHeader.getCallId()); + sipEvent.getErrorEvent().response(eventResult); } } } @@ -142,28 +139,28 @@ public class SIPProcessorObserver implements ISIPProcessorObserver { */ @Override public void processTimeout(TimeoutEvent timeoutEvent) { - logger.info("[消息发送超时]"); - ClientTransaction clientTransaction = timeoutEvent.getClientTransaction(); - - if (clientTransaction != null) { - logger.info("[发送错误订阅] clientTransaction != null"); - Request request = clientTransaction.getRequest(); - if (request != null) { - logger.info("[发送错误订阅] request != null"); - CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); - if (callIdHeader != null) { - logger.info("[发送错误订阅]"); - SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); - SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(timeoutEvent); - if (subscribe != null){ - subscribe.response(eventResult); - } - sipSubscribe.removeOkSubscribe(callIdHeader.getCallId()); - sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId()); - } - } - } - eventPublisher.requestTimeOut(timeoutEvent); + log.info("[消息发送超时]"); +// ClientTransaction clientTransaction = timeoutEvent.getClientTransaction(); +// +// if (clientTransaction != null) { +// log.info("[发送错误订阅] clientTransaction != null"); +// Request request = clientTransaction.getRequest(); +// if (request != null) { +// log.info("[发送错误订阅] request != null"); +// CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); +// if (callIdHeader != null) { +// log.info("[发送错误订阅]"); +// SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); +// SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(timeoutEvent); +// if (subscribe != null){ +// subscribe.response(eventResult); +// } +// sipSubscribe.removeOkSubscribe(callIdHeader.getCallId()); +// sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId()); +// } +// } +// } +// eventPublisher.requestTimeOut(timeoutEvent); } @Override @@ -202,4 +199,6 @@ public class SIPProcessorObserver implements ISIPProcessorObserver { } + + } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java index ce1074bd6..d894bb6f1 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java @@ -1,12 +1,13 @@ package com.genersoft.iot.vmp.gb28181.transmit; +import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.gb28181.SipLayer; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.gb28181.event.sip.SipEvent; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.utils.GitUtil; import gov.nist.javax.sip.SipProviderImpl; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; @@ -22,13 +23,13 @@ import java.text.ParseException; /** * 发送SIP消息 + * * @author lin */ +@Slf4j @Component public class SIPSender { - private final Logger logger = LoggerFactory.getLogger(SIPSender.class); - @Autowired private SipLayer sipLayer; @@ -37,75 +38,80 @@ public class SIPSender { @Autowired private SipSubscribe sipSubscribe; + @Autowired + private SipConfig sipConfig; public void transmitRequest(String ip, Message message) throws SipException, ParseException { - transmitRequest(ip, message, null, null); + transmitRequest(ip, message, null, null, null); } public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent) throws SipException, ParseException { - transmitRequest(ip, message, errorEvent, null); + transmitRequest(ip, message, errorEvent, null, null); } public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException { - ViaHeader viaHeader = (ViaHeader)message.getHeader(ViaHeader.NAME); - String transport = "UDP"; - if (viaHeader == null) { - logger.warn("[消息头缺失]: ViaHeader, 使用默认的UDP方式处理数据"); - }else { - transport = viaHeader.getTransport(); - } - if (message.getHeader(UserAgentHeader.NAME) == null) { - try { - message.addHeader(SipUtils.createUserAgentHeader(gitUtil)); - } catch (ParseException e) { - logger.error("添加UserAgentHeader失败", e); - } - } - - CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME); - // 添加错误订阅 - if (errorEvent != null) { - sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> { - sipSubscribe.removeErrorSubscribe(eventResult.callId); - sipSubscribe.removeOkSubscribe(eventResult.callId); - errorEvent.response(eventResult); - })); - } - // 添加订阅 - if (okEvent != null) { - sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> { - sipSubscribe.removeOkSubscribe(eventResult.callId); - sipSubscribe.removeErrorSubscribe(eventResult.callId); - okEvent.response(eventResult); - }); - } - if ("TCP".equals(transport)) { - SipProviderImpl tcpSipProvider = sipLayer.getTcpSipProvider(ip); - if (tcpSipProvider == null) { - logger.error("[发送信息失败] 未找到tcp://{}的监听信息", ip); - return; - } - if (message instanceof Request) { - tcpSipProvider.sendRequest((Request)message); - }else if (message instanceof Response) { - tcpSipProvider.sendResponse((Response)message); - } - - } else if ("UDP".equals(transport)) { - SipProviderImpl sipProvider = sipLayer.getUdpSipProvider(ip); - if (sipProvider == null) { - logger.error("[发送信息失败] 未找到udp://{}的监听信息", ip); - return; - } - if (message instanceof Request) { - sipProvider.sendRequest((Request)message); - }else if (message instanceof Response) { - sipProvider.sendResponse((Response)message); - } - } + transmitRequest(ip, message, errorEvent, okEvent, null); } - public CallIdHeader getNewCallIdHeader(String ip, String transport){ + public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent, Long timeout) throws SipException { + ViaHeader viaHeader = (ViaHeader) message.getHeader(ViaHeader.NAME); + String transport = "UDP"; + if (viaHeader == null) { + log.warn("[消息头缺失]: ViaHeader, 使用默认的UDP方式处理数据"); + } else { + transport = viaHeader.getTransport(); + } + if (message.getHeader(UserAgentHeader.NAME) == null) { + try { + message.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + } catch (ParseException e) { + log.error("添加UserAgentHeader失败", e); + } + } + + if (okEvent != null || errorEvent != null) { + CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME); + SipEvent sipEvent = SipEvent.getInstance(callIdHeader.getCallId(), eventResult -> { + sipSubscribe.removeSubscribe(eventResult.callId); + if(okEvent != null) { + okEvent.response(eventResult); + } + }, (eventResult -> { + sipSubscribe.removeSubscribe(eventResult.callId); + if (errorEvent != null) { + errorEvent.response(eventResult); + } + }), timeout == null ? sipConfig.getTimeout() : timeout); + sipSubscribe.addSubscribe(callIdHeader.getCallId(), sipEvent); + } + + if ("TCP".equals(transport)) { + SipProviderImpl tcpSipProvider = sipLayer.getTcpSipProvider(ip); + if (tcpSipProvider == null) { + log.error("[发送信息失败] 未找到tcp://{}的监听信息", ip); + return; + } + if (message instanceof Request) { + tcpSipProvider.sendRequest((Request) message); + } else if (message instanceof Response) { + tcpSipProvider.sendResponse((Response) message); + } + + } else if ("UDP".equals(transport)) { + SipProviderImpl sipProvider = sipLayer.getUdpSipProvider(ip); + if (sipProvider == null) { + log.error("[发送信息失败] 未找到udp://{}的监听信息", ip); + return; + } + if (message instanceof Request) { + sipProvider.sendRequest((Request) message); + } else if (message instanceof Response) { + sipProvider.sendResponse((Response) message); + } + } + } + + public CallIdHeader getNewCallIdHeader(String ip, String transport) { if (ObjectUtils.isEmpty(transport)) { return sipLayer.getUdpSipProvider().getNewCallId(); } @@ -113,7 +119,7 @@ public class SIPSender { if (ObjectUtils.isEmpty(ip)) { sipProvider = transport.equalsIgnoreCase("TCP") ? sipLayer.getTcpSipProvider() : sipLayer.getUdpSipProvider(); - }else { + } else { sipProvider = transport.equalsIgnoreCase("TCP") ? sipLayer.getTcpSipProvider(ip) : sipLayer.getUdpSipProvider(ip); } @@ -124,9 +130,11 @@ public class SIPSender { if (sipProvider != null) { return sipProvider.getNewCallId(); - }else { - logger.warn("[新建CallIdHeader失败], ip={}, transport={}", ip, transport); + } else { + log.warn("[新建CallIdHeader失败], ip={}, transport={}", ip, transport); return null; } } + + } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java index 8d0ed7fb4..61aaac4a0 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java @@ -68,12 +68,8 @@ public class DeferredResultHolder { } public void put(String key, String id, DeferredResult result) { - Map deferredResultMap = map.get(key); - if (deferredResultMap == null) { - deferredResultMap = new ConcurrentHashMap<>(); - map.put(key, deferredResultMap); - } - deferredResultMap.put(id, new DeferredResultEx(result)); + Map deferredResultMap = map.computeIfAbsent(key, k -> new ConcurrentHashMap<>()); + deferredResultMap.put(id, new DeferredResultEx(result)); } public DeferredResultEx get(String key, String id) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java index f4e2d2013..5a22f6df9 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java @@ -1,10 +1,13 @@ package com.genersoft.iot.vmp.gb28181.transmit.callback; -/** +import lombok.Data; + +/** * @description: 请求信息定义 * @author: swwheihei * @date: 2020年5月8日 下午1:09:18 */ +@Data public class RequestMessage { private String id; @@ -12,28 +15,4 @@ public class RequestMessage { private String key; private Object data; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public void setKey(String key) { - this.key = key; - } - - public String getKey() { - return key; - } - - public Object getData() { - return data; - } - - public void setData(Object data) { - this.data = data; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java index fcafbf75a..dca16ba84 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java @@ -100,30 +100,30 @@ public interface ISIPCommander { * @param device 视频设备 * @param channel 预览通道 */ - void playStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel, HookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + void playStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent, Long timeout) throws InvalidArgumentException, SipException, ParseException; /** * 请求回放视频流 * * @param device 视频设备 - * @param channelId 预览通道 + * @param channel 预览通道 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss */ - void playbackStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime, HookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + void playbackStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInf, Device device, DeviceChannel channel, String startTime, String endTime, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent, Long timeout) throws InvalidArgumentException, SipException, ParseException; /** * 请求历史媒体下载 * * @param device 视频设备 - * @param channelId 预览通道 + * @param channel 预览通道 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss * @param downloadSpeed 下载倍速参数 */ - void downloadStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, - String startTime, String endTime, int downloadSpeed, HookSubscribe.Event hookEvent, - SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; + void downloadStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel, + String startTime, String endTime, int downloadSpeed, + SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent, Long timeout) throws InvalidArgumentException, SipException, ParseException; /** @@ -131,7 +131,7 @@ public interface ISIPCommander { */ void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException; - void talkStreamCmd(MediaServer mediaServerItem, SendRtpItem sendRtpItem, Device device, String channelId, String callId, HookSubscribe.Event event, HookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + void talkStreamCmd(MediaServer mediaServerItem, SendRtpInfo sendRtpItem, Device device, DeviceChannel channelId, String callId, HookSubscribe.Event event, HookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent, Long timeout) throws InvalidArgumentException, SipException, ParseException; void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException; @@ -141,22 +141,22 @@ public interface ISIPCommander { /** * 回放暂停 */ - void playPauseCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException; + void playPauseCmd(Device device, DeviceChannel channel, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException; /** * 回放恢复 */ - void playResumeCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException; + void playResumeCmd(Device device, DeviceChannel channel, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException; /** * 回放拖动播放 */ - void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) throws InvalidArgumentException, ParseException, SipException; + void playSeekCmd(Device device, DeviceChannel channel, StreamInfo streamInfo, long seekTime) throws InvalidArgumentException, ParseException, SipException; /** * 回放倍速播放 */ - void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) throws InvalidArgumentException, ParseException, SipException; + void playSpeedCmd(Device device, DeviceChannel channel, StreamInfo streamInfo, Double speed) throws InvalidArgumentException, ParseException, SipException; /** * 回放控制 @@ -164,7 +164,7 @@ public interface ISIPCommander { * @param streamInfo * @param content */ - void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException; + void playbackControlCmd(Device device, DeviceChannel channel, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException; void streamByeCmdForDeviceInvite(Device device, String channelId, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException; @@ -350,7 +350,9 @@ public interface ISIPCommander { void dragZoomCmd(Device device, String channelId, String cmdString) throws InvalidArgumentException, SipException, ParseException; - /** + void playbackControlCmd(Device device, DeviceChannel channel, String stream, String content, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException; + + /** * 向设备发送报警NOTIFY消息, 用于互联结构下,此时将设备当成一个平级平台看待 * @param device 设备 * @param deviceAlarm 报警信息信息 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java index bc999f037..cd8e0b685 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java @@ -22,12 +22,12 @@ public interface ISIPCommanderForPlatform { * @param parentPlatform * @return */ - void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException; + void register(Platform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException; - void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException; + void register(Platform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException; - void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean isRegister) throws SipException, InvalidArgumentException, ParseException; + void register(Platform parentPlatform, SipTransactionInfo sipTransactionInfo, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean isRegister) throws SipException, InvalidArgumentException, ParseException; /** * 向上级平台注销 @@ -35,7 +35,7 @@ public interface ISIPCommanderForPlatform { * @param parentPlatform * @return */ - void unregister(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException; + void unregister(Platform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException; /** @@ -44,7 +44,7 @@ public interface ISIPCommanderForPlatform { * @param parentPlatform * @return callId(作为接受回复的判定) */ - String keepalive(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) + String keepalive(Platform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException; @@ -58,10 +58,10 @@ public interface ISIPCommanderForPlatform { * @param size * @return */ - void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) + void catalogQuery(CommonGBChannel channel, Platform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException; - void catalogQuery(List channels, ParentPlatform parentPlatform, String sn, String fromTag) + void catalogQuery(List channels, Platform parentPlatform, String sn, String fromTag) throws InvalidArgumentException, ParseException, SipException; /** @@ -72,7 +72,7 @@ public interface ISIPCommanderForPlatform { * @param fromTag FROM头的tag信息 * @return */ - void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException; + void deviceInfoResponse(Platform parentPlatform, Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException; /** * 向上级回复DeviceStatus查询信息 @@ -82,7 +82,7 @@ public interface ISIPCommanderForPlatform { * @param fromTag * @return */ - void deviceStatusResponse(ParentPlatform parentPlatform,String channelId, String sn, String fromTag,boolean status) throws SipException, InvalidArgumentException, ParseException; + void deviceStatusResponse(Platform parentPlatform, String channelId, String sn, String fromTag, boolean status) throws SipException, InvalidArgumentException, ParseException; /** * 向上级回复移动位置订阅消息 @@ -92,7 +92,7 @@ public interface ISIPCommanderForPlatform { * @param subscribeInfo 订阅相关的信息 * @return */ - void sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo) + void sendNotifyMobilePosition(Platform parentPlatform, GPSMsgInfo gpsMsgInfo, CommonGBChannel channel, SubscribeInfo subscribeInfo) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException; /** @@ -102,7 +102,7 @@ public interface ISIPCommanderForPlatform { * @param deviceAlarm 报警信息信息 * @return */ - void sendAlarmMessage(ParentPlatform parentPlatform, DeviceAlarm deviceAlarm) throws SipException, InvalidArgumentException, ParseException; + void sendAlarmMessage(Platform parentPlatform, DeviceAlarm deviceAlarm) throws SipException, InvalidArgumentException, ParseException; /** * 回复catalog事件-增加/更新 @@ -110,7 +110,7 @@ public interface ISIPCommanderForPlatform { * @param parentPlatform * @param deviceChannels */ - void sendNotifyForCatalogAddOrUpdate(String type, ParentPlatform parentPlatform, List deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException; + void sendNotifyForCatalogAddOrUpdate(String type, Platform parentPlatform, List deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException; /** * 回复catalog事件-删除 @@ -118,7 +118,7 @@ public interface ISIPCommanderForPlatform { * @param parentPlatform * @param deviceChannels */ - void sendNotifyForCatalogOther(String type, ParentPlatform parentPlatform, List deviceChannels, + void sendNotifyForCatalogOther(String type, Platform parentPlatform, List deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException; @@ -130,7 +130,7 @@ public interface ISIPCommanderForPlatform { * @param fromTag fromTag * @param recordInfo 录像信息 */ - void recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo) + void recordInfo(CommonGBChannel deviceChannel, Platform parentPlatform, String fromTag, RecordInfo recordInfo) throws SipException, InvalidArgumentException, ParseException; /** @@ -140,23 +140,15 @@ public interface ISIPCommanderForPlatform { * @param sendRtpItem * @return */ - void sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException; + void sendMediaStatusNotify(Platform platform, SendRtpInfo sendRtpItem) throws SipException, InvalidArgumentException, ParseException; - /** - * 向发起点播的上级回复bye - * - * @param platform 平台信息 - * @param callId callId - */ - void streamByeCmd(ParentPlatform platform, String callId) throws SipException, InvalidArgumentException, ParseException; + void streamByeCmd(Platform platform, SendRtpInfo sendRtpItem, CommonGBChannel channel) throws SipException, InvalidArgumentException, ParseException; - void streamByeCmd(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException; + void streamByeCmd(Platform platform, CommonGBChannel channel, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException; - void streamByeCmd(ParentPlatform platform, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException; - - void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServer mediaServerItem, + void broadcastInviteCmd(Platform platform, CommonGBChannel channel, MediaServer mediaServerItem, SSRCInfo ssrcInfo, HookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws ParseException, SipException, InvalidArgumentException; - void broadcastResultCmd(ParentPlatform platform, DeviceChannel deviceChannel, String sn, boolean result, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; + void broadcastResultCmd(Platform platform, CommonGBChannel deviceChannel, String sn, boolean result, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java index 543072ac0..628bdcd82 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java @@ -2,10 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd; import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.gb28181.SipLayer; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; -import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; -import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo; +import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.utils.GitUtil; @@ -47,12 +44,12 @@ public class SIPRequestHeaderPlarformProvider { @Autowired private IRedisCatchStorage redisCatchStorage; - public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, long CSeq, String fromTag, String toTag, CallIdHeader callIdHeader, int expires) throws ParseException, InvalidArgumentException, PeerUnavailableException { + public Request createRegisterRequest(@NotNull Platform parentPlatform, long CSeq, String fromTag, String toTag, CallIdHeader callIdHeader, int expires) throws ParseException, InvalidArgumentException, PeerUnavailableException { Request request = null; String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort(); //请求行 SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), - parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort()); + parentPlatform.getServerIp() + ":" + parentPlatform.getServerPort()); //via ArrayList viaHeaders = new ArrayList(); ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), @@ -88,16 +85,16 @@ public class SIPRequestHeaderPlarformProvider { return request; } - public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, String fromTag, String toTag, - WWWAuthenticateHeader www , CallIdHeader callIdHeader, int expires) throws ParseException, PeerUnavailableException, InvalidArgumentException { + public Request createRegisterRequest(@NotNull Platform parentPlatform, String fromTag, String toTag, + WWWAuthenticateHeader www , CallIdHeader callIdHeader, int expires) throws ParseException, PeerUnavailableException, InvalidArgumentException { Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(), fromTag, toTag, callIdHeader, expires); - SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort()); + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIp() + ":" + parentPlatform.getServerPort()); if (www == null) { AuthorizationHeader authorizationHeader = SipFactory.getInstance().createHeaderFactory().createAuthorizationHeader("Digest"); String username = parentPlatform.getUsername(); - if ( username == null || username == "" ) + if ( username == null || username.isEmpty()) { authorizationHeader.setUsername(parentPlatform.getDeviceGBId()); } else { @@ -165,20 +162,20 @@ public class SIPRequestHeaderPlarformProvider { return registerRequest; } - public Request createMessageRequest(ParentPlatform parentPlatform, String content, SendRtpItem sendRtpItem) throws PeerUnavailableException, ParseException, InvalidArgumentException { + public Request createMessageRequest(Platform parentPlatform, String content, SendRtpInfo sendRtpItem) throws PeerUnavailableException, ParseException, InvalidArgumentException { CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId()); callIdHeader.setCallId(sendRtpItem.getCallId()); return createMessageRequest(parentPlatform, content, sendRtpItem.getToTag(), SipUtils.getNewViaTag(), sendRtpItem.getFromTag(), callIdHeader); } - public Request createMessageRequest(ParentPlatform parentPlatform, String content, String fromTag, String viaTag, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException { + public Request createMessageRequest(Platform parentPlatform, String content, String fromTag, String viaTag, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException { return createMessageRequest(parentPlatform, content, fromTag, viaTag, null, callIdHeader); } - public Request createMessageRequest(ParentPlatform parentPlatform, String content, String fromTag, String viaTag, String toTag, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException { + public Request createMessageRequest(Platform parentPlatform, String content, String fromTag, String viaTag, String toTag, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException { Request request = null; - String serverAddress = parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort(); + String serverAddress = parentPlatform.getServerIp()+ ":" + parentPlatform.getServerPort(); // sipuri SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress); // via @@ -214,10 +211,10 @@ public class SIPRequestHeaderPlarformProvider { return request; } - public SIPRequest createNotifyRequest(ParentPlatform parentPlatform, String content, SubscribeInfo subscribeInfo) throws PeerUnavailableException, ParseException, InvalidArgumentException { + public SIPRequest createNotifyRequest(Platform parentPlatform, String content, SubscribeInfo subscribeInfo) throws PeerUnavailableException, ParseException, InvalidArgumentException { SIPRequest request = null; // sipuri - SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort()); + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIp()+ ":" + parentPlatform.getServerPort()); // via ArrayList viaHeaders = new ArrayList<>(); ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), parentPlatform.getDevicePort(), @@ -269,7 +266,7 @@ public class SIPRequestHeaderPlarformProvider { return request; } - public SIPRequest createByeRequest(ParentPlatform platform, SendRtpItem sendRtpItem) throws PeerUnavailableException, ParseException, InvalidArgumentException { + public SIPRequest createByeRequest(Platform platform, SendRtpInfo sendRtpItem, CommonGBChannel channel) throws PeerUnavailableException, ParseException, InvalidArgumentException { if (sendRtpItem == null ) { return null; @@ -277,7 +274,7 @@ public class SIPRequestHeaderPlarformProvider { SIPRequest request = null; // sipuri - SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+ ":" + platform.getServerPort()); + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIp()+ ":" + platform.getServerPort()); // via ArrayList viaHeaders = new ArrayList<>(); ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(platform.getDeviceIp(), platform.getDevicePort(), @@ -285,7 +282,7 @@ public class SIPRequestHeaderPlarformProvider { viaHeader.setRPort(); viaHeaders.add(viaHeader); // from - SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sendRtpItem.getChannelId(), + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channel.getGbDeviceId(), platform.getDeviceIp() + ":" + platform.getDevicePort()); Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, sendRtpItem.getToTag()); @@ -315,10 +312,10 @@ public class SIPRequestHeaderPlarformProvider { return request; } - public Request createInviteRequest(ParentPlatform platform, String channelId, String content, String viaTag, String fromTag, String ssrc, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException { + public Request createInviteRequest(Platform platform, String channelId, String content, String viaTag, String fromTag, String ssrc, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException { Request request = null; //请求行 - String platformHostAddress = platform.getServerIP() + ":" + platform.getServerPort(); + String platformHostAddress = platform.getServerIp() + ":" + platform.getServerPort(); String localHostAddress = sipLayer.getLocalIp(platform.getDeviceIp())+":"+ platform.getDevicePort(); SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, platformHostAddress); //via @@ -355,7 +352,7 @@ public class SIPRequestHeaderPlarformProvider { return request; } - public Request createByteRequest(ParentPlatform platform, String channelId, SipTransactionInfo transactionInfo) throws PeerUnavailableException, ParseException, InvalidArgumentException { + public Request createByteRequest(Platform platform, String channelId, SipTransactionInfo transactionInfo) throws PeerUnavailableException, ParseException, InvalidArgumentException { String deviceHostAddress = platform.getDeviceIp() + ":" + platform.getDevicePort(); Request request = null; SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, deviceHostAddress); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java index 8787ed1fd..7bfd91bfa 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java @@ -4,7 +4,6 @@ import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.gb28181.SipLayer; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.utils.GitUtil; @@ -44,8 +43,6 @@ public class SIPRequestHeaderProvider { @Autowired private IRedisCatchStorage redisCatchStorage; - @Autowired - private VideoStreamSessionManager streamSession; public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { Request request = null; @@ -167,11 +164,12 @@ public class SIPRequestHeaderProvider { // SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); // via ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag()); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), transactionInfo.getViaBranch()); // viaHeader.setRPort(); viaHeaders.add(viaHeader); //from - SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain()); +// SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain()); + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp()) + ":" + sipConfig.getPort()); Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag()); //to diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index e27343ce5..8cb38a103 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java @@ -8,23 +8,22 @@ import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.SipLayer; 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.session.SipInviteSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider; import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.event.hook.Hook; import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; import com.genersoft.iot.vmp.media.event.hook.HookType; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.service.bean.SSRCInfo; import com.genersoft.iot.vmp.utils.DateUtil; import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; @@ -37,7 +36,6 @@ import javax.sip.SipFactory; import javax.sip.header.CallIdHeader; import javax.sip.message.Request; import java.text.ParseException; -import java.util.List; /** * @description:设备能力接口,用于定义设备的控制、查询能力 @@ -46,10 +44,9 @@ import java.util.List; */ @Component @DependsOn("sipLayer") +@Slf4j public class SIPCommander implements ISIPCommander { - private final Logger logger = LoggerFactory.getLogger(SIPCommander.class); - @Autowired private SipConfig sipConfig; @@ -63,7 +60,7 @@ public class SIPCommander implements ISIPCommander { private SIPRequestHeaderProvider headerProvider; @Autowired - private VideoStreamSessionManager streamSession; + private SipInviteSessionManager sessionManager; @Autowired private UserSetting userSetting; @@ -261,26 +258,16 @@ public class SIPCommander implements ISIPCommander { * * @param device 视频设备 * @param channel 预览通道 - * @param event hook订阅 * @param errorEvent sip错误订阅 */ @Override public void playStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel, - HookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent, Long timeout) throws InvalidArgumentException, SipException, ParseException { String stream = ssrcInfo.getStream(); if (device == null) { return; } - - logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort()); - Hook rtpHook = Hook.getInstance(HookType.on_media_arrival, "rtp", stream, mediaServerItem.getId()); - subscribe.addSubscribe(rtpHook, (hookData) -> { - if (event != null) { - event.response(hookData); - subscribe.removeSubscribe(rtpHook); - } - }); String sdpIp; if (!ObjectUtils.isEmpty(device.getSdpIp())) { sdpIp = device.getSdpIp(); @@ -289,7 +276,7 @@ public class SIPCommander implements ISIPCommander { } StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); - content.append("o=" + channel.getChannelId() + " 0 0 IN IP4 " + sdpIp + "\r\n"); + content.append("o=" + device.getDeviceId() + " 0 0 IN IP4 " + sdpIp + "\r\n"); content.append("s=Play\r\n"); content.append("c=IN IP4 " + sdpIp + "\r\n"); content.append("t=0 0\r\n"); @@ -348,38 +335,37 @@ public class SIPCommander implements ISIPCommander { // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率 // content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 未发现支持此特性的设备 - - - Request request = headerProvider.createInviteRequest(device, channel.getChannelId(), content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + Request request = headerProvider.createInviteRequest(device, channel.getDeviceId(), content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, (e -> { - streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getStream()); mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); errorEvent.response(e); }), e -> { ResponseEvent responseEvent = (ResponseEvent) e.event; SIPResponse response = (SIPResponse) responseEvent.getResponse(); String callId = response.getCallIdHeader().getCallId(); - streamSession.put(device.getDeviceId(), channel.getChannelId(), callId, stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, + SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), channel.getId(), callId, stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY); + sessionManager.put(ssrcTransaction); okEvent.response(e); - }); + }, timeout); } /** * 请求回放视频流 * * @param device 视频设备 - * @param channelId 预览通道 + * @param channel 预览通道 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss */ @Override - public void playbackStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, - String startTime, String endTime, HookSubscribe.Event hookEvent, - SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + public void playbackStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel, + String startTime, String endTime, + SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent, Long timeout) throws InvalidArgumentException, SipException, ParseException { - logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort()); + log.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort()); String sdpIp; if (!ObjectUtils.isEmpty(device.getSdpIp())) { sdpIp = device.getSdpIp(); @@ -388,9 +374,9 @@ public class SIPCommander implements ISIPCommander { } StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); - content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n"); + content.append("o=" + device.getDeviceId() + " 0 0 IN IP4 " + sdpIp + "\r\n"); content.append("s=Playback\r\n"); - content.append("u=" + channelId + ":0\r\n"); + content.append("u=" + channel.getDeviceId() + ":0\r\n"); content.append("c=IN IP4 " + sdpIp + "\r\n"); content.append("t=" + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime) + " " + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) + "\r\n"); @@ -448,40 +434,27 @@ public class SIPCommander implements ISIPCommander { //ssrc content.append("y=" + ssrcInfo.getSsrc() + "\r\n"); - Hook rtpHook = Hook.getInstance(HookType.on_media_arrival, "rtp", ssrcInfo.getStream(), mediaServerItem.getId()); - // 添加订阅 - subscribe.addSubscribe(rtpHook, (hookData) -> { - if (hookEvent != null) { - hookEvent.response(hookData); - } - subscribe.removeSubscribe(rtpHook); - }); - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc()); + Request request = headerProvider.createPlaybackInviteRequest(device, channel.getDeviceId(), content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc()); sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> { ResponseEvent responseEvent = (ResponseEvent) event.event; SIPResponse response = (SIPResponse) responseEvent.getResponse(); - streamSession.put(device.getDeviceId(), channelId,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAYBACK); + SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), + channel.getId(), sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAYBACK); + sessionManager.put(ssrcTransaction); okEvent.response(event); - }); + }, timeout); } /** * 请求历史媒体下载 - * - * @param device 视频设备 - * @param channelId 预览通道 - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss - * @param downloadSpeed 下载倍速参数 */ @Override - public void downloadStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + public void downloadStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel, String startTime, String endTime, int downloadSpeed, - HookSubscribe.Event hookEvent, - SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException { + SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent, Long timeout) throws InvalidArgumentException, SipException, ParseException { - logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort()); + log.info("[发送-请求历史媒体下载-命令] 流ID: {},节点为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort()); String sdpIp; if (!ObjectUtils.isEmpty(device.getSdpIp())) { sdpIp = device.getSdpIp(); @@ -490,9 +463,9 @@ public class SIPCommander implements ISIPCommander { } StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); - content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n"); + content.append("o=" + device.getDeviceId() + " 0 0 IN IP4 " + sdpIp + "\r\n"); content.append("s=Download\r\n"); - content.append("u=" + channelId + ":0\r\n"); + content.append("u=" + channel.getDeviceId() + ":0\r\n"); content.append("c=IN IP4 " + sdpIp + "\r\n"); content.append("t=" + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime) + " " + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) + "\r\n"); @@ -548,43 +521,26 @@ public class SIPCommander implements ISIPCommander { content.append("a=downloadspeed:" + downloadSpeed + "\r\n"); content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc - logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc()); - Hook rtpHook = Hook.getInstance(HookType.on_media_arrival, "rtp", ssrcInfo.getStream(), mediaServerItem.getId()); + log.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc()); // 添加订阅 CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport()); - String callId= newCallIdHeader.getCallId(); - subscribe.addSubscribe(rtpHook, (hookData) -> { - logger.debug("sipc 添加订阅===callId {}",callId); - hookEvent.response(hookData); - subscribe.removeSubscribe(rtpHook); - // 添加流注销的订阅,注销了后向设备发送bye - Hook departureHook = Hook.getInstance(HookType.on_media_departure, "rtp", ssrcInfo.getStream(), mediaServerItem.getId()); - subscribe.addSubscribe(departureHook, - (departureHookData) -> { - logger.info("[录像]下载结束, 发送BYE"); - try { - streamByeCmd(device, channelId, ssrcInfo.getStream(), callId); - } catch (InvalidArgumentException | ParseException | SipException | - SsrcTransactionNotFoundException e) { - logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage()); - } - }); - }); - - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,newCallIdHeader, ssrcInfo.getSsrc()); + Request request = headerProvider.createPlaybackInviteRequest(device, channel.getDeviceId(), content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,newCallIdHeader, ssrcInfo.getSsrc()); sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> { ResponseEvent responseEvent = (ResponseEvent) event.event; SIPResponse response = (SIPResponse) responseEvent.getResponse(); String contentString =new String(response.getRawContent()); String ssrc = SipUtils.getSsrcFromSdp(contentString); - streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, InviteSessionType.DOWNLOAD); + SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), channel.getId(), response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, InviteSessionType.DOWNLOAD); + sessionManager.put(ssrcTransaction); okEvent.response(event); - }); + }, timeout); } @Override - public void talkStreamCmd(MediaServer mediaServerItem, SendRtpItem sendRtpItem, Device device, String channelId, String callId, HookSubscribe.Event event, HookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + public void talkStreamCmd(MediaServer mediaServerItem, SendRtpInfo sendRtpItem, Device device, DeviceChannel channel, + String callId, HookSubscribe.Event event, HookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, + SipSubscribe.Event errorEvent, Long timeout) throws InvalidArgumentException, SipException, ParseException { String stream = sendRtpItem.getStream(); @@ -593,11 +549,11 @@ public class SIPCommander implements ISIPCommander { } if (!mediaServerItem.isRtpEnable()) { // 单端口暂不支持语音喊话 - logger.info("[语音喊话] 单端口暂不支持此操作"); + log.info("[语音喊话] 单端口暂不支持此操作"); return; } - logger.info("[语音喊话] {} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), sendRtpItem.getPort()); + log.info("[语音喊话] {} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), sendRtpItem.getPort()); Hook hook = Hook.getInstance(HookType.on_media_arrival, "rtp", stream, mediaServerItem.getId()); subscribe.addSubscribe(hook, (hookData) -> { if (event != null) { @@ -617,7 +573,7 @@ public class SIPCommander implements ISIPCommander { // StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); - content.append("o=" + channelId + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); + content.append("o=" + device.getDeviceId() + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); content.append("s=Talk\r\n"); content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); content.append("t=0 0\r\n"); @@ -632,19 +588,20 @@ public class SIPCommander implements ISIPCommander { // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率 content.append("f=v/////a/1/8/1" + "\r\n"); - Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), + Request request = headerProvider.createInviteRequest(device, channel.getDeviceId(), content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, sendRtpItem.getSsrc(), callIdHeader); sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, (e -> { - streamSession.remove(device.getDeviceId(), channelId, sendRtpItem.getStream()); + sessionManager.removeByStream(sendRtpItem.getStream()); mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); errorEvent.response(e); }), e -> { // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值 ResponseEvent responseEvent = (ResponseEvent) e.event; SIPResponse response = (SIPResponse) responseEvent.getResponse(); - streamSession.put(device.getDeviceId(), channelId, "talk", stream, sendRtpItem.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.TALK); + SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), channel.getId(), "talk", stream, sendRtpItem.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.TALK); + sessionManager.put(ssrcTransaction); okEvent.response(e); - }); + }, timeout); } /** @@ -661,24 +618,25 @@ public class SIPCommander implements ISIPCommander { @Override public void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException { if (device == null) { - logger.warn("[发送BYE] device为null"); + log.warn("[发送BYE] device为null"); return; } - List ssrcTransactionList = streamSession.getSsrcTransactionForAll(device.getDeviceId(), channelId, callId, stream); - if (ssrcTransactionList == null || ssrcTransactionList.isEmpty()) { - logger.info("[发送BYE] 未找到事务信息,设备: device: {}, channel: {}", device.getDeviceId(), channelId); + SsrcTransaction ssrcTransaction = null; + if (callId != null) { + ssrcTransaction = sessionManager.getSsrcTransactionByCallId(callId); + }else if (stream != null) { + ssrcTransaction = sessionManager.getSsrcTransactionByStream(stream); + } + + if (ssrcTransaction == null) { + log.info("[发送BYE] 未找到事务信息,设备: device: {}, channel: {}", device.getDeviceId(), channelId); throw new SsrcTransactionNotFoundException(device.getDeviceId(), channelId, callId, stream); } - for (SsrcTransaction ssrcTransaction : ssrcTransactionList) { - logger.info("[发送BYE] 设备: device: {}, channel: {}, callId: {}", device.getDeviceId(), channelId, ssrcTransaction.getCallId()); - mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); - - mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); - streamSession.removeByCallId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getCallId()); - Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo()); - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent); - } + log.info("[发送BYE] 设备: device: {}, channel: {}, callId: {}", device.getDeviceId(), channelId, ssrcTransaction.getCallId()); + sessionManager.removeByCallId(ssrcTransaction.getCallId()); + Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo()); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent); } @Override @@ -1339,7 +1297,7 @@ public class SIPCommander implements ISIPCommander { dragXml.append("\r\n"); Request request = headerProvider.createMessageRequest(device, dragXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); - logger.debug("拉框信令: " + request.toString()); + log.debug("拉框信令: " + request.toString()); sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request); } @@ -1349,13 +1307,13 @@ public class SIPCommander implements ISIPCommander { * 回放暂停 */ @Override - public void playPauseCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException { + public void playPauseCmd(Device device, DeviceChannel channel, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException { StringBuffer content = new StringBuffer(200); content.append("PAUSE RTSP/1.0\r\n"); content.append("CSeq: " + getInfoCseq() + "\r\n"); content.append("PauseTime: now\r\n"); - playbackControlCmd(device, streamInfo, content.toString(), null, null); + playbackControlCmd(device, channel, streamInfo, content.toString(), null, null); } @@ -1363,39 +1321,39 @@ public class SIPCommander implements ISIPCommander { * 回放恢复 */ @Override - public void playResumeCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException { + public void playResumeCmd(Device device, DeviceChannel channel, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException { StringBuffer content = new StringBuffer(200); content.append("PLAY RTSP/1.0\r\n"); content.append("CSeq: " + getInfoCseq() + "\r\n"); content.append("Range: npt=now-\r\n"); - playbackControlCmd(device, streamInfo, content.toString(), null, null); + playbackControlCmd(device, channel, streamInfo, content.toString(), null, null); } /** * 回放拖动播放 */ @Override - public void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) throws InvalidArgumentException, ParseException, SipException { + public void playSeekCmd(Device device, DeviceChannel channel, StreamInfo streamInfo, long seekTime) throws InvalidArgumentException, ParseException, SipException { StringBuffer content = new StringBuffer(200); content.append("PLAY RTSP/1.0\r\n"); content.append("CSeq: " + getInfoCseq() + "\r\n"); content.append("Range: npt=" + Math.abs(seekTime) + "-\r\n"); - playbackControlCmd(device, streamInfo, content.toString(), null, null); + playbackControlCmd(device, channel, streamInfo, content.toString(), null, null); } /** * 回放倍速播放 */ @Override - public void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) throws InvalidArgumentException, ParseException, SipException { + public void playSpeedCmd(Device device, DeviceChannel channel, StreamInfo streamInfo, Double speed) throws InvalidArgumentException, ParseException, SipException { StringBuffer content = new StringBuffer(200); content.append("PLAY RTSP/1.0\r\n"); content.append("CSeq: " + getInfoCseq() + "\r\n"); content.append("Scale: " + String.format("%.6f", speed) + "\r\n"); - playbackControlCmd(device, streamInfo, content.toString(), null, null); + playbackControlCmd(device, channel, streamInfo, content.toString(), null, null); } private int getInfoCseq() { @@ -1403,17 +1361,23 @@ public class SIPCommander implements ISIPCommander { } @Override - public void playbackControlCmd(Device device, StreamInfo streamInfo, String content, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException { + public void playbackControlCmd(Device device, DeviceChannel channel, StreamInfo streamInfo, String content, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException { - SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), streamInfo.getChannelId(), null, streamInfo.getStream()); + playbackControlCmd(device, channel, streamInfo.getStream(), content, errorEvent, okEvent); + } + + @Override + public void playbackControlCmd(Device device, DeviceChannel channel, String stream, String content, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException { + + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(stream); if (ssrcTransaction == null) { - logger.info("[回放控制]未找到视频流信息,设备:{}, 流ID: {}", device.getDeviceId(), streamInfo.getStream()); + log.info("[回放控制]未找到视频流信息,设备:{}, 流ID: {}", device.getDeviceId(), stream); return; } - SIPRequest request = headerProvider.createInfoRequest(device, streamInfo.getChannelId(), content.toString(), ssrcTransaction.getSipTransactionInfo()); + SIPRequest request = headerProvider.createInfoRequest(device, channel.getDeviceId(), content, ssrcTransaction.getSipTransactionInfo()); if (request == null) { - logger.info("[回放控制]构建Request信息失败,设备:{}, 流ID: {}", device.getDeviceId(), streamInfo.getStream()); + log.info("[回放控制]构建Request信息失败,设备:{}, 流ID: {}", device.getDeviceId(), stream); return; } @@ -1425,7 +1389,7 @@ public class SIPCommander implements ISIPCommander { if (device == null) { return; } - logger.info("[发送报警通知]设备: {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(), + log.info("[发送报警通知]设备: {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(), deviceAlarm.getLongitude(), deviceAlarm.getLatitude()); String characterSet = device.getCharset(); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderForPlatform.java similarity index 54% rename from src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java rename to src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderForPlatform.java index 6a6bfcf5c..87821ab08 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderForPlatform.java @@ -8,7 +8,7 @@ import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.SipLayer; 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.session.SipInviteSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderPlarformProvider; @@ -27,8 +27,7 @@ import com.genersoft.iot.vmp.utils.GitUtil; import gov.nist.javax.sip.message.MessageFactoryImpl; import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.DependsOn; import org.springframework.lang.Nullable; @@ -46,11 +45,10 @@ import java.text.ParseException; import java.util.ArrayList; import java.util.List; +@Slf4j @Component @DependsOn("sipLayer") -public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { - - private final Logger logger = LoggerFactory.getLogger(SIPCommanderFroPlatform.class); +public class SIPCommanderForPlatform implements ISIPCommanderForPlatform { @Autowired private SIPRequestHeaderPlarformProvider headerProviderPlatformProvider; @@ -78,7 +76,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @Autowired - private VideoStreamSessionManager streamSession; + private SipInviteSessionManager sessionManager; @Autowired private DynamicTask dynamicTask; @@ -87,24 +85,24 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { private GitUtil gitUtil; @Override - public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException { + public void register(Platform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException { register(parentPlatform, null, null, errorEvent, okEvent, true); } @Override - public void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException { + public void register(Platform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException { register(parentPlatform, sipTransactionInfo, null, errorEvent, okEvent, true); } @Override - public void unregister(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException { + public void unregister(Platform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException { register(parentPlatform, sipTransactionInfo, null, errorEvent, okEvent, false); } @Override - public void register(ParentPlatform parentPlatform, @Nullable SipTransactionInfo sipTransactionInfo, @Nullable WWWAuthenticateHeader www, - SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean isRegister) throws SipException, InvalidArgumentException, ParseException { + public void register(Platform parentPlatform, @Nullable SipTransactionInfo sipTransactionInfo, @Nullable WWWAuthenticateHeader www, + SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean isRegister) throws SipException, InvalidArgumentException, ParseException { Request request; CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); @@ -129,28 +127,23 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { // 将 callid 写入缓存, 等注册成功可以更新状态 String callIdFromHeader = callIdHeader.getCallId(); redisCatchStorage.updatePlatformRegisterInfo(callIdFromHeader, PlatformRegisterInfo.getInstance(parentPlatform.getServerGBId(), isRegister)); - - sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (event)->{ - if (event != null) { - logger.info("向上级平台 [ {} ] 注册发生错误: {} ", - parentPlatform.getServerGBId(), - event.msg); - } - redisCatchStorage.delPlatformRegisterInfo(callIdFromHeader); - if (errorEvent != null ) { - errorEvent.response(event); - } - }); - }else { request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, fromTag, toTag, www, callIdHeader, isRegister? parentPlatform.getExpires() : 0); } - sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, okEvent); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, (event)->{ + if (event != null) { + log.info("[国标级联]:{}, 注册失败: {} ", parentPlatform.getServerGBId(), event.msg); + } + redisCatchStorage.delPlatformRegisterInfo(callIdHeader.getCallId()); + if (errorEvent != null ) { + errorEvent.response(event); + } + }, okEvent, 5L); } @Override - public String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException { + public String keepalive(Platform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException { String characterSet = parentPlatform.getCharacterSet(); StringBuffer keepaliveXml = new StringBuffer(200); keepaliveXml.append(" channels = new ArrayList<>(); + List channels = new ArrayList<>(); if (channel != null) { channels.add(channel); } @@ -200,177 +194,25 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { } @Override - public void catalogQuery(List channels, ParentPlatform parentPlatform, String sn, String fromTag) throws InvalidArgumentException, ParseException, SipException { + public void catalogQuery(List channels, Platform parentPlatform, String sn, String fromTag) throws InvalidArgumentException, ParseException, SipException { if ( parentPlatform ==null) { return ; } sendCatalogResponse(channels, parentPlatform, sn, fromTag, 0, true); } - private String getCatalogXml(List channels, String sn, ParentPlatform parentPlatform, int size) { - String characterSet = parentPlatform.getCharacterSet(); + private String getCatalogXml(List channels, String sn, Platform platform, int size) { + String characterSet = platform.getCharacterSet(); StringBuffer catalogXml = new StringBuffer(600); catalogXml.append("\r\n") .append("\r\n") .append("Catalog\r\n") .append("" +sn + "\r\n") - .append("" + parentPlatform.getDeviceGBId() + "\r\n") + .append("" + platform.getDeviceGBId() + "\r\n") .append("" + size + "\r\n") .append("\r\n"); - if (channels.size() > 0) { - for (DeviceChannel channel : channels) { - if (parentPlatform.getServerGBId().equals(channel.getParentId())) { - channel.setParentId(parentPlatform.getDeviceGBId()); - } - catalogXml.append("\r\n"); - // 行政区划分组只需要这两项就可以 - catalogXml.append("" + channel.getChannelId() + "\r\n"); - catalogXml.append("" + channel.getName() + "\r\n"); - if (channel.getChannelId().length() <= 8) { - catalogXml.append("\r\n"); - continue; - }else { - if (channel.getChannelId().length() != 20) { - catalogXml.append("\r\n"); - logger.warn("[编号长度异常] {} 长度错误,请使用20位长度的国标编号,当前长度:{}", channel.getChannelId(), channel.getChannelId().length()); - catalogXml.append("\r\n"); - continue; - } - switch (Integer.parseInt(channel.getChannelId().substring(10, 13))){ - case 200: -// catalogXml.append("三永华通\r\n"); -// GitUtil gitUtil = SpringBeanFactory.getBean("gitUtil"); -// String model = (gitUtil == null || gitUtil.getBuildVersion() == null)?"1.0": gitUtil.getBuildVersion(); -// catalogXml.append("" + model + "\r\n"); -// catalogXml.append("三永华通\r\n"); - if (channel.getCivilCode() != null) { - catalogXml.append(""+channel.getCivilCode()+"\r\n"); - }else { - catalogXml.append("\r\n"); - } - - catalogXml.append("1\r\n"); - catalogXml.append("0\r\n"); - break; - case 215: - if (!ObjectUtils.isEmpty(channel.getParentId())) { - catalogXml.append("" + channel.getParentId() + "\r\n"); - } - - break; - case 216: - if (!ObjectUtils.isEmpty(channel.getParentId())) { - catalogXml.append("" + channel.getParentId() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - if (!ObjectUtils.isEmpty(channel.getBusinessGroupId())) { - catalogXml.append("" + channel.getBusinessGroupId() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - break; - default: - // 通道项 - if (channel.getManufacture() != null) { - catalogXml.append("" + channel.getManufacture() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - if (channel.getSecrecy() != null) { - catalogXml.append("" + channel.getSecrecy() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - catalogXml.append("" + channel.getRegisterWay() + "\r\n"); - if (channel.getModel() != null) { - catalogXml.append("" + channel.getModel() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - if (channel.getOwner() != null) { - catalogXml.append("" + channel.getOwner()+ "\r\n"); - }else { - catalogXml.append("\r\n"); - } - if (channel.getCivilCode() != null) { - catalogXml.append("" + channel.getCivilCode() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - if (channel.getAddress() == null) { - catalogXml.append("
\r\n"); - }else { - catalogXml.append("
" + channel.getAddress() + "
\r\n"); - } - if (!ObjectUtils.isEmpty(channel.getParentId())) { - catalogXml.append("" + channel.getParentId() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - if (!ObjectUtils.isEmpty(channel.getBlock())) { - catalogXml.append("" + channel.getBlock() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - if (!ObjectUtils.isEmpty(channel.getSafetyWay())) { - catalogXml.append("" + channel.getSafetyWay() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - if (!ObjectUtils.isEmpty(channel.getCertNum())) { - catalogXml.append("" + channel.getCertNum() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - if (!ObjectUtils.isEmpty(channel.getCertifiable())) { - catalogXml.append("" + channel.getCertifiable() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - if (!ObjectUtils.isEmpty(channel.getErrCode())) { - catalogXml.append("" + channel.getErrCode() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - if (!ObjectUtils.isEmpty(channel.getEndTime())) { - catalogXml.append("" + channel.getEndTime() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - if (!ObjectUtils.isEmpty(channel.getSecrecy())) { - catalogXml.append("" + channel.getSecrecy() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - if (!ObjectUtils.isEmpty(channel.getIpAddress())) { - catalogXml.append("" + channel.getIpAddress() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - catalogXml.append("" + channel.getPort() + "\r\n"); - if (!ObjectUtils.isEmpty(channel.getPassword())) { - catalogXml.append("" + channel.getPassword() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - if (!ObjectUtils.isEmpty(channel.getPtzType())) { - catalogXml.append("" + channel.getPtzType() + "\r\n"); - }else { - catalogXml.append("\r\n"); - } - catalogXml.append("" + (channel.isStatus() ?"ON":"OFF") + "\r\n"); - - catalogXml.append("" + - (channel.getLongitudeWgs84() != 0? channel.getLongitudeWgs84():channel.getLongitude()) - + "\r\n"); - catalogXml.append("" + - (channel.getLatitudeWgs84() != 0? channel.getLatitudeWgs84():channel.getLatitude()) - + "\r\n"); - break; - - } - catalogXml.append("\r\n"); - } + if (!channels.isEmpty()) { + for (CommonGBChannel channel : channels) { + catalogXml.append(channel.encode(platform.getDeviceGBId())); } } @@ -379,11 +221,11 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { return catalogXml.toString(); } - private void sendCatalogResponse(List channels, ParentPlatform parentPlatform, String sn, String fromTag, int index, boolean sendAfterResponse) throws SipException, InvalidArgumentException, ParseException { + private void sendCatalogResponse(List channels, Platform parentPlatform, String sn, String fromTag, int index, boolean sendAfterResponse) throws SipException, InvalidArgumentException, ParseException { if (index >= channels.size()) { return; } - List deviceChannels; + List deviceChannels; if (index + parentPlatform.getCatalogGroup() < channels.size()) { deviceChannels = channels.subList(index, index + parentPlatform.getCatalogGroup()); }else { @@ -399,22 +241,23 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { String callId = request.getCallIdHeader().getCallId(); - logger.info("[命令发送] 国标级联{} 目录查询回复: 共{}条,已发送{}条", parentPlatform.getServerGBId(), + log.info("[命令发送] 国标级联{} 目录查询回复: 共{}条,已发送{}条", parentPlatform.getServerGBId(), channels.size(), Math.min(index + parentPlatform.getCatalogGroup(), channels.size())); - logger.debug(catalogXml); + log.debug(catalogXml); if (sendAfterResponse) { // 默认按照收到200回复后发送下一条, 如果超时收不到回复,就以30毫秒的间隔直接发送。 - dynamicTask.startDelay(timeoutTaskKey, ()->{ - sipSubscribe.removeOkSubscribe(callId); - int indexNext = index + parentPlatform.getCatalogGroup(); - try { - sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage()); - } - }, 3000); sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, eventResult -> { - logger.error("[目录推送失败] 国标级联 platform : {}, code: {}, msg: {}, 停止发送", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg); + if (eventResult.type.equals(SipSubscribe.EventResultType.timeout)) { + // 消息发送超时, 以30毫秒的间隔直接发送 + int indexNext = index + parentPlatform.getCatalogGroup(); + try { + sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage()); + } + return; + } + log.error("[目录推送失败] 国标级联 platform : {}, code: {}, msg: {}, 停止发送", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg); dynamicTask.stop(timeoutTaskKey); }, eventResult -> { dynamicTask.stop(timeoutTaskKey); @@ -422,12 +265,12 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { try { sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, true); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage()); } }); }else { sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, eventResult -> { - logger.error("[目录推送失败] 国标级联 platform : {}, code: {}, msg: {}, 停止发送", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg); + log.error("[目录推送失败] 国标级联 platform : {}, code: {}, msg: {}, 停止发送", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg); dynamicTask.stop(timeoutTaskKey); }, null); dynamicTask.startDelay(timeoutTaskKey, ()->{ @@ -435,7 +278,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { try { sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage()); } }, 30); } @@ -449,7 +292,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { * @return */ @Override - public void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException { + public void deviceInfoResponse(Platform parentPlatform, Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException { if (parentPlatform == null) { return; } @@ -487,7 +330,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { * @return */ @Override - public void deviceStatusResponse(ParentPlatform parentPlatform,String channelId, String sn, String fromTag,boolean status) throws SipException, InvalidArgumentException, ParseException { + public void deviceStatusResponse(Platform parentPlatform, String channelId, String sn, String fromTag, boolean status) throws SipException, InvalidArgumentException, ParseException { if (parentPlatform == null) { return ; } @@ -511,11 +354,13 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { } @Override - public void sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException { + public void sendNotifyMobilePosition(Platform parentPlatform, GPSMsgInfo gpsMsgInfo, CommonGBChannel channel, SubscribeInfo subscribeInfo) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException { if (parentPlatform == null) { return; } - logger.info("[发送 移动位置订阅] {}/{}->{},{}", parentPlatform.getServerGBId(), gpsMsgInfo.getId(), gpsMsgInfo.getLng(), gpsMsgInfo.getLat()); + if (log.isDebugEnabled()) { + log.debug("[发送 移动位置订阅] {}/{}->{},{}", parentPlatform.getServerGBId(), gpsMsgInfo.getId(), gpsMsgInfo.getLng(), gpsMsgInfo.getLat()); + } String characterSet = parentPlatform.getCharacterSet(); StringBuffer deviceStatusXml = new StringBuffer(600); @@ -523,7 +368,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { .append("\r\n") .append("MobilePosition\r\n") .append("" + (int)((Math.random()*9+1)*100000) + "\r\n") - .append("" + gpsMsgInfo.getId() + "\r\n") + .append("" + channel.getGbDeviceId() + "\r\n") .append("\r\n") .append("" + gpsMsgInfo.getLng() + "\r\n") .append("" + gpsMsgInfo.getLat() + "\r\n") @@ -533,17 +378,17 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { .append("\r\n"); sendNotify(parentPlatform, deviceStatusXml.toString(), subscribeInfo, eventResult -> { - logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg); + log.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg); }, null); } @Override - public void sendAlarmMessage(ParentPlatform parentPlatform, DeviceAlarm deviceAlarm) throws SipException, InvalidArgumentException, ParseException { + public void sendAlarmMessage(Platform parentPlatform, DeviceAlarm deviceAlarm) throws SipException, InvalidArgumentException, ParseException { if (parentPlatform == null) { return; } - logger.info("[发送报警通知]平台: {}/{}->{},{}: {}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(), + log.info("[发送报警通知]平台: {}/{}->{},{}: {}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(), deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSON.toJSONString(deviceAlarm)); String characterSet = parentPlatform.getCharacterSet(); StringBuffer deviceStatusXml = new StringBuffer(600); @@ -571,7 +416,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { } @Override - public void sendNotifyForCatalogAddOrUpdate(String type, ParentPlatform parentPlatform, List deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException { + public void sendNotifyForCatalogAddOrUpdate(String type, Platform parentPlatform, List deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException { if (parentPlatform == null || deviceChannels == null || deviceChannels.isEmpty() || subscribeInfo == null) { return; } @@ -581,7 +426,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { if (index >= deviceChannels.size()) { return; } - List channels; + List channels; if (index + parentPlatform.getCatalogGroup() < deviceChannels.size()) { channels = deviceChannels.subList(index, index + parentPlatform.getCatalogGroup()); }else { @@ -590,23 +435,23 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { Integer finalIndex = index; String catalogXmlContent = getCatalogXmlContentForCatalogAddOrUpdate(parentPlatform, channels, deviceChannels.size(), type, subscribeInfo); - System.out.println(catalogXmlContent); - logger.info("[发送NOTIFY通知]类型: {},发送数量: {}", type, channels.size()); + log.info("[发送NOTIFY通知]类型: {},发送数量: {}", type, channels.size()); sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> { - logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg); + log.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg); + log.error(catalogXmlContent); }, (eventResult -> { try { sendNotifyForCatalogAddOrUpdate(type, parentPlatform, deviceChannels, subscribeInfo, finalIndex + parentPlatform.getCatalogGroup()); } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | IllegalAccessException e) { - logger.error("[命令发送失败] 国标级联 NOTIFY通知: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 NOTIFY通知: {}", e.getMessage()); } })); } - private void sendNotify(ParentPlatform parentPlatform, String catalogXmlContent, - SubscribeInfo subscribeInfo, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent ) + private void sendNotify(Platform parentPlatform, String catalogXmlContent, + SubscribeInfo subscribeInfo, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent ) throws SipException, ParseException, InvalidArgumentException { MessageFactoryImpl messageFactory = (MessageFactoryImpl) SipFactory.getInstance().createMessageFactory(); String characterSet = parentPlatform.getCharacterSet(); @@ -618,55 +463,19 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { sipSender.transmitRequest(parentPlatform.getDeviceIp(), notifyRequest, errorEvent, okEvent); } - private String getCatalogXmlContentForCatalogAddOrUpdate(ParentPlatform parentPlatform, List channels, int sumNum, String type, SubscribeInfo subscribeInfo) { + private String getCatalogXmlContentForCatalogAddOrUpdate(Platform platform, List channels, int sumNum, String type, SubscribeInfo subscribeInfo) { StringBuffer catalogXml = new StringBuffer(600); - String characterSet = parentPlatform.getCharacterSet(); + String characterSet = platform.getCharacterSet(); catalogXml.append("\r\n") .append("\r\n") .append("Catalog\r\n") .append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n") - .append("" + parentPlatform.getDeviceGBId() + "\r\n") + .append("" + platform.getDeviceGBId() + "\r\n") .append(""+ sumNum +"\r\n") .append("\r\n"); if (!channels.isEmpty()) { - for (DeviceChannel channel : channels) { - if (parentPlatform.getServerGBId().equals(channel.getParentId())) { - channel.setParentId(parentPlatform.getDeviceGBId()); - } - catalogXml.append("\r\n"); - // 行政区划分组只需要这两项就可以 - catalogXml.append("" + channel.getChannelId() + "\r\n"); - catalogXml.append("" + channel.getName() + "\r\n"); - if (channel.getParentId() != null) { - // 业务分组加上这一项即可,提高兼容性, - catalogXml.append("" + channel.getParentId() + "\r\n"); - } - if (channel.getChannelId().length() == 20 && Integer.parseInt(channel.getChannelId().substring(10, 13)) == 216) { - // 虚拟组织增加BusinessGroupID字段 - catalogXml.append("" + channel.getParentId() + "\r\n"); - } - catalogXml.append("" + channel.getParental() + "\r\n"); - if (channel.getParental() == 0) { - // 通道项 - catalogXml.append("" + channel.getManufacture() + "\r\n") - .append("" + channel.getSecrecy() + "\r\n") - .append("" + channel.getRegisterWay() + "\r\n") - .append("" + (channel.isStatus() ? "ON" : "OFF") + "\r\n"); - - if (channel.getChannelType() != 2) { // 业务分组/虚拟组织/行政区划 不设置以下属性 - catalogXml.append("" + channel.getModel() + "\r\n") - .append(" " + channel.getOwner()+ "\r\n") - .append("" + channel.getCivilCode() + "\r\n") - .append("
" + channel.getAddress() + "
\r\n"); - catalogXml.append("" + channel.getLongitude() + "\r\n"); - catalogXml.append("" + channel.getLatitude() + "\r\n"); - } - if (!"presence".equals(subscribeInfo.getEventType())) { - catalogXml.append("" + type + "\r\n"); - } - - } - catalogXml.append("
\r\n"); + for (CommonGBChannel channel : channels) { + catalogXml.append(channel.encode(type, platform.getDeviceGBId())); } } catalogXml.append("
\r\n") @@ -675,13 +484,13 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { } @Override - public void sendNotifyForCatalogOther(String type, ParentPlatform parentPlatform, List deviceChannels, - SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException { + public void sendNotifyForCatalogOther(String type, Platform parentPlatform, List deviceChannels, + SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException { if (parentPlatform == null || deviceChannels == null || deviceChannels.size() == 0 || subscribeInfo == null) { - logger.warn("[缺少必要参数]"); + log.warn("[缺少必要参数]"); return; } @@ -691,48 +500,42 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { if (index >= deviceChannels.size()) { return; } - List channels; + List channels; if (index + parentPlatform.getCatalogGroup() < deviceChannels.size()) { channels = deviceChannels.subList(index, index + parentPlatform.getCatalogGroup()); }else { channels = deviceChannels.subList(index, deviceChannels.size()); } - logger.info("[发送NOTIFY通知]类型: {},发送数量: {}", type, channels.size()); + log.info("[发送NOTIFY通知]类型: {},发送数量: {}", type, channels.size()); Integer finalIndex = index; String catalogXmlContent = getCatalogXmlContentForCatalogOther(parentPlatform, channels, type); sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> { - logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg); + log.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg); }, eventResult -> { try { sendNotifyForCatalogOther(type, parentPlatform, deviceChannels, subscribeInfo, finalIndex + parentPlatform.getCatalogGroup()); } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | IllegalAccessException e) { - logger.error("[命令发送失败] 国标级联 NOTIFY通知: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 NOTIFY通知: {}", e.getMessage()); } }); } - private String getCatalogXmlContentForCatalogOther(ParentPlatform parentPlatform, List channels, String type) { + private String getCatalogXmlContentForCatalogOther(Platform platform, List channels, String type) { - String characterSet = parentPlatform.getCharacterSet(); + String characterSet = platform.getCharacterSet(); StringBuffer catalogXml = new StringBuffer(600); catalogXml.append("\r\n") .append("\r\n") .append("Catalog\r\n") .append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n") - .append("" + parentPlatform.getDeviceGBId() + "\r\n") + .append("" + platform.getDeviceGBId() + "\r\n") .append("1\r\n") .append("\r\n"); - if (channels.size() > 0) { - for (DeviceChannel channel : channels) { - if (parentPlatform.getServerGBId().equals(channel.getParentId())) { - channel.setParentId(parentPlatform.getDeviceGBId()); - } - catalogXml.append("\r\n") - .append("" + channel.getChannelId() + "\r\n") - .append("" + type + "\r\n") - .append("\r\n"); + if (!channels.isEmpty()) { + for (CommonGBChannel channel : channels) { + catalogXml.append(channel.encode(type, platform.getDeviceGBId())); } } catalogXml.append("\r\n") @@ -740,18 +543,18 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { return catalogXml.toString(); } @Override - public void recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo) throws SipException, InvalidArgumentException, ParseException { + public void recordInfo(CommonGBChannel deviceChannel, Platform parentPlatform, String fromTag, RecordInfo recordInfo) throws SipException, InvalidArgumentException, ParseException { if ( parentPlatform ==null) { return ; } - logger.info("[国标级联] 发送录像数据通道: {}", recordInfo.getChannelId()); + log.info("[国标级联] 发送录像数据通道: {}", recordInfo.getChannelId()); String characterSet = parentPlatform.getCharacterSet(); StringBuffer recordXml = new StringBuffer(600); recordXml.append("\r\n") .append("\r\n") .append("RecordInfo\r\n") .append("" +recordInfo.getSn() + "\r\n") - .append("" + deviceChannel.getChannelId() + "\r\n") + .append("" + deviceChannel.getGbDeviceId() + "\r\n") .append("" + recordInfo.getSumNum() + "\r\n"); if (recordInfo.getRecordList() == null ) { recordXml.append("\r\n"); @@ -761,7 +564,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { for (RecordItem recordItem : recordInfo.getRecordList()) { recordXml.append("\r\n"); if (deviceChannel != null) { - recordXml.append("" + deviceChannel.getChannelId() + "\r\n") + recordXml.append("" + deviceChannel.getGbDeviceId() + "\r\n") .append("" + recordItem.getName() + "\r\n") .append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "\r\n") .append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "\r\n") @@ -781,19 +584,19 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { recordXml.append("\r\n") .append("\r\n"); - logger.info("[国标级联] 发送录像数据通道:{}, 内容: {}", recordInfo.getChannelId(), recordXml); + log.debug("[国标级联] 发送录像数据通道:{}, 内容: {}", 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, null, eventResult -> { - logger.info("[国标级联] 发送录像数据通道:{}, 发送成功", recordInfo.getChannelId()); + log.info("[国标级联] 发送录像数据通道:{}, 发送成功", recordInfo.getChannelId()); }); } @Override - public void sendMediaStatusNotify(ParentPlatform parentPlatform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException { + public void sendMediaStatusNotify(Platform parentPlatform, SendRtpInfo sendRtpItem) throws SipException, InvalidArgumentException, ParseException { if (sendRtpItem == null || parentPlatform == null) { return; } @@ -817,69 +620,64 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { } @Override - public void streamByeCmd(ParentPlatform platform, String callId) throws SipException, InvalidArgumentException, ParseException { - if (platform == null) { - return; - } - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platform.getServerGBId(), null, null, callId); - if (sendRtpItem != null) { - streamByeCmd(platform, sendRtpItem); - } - } - - @Override - public synchronized void streamByeCmd(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException { + public synchronized void streamByeCmd(Platform platform, SendRtpInfo sendRtpItem, CommonGBChannel channel) throws SipException, InvalidArgumentException, ParseException { if (sendRtpItem == null ) { - logger.info("[向上级发送BYE], sendRtpItem 为NULL"); + log.info("[向上级发送BYE], sendRtpItem 为NULL"); return; } if (platform == null) { - logger.info("[向上级发送BYE], platform 为NULL"); + log.info("[向上级发送BYE], platform 为NULL"); return; } - logger.info("[向上级发送BYE], {}/{}", platform.getServerGBId(), sendRtpItem.getChannelId()); + log.info("[向上级发送BYE], {}/{}", platform.getServerGBId(), sendRtpItem.getChannelId()); String mediaServerId = sendRtpItem.getMediaServerId(); MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId); if (mediaServerItem != null) { mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); mediaServerService.closeRTPServer(mediaServerItem, sendRtpItem.getStream()); } - SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(platform, sendRtpItem); + SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(platform, sendRtpItem, channel); if (byeRequest == null) { - logger.warn("[向上级发送bye]:无法创建 byeRequest"); + log.warn("[向上级发送bye]:无法创建 byeRequest"); } sipSender.transmitRequest(platform.getDeviceIp(),byeRequest); } @Override - public void streamByeCmd(ParentPlatform platform, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException { - SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(platform.getServerGBId(), channelId, callId, stream); + public void streamByeCmd(Platform platform, CommonGBChannel channel, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException { + + SsrcTransaction ssrcTransaction = null; + if (callId != null) { + ssrcTransaction = sessionManager.getSsrcTransactionByCallId(callId); + }else if (stream != null) { + ssrcTransaction = sessionManager.getSsrcTransactionByStream(stream); + } if (ssrcTransaction == null) { - throw new SsrcTransactionNotFoundException(platform.getServerGBId(), channelId, callId, stream); + throw new SsrcTransactionNotFoundException(platform.getServerGBId(), channel.getGbDeviceId(), callId, stream); } mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); - streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); + sessionManager.removeByStream(ssrcTransaction.getStream()); - Request byteRequest = headerProviderPlatformProvider.createByteRequest(platform, channelId, ssrcTransaction.getSipTransactionInfo()); + Request byteRequest = headerProviderPlatformProvider.createByteRequest(platform, channel.getGbDeviceId(), ssrcTransaction.getSipTransactionInfo()); sipSender.transmitRequest(sipLayer.getLocalIp(platform.getDeviceIp()), byteRequest, null, okEvent); } @Override - public void broadcastResultCmd(ParentPlatform platform, DeviceChannel deviceChannel, String sn, boolean result, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException { + public void broadcastResultCmd(Platform platform, CommonGBChannel deviceChannel, String sn, boolean result, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException { if (platform == null || deviceChannel == null) { return; } String characterSet = platform.getCharacterSet(); StringBuffer mediaStatusXml = new StringBuffer(200); - mediaStatusXml.append("\r\n"); - mediaStatusXml.append("\r\n"); - mediaStatusXml.append("Broadcast\r\n"); - mediaStatusXml.append("" + sn + "\r\n"); - mediaStatusXml.append("" + deviceChannel.getChannelId() + "\r\n"); - mediaStatusXml.append("" + (result?"OK":"ERROR") + "\r\n"); - mediaStatusXml.append("\r\n"); + mediaStatusXml.append("\r\n") + .append("\r\n") + .append("Broadcast\r\n") + .append("" + sn + "\r\n") + .append("" + deviceChannel.getGbDeviceId() + "\r\n") + .append("" + (result?"OK":"ERROR") + "\r\n") + .append("\r\n"); CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(platform.getDeviceIp(), platform.getTransport()); @@ -890,7 +688,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { } @Override - public void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServer mediaServerItem, + public void broadcastInviteCmd(Platform platform, CommonGBChannel channel, MediaServer mediaServerItem, SSRCInfo ssrcInfo, HookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws ParseException, SipException, InvalidArgumentException { String stream = ssrcInfo.getStream(); @@ -899,7 +697,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { return; } - logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort()); + log.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort()); Hook hook = Hook.getInstance(HookType.on_media_arrival, "rtp", stream, mediaServerItem.getId()); subscribe.addSubscribe(hook, (hookData) -> { if (event != null) { @@ -911,7 +709,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); - content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n"); + content.append("o=" + channel.getGbDeviceId() + " 0 0 IN IP4 " + sdpIp + "\r\n"); content.append("s=Play\r\n"); content.append("c=IN IP4 " + sdpIp + "\r\n"); content.append("t=0 0\r\n"); @@ -938,18 +736,19 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getTransport()); - Request request = headerProviderPlatformProvider.createInviteRequest(platform, channelId, + Request request = headerProviderPlatformProvider.createInviteRequest(platform, channel.getGbDeviceId(), content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), ssrcInfo.getSsrc(), callIdHeader); sipSender.transmitRequest(sipLayer.getLocalIp(platform.getDeviceIp()), request, (e -> { - streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getStream()); mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); subscribe.removeSubscribe(hook); errorEvent.response(e); }), e -> { ResponseEvent responseEvent = (ResponseEvent) e.event; SIPResponse response = (SIPResponse) responseEvent.getResponse(); - streamSession.put(platform.getServerGBId(), channelId, callIdHeader.getCallId(), stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.BROADCAST); + SsrcTransaction ssrcTransaction = SsrcTransaction.buildForPlatform(platform.getServerGBId(), channel.getGbId(), callIdHeader.getCallId(), stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.BROADCAST); + sessionManager.put(ssrcTransaction); okEvent.response(e); }); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java index f3f743172..990e788b9 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java @@ -1,17 +1,16 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.google.common.primitives.Bytes; import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPResponse; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import javax.sip.*; @@ -34,10 +33,9 @@ import java.util.List; * @author: songww * @date: 2020年5月3日 下午4:42:22 */ +@Slf4j public abstract class SIPRequestProcessorParent { - private final static Logger logger = LoggerFactory.getLogger(SIPRequestProcessorParent.class); - @Autowired private SIPSender sipSender; @@ -45,7 +43,7 @@ public abstract class SIPRequestProcessorParent { try { return SipFactory.getInstance().createHeaderFactory(); } catch (PeerUnavailableException e) { - logger.error("未处理的异常 ", e); + log.error("未处理的异常 ", e); } return null; } @@ -54,7 +52,7 @@ public abstract class SIPRequestProcessorParent { try { return SipFactory.getInstance().createMessageFactory(); } catch (PeerUnavailableException e) { - logger.error("未处理的异常 ", e); + log.error("未处理的异常 ", e); } return null; } @@ -94,7 +92,7 @@ public abstract class SIPRequestProcessorParent { if (responseAckExtraParam != null) { if (responseAckExtraParam.sipURI != null && sipRequest.getMethod().equals(Request.INVITE)) { - logger.debug("responseSdpAck SipURI: {}:{}", responseAckExtraParam.sipURI.getHost(), responseAckExtraParam.sipURI.getPort()); + log.debug("responseSdpAck SipURI: {}:{}", responseAckExtraParam.sipURI.getHost(), responseAckExtraParam.sipURI.getPort()); Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress( SipFactory.getInstance().createAddressFactory().createSipURI(responseAckExtraParam.sipURI.getUser(), responseAckExtraParam.sipURI.getHost()+":"+responseAckExtraParam.sipURI.getPort() )); @@ -106,7 +104,7 @@ public abstract class SIPRequestProcessorParent { if (sipRequest.getMethod().equals(Request.SUBSCRIBE)) { if (responseAckExtraParam.expires == -1) { - logger.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header"); + log.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header"); }else { ExpiresHeader expiresHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(responseAckExtraParam.expires); response.addHeader(expiresHeader); @@ -114,7 +112,7 @@ public abstract class SIPRequestProcessorParent { } }else { if (sipRequest.getMethod().equals(Request.SUBSCRIBE)) { - logger.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header"); + log.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header"); } } @@ -129,14 +127,14 @@ public abstract class SIPRequestProcessorParent { /** * 回复带sdp的200 */ - public SIPResponse responseSdpAck(SIPRequest request, String sdp, ParentPlatform platform) throws SipException, InvalidArgumentException, ParseException { + public SIPResponse responseSdpAck(SIPRequest request, String sdp, Platform platform) throws SipException, InvalidArgumentException, ParseException { ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); // 兼容国标中的使用编码@域名作为RequestURI的情况 SipURI sipURI = (SipURI)request.getRequestURI(); if (sipURI.getPort() == -1) { - sipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+":"+platform.getServerPort()); + sipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIp()+":"+platform.getServerPort()); } ResponseAckExtraParam responseAckExtraParam = new ResponseAckExtraParam(); responseAckExtraParam.contentTypeHeader = contentTypeHeader; @@ -152,12 +150,12 @@ public abstract class SIPRequestProcessorParent { /** * 回复带xml的200 */ - public SIPResponse responseXmlAck(SIPRequest request, String xml, ParentPlatform platform, Integer expires) throws SipException, InvalidArgumentException, ParseException { + public SIPResponse responseXmlAck(SIPRequest request, String xml, Platform platform, Integer expires) throws SipException, InvalidArgumentException, ParseException { ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); SipURI sipURI = (SipURI)request.getRequestURI(); if (sipURI.getPort() == -1) { - sipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+":"+platform.getServerPort()); + sipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIp()+":"+platform.getServerPort()); } ResponseAckExtraParam responseAckExtraParam = new ResponseAckExtraParam(); responseAckExtraParam.contentTypeHeader = contentTypeHeader; @@ -210,8 +208,8 @@ public abstract class SIPRequestProcessorParent { try { xml = reader.read(new ByteArrayInputStream(bytesResult)); }catch (DocumentException e) { - logger.warn("[xml解析异常]: 原文如下: \r\n{}", new String(bytesResult)); - logger.warn("[xml解析异常]: 原文如下: 尝试兼容性处理"); + log.warn("[xml解析异常]: 原文如下: \r\n{}", new String(bytesResult)); + log.warn("[xml解析异常]: 原文如下: 尝试兼容性处理"); String[] xmlLineArray = new String(bytesResult).split("\\r?\\n"); // 兼容海康的address字段带有<破换xml结构导致无法解析xml的问题 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java index d4df0ae5b..004b1ec82 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java @@ -4,21 +4,23 @@ 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.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IPlatformService; +import com.genersoft.iot.vmp.gb28181.service.IPlayService; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.service.IDeviceService; -import com.genersoft.iot.vmp.service.IPlayService; +import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -34,10 +36,10 @@ import javax.sip.header.ToHeader; * SIP命令类型: ACK请求 * @author lin */ +@Slf4j @Component public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { - private final Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class); private final String method = "ACK"; @Autowired @@ -51,6 +53,7 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In @Autowired private IRedisCatchStorage redisCatchStorage; + @Autowired private IRedisRpcService redisRpcService; @@ -58,11 +61,14 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In private UserSetting userSetting; @Autowired - private IVideoManagerStorage storager; + private IPlatformService platformService; @Autowired private IDeviceService deviceService; + @Autowired + private IDeviceChannelService deviceChannelService; + @Autowired private IMediaServerService mediaServerService; @@ -72,6 +78,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In @Autowired private IPlayService playService; + @Autowired + private ISendRtpServerService sendRtpServerService; + /** * 处理 ACK请求 @@ -82,68 +91,75 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In dynamicTask.stop(callIdHeader.getCallId()); String fromUserId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); String toUserId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); - logger.info("[收到ACK]: 来自->{}", fromUserId); - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId()); + log.info("[收到ACK]: 来自->{}", fromUserId); + SendRtpInfo sendRtpItem = sendRtpServerService.queryByCallId(callIdHeader.getCallId()); if (sendRtpItem == null) { - logger.warn("[收到ACK]:未找到来自{},callId: {}", fromUserId, callIdHeader.getCallId()); + log.warn("[收到ACK]:未找到来自{},callId: {}", fromUserId, callIdHeader.getCallId()); return; } // tcp主动时,此时是级联下级平台,在回复200ok时,本地已经请求zlm开启监听,跳过下面步骤 if (sendRtpItem.isTcpActive()) { - logger.info("收到ACK,rtp/{} TCP主动方式后续处理", sendRtpItem.getStream()); + log.info("收到ACK,rtp/{} TCP主动方式后续处理", sendRtpItem.getStream()); return; } - MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); - logger.info("收到ACK,rtp/{}开始向上级推流, 目标={}:{},SSRC={}, 协议:{}", + MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + log.info("收到ACK,rtp/{}开始向上级推流, 目标={}:{},SSRC={}, 协议:{}", sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp()?(sendRtpItem.isTcpActive()?"TCP主动":"TCP被动"):"UDP" ); - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(fromUserId); + Platform parentPlatform = platformService.queryPlatformByServerGBId(fromUserId); if (parentPlatform != null) { + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(sendRtpItem.getChannelId()); if (!userSetting.getServerId().equals(sendRtpItem.getServerId())) { - WVPResult wvpResult = redisRpcService.startSendRtp(sendRtpItem.getRedisKey(), sendRtpItem); + WVPResult wvpResult = redisRpcService.startSendRtp(callIdHeader.getCallId(), sendRtpItem); if (wvpResult.getCode() == 0) { - redisCatchStorage.sendPlatformStartPlayMsg(sendRtpItem, parentPlatform); + redisCatchStorage.sendPlatformStartPlayMsg(sendRtpItem, deviceChannel, parentPlatform); } } else { try { - if (sendRtpItem.isTcpActive()) { - mediaServerService.startSendRtpPassive(mediaInfo,sendRtpItem, null); - } else { - mediaServerService.startSendRtp(mediaInfo, sendRtpItem); + if (mediaServer != null) { + if (sendRtpItem.isTcpActive()) { + mediaServerService.startSendRtpPassive(mediaServer,sendRtpItem, null); + } else { + mediaServerService.startSendRtp(mediaServer, sendRtpItem); + } + }else { + // mediaInfo 在集群的其他wvp里 + } - redisCatchStorage.sendPlatformStartPlayMsg(sendRtpItem, parentPlatform); + + redisCatchStorage.sendPlatformStartPlayMsg(sendRtpItem, deviceChannel, parentPlatform); }catch (ControllerException e) { - logger.error("RTP推流失败: {}", e.getMessage()); + log.error("RTP推流失败: {}", e.getMessage()); playService.startSendRtpStreamFailHand(sendRtpItem, parentPlatform, callIdHeader); } } }else { - Device device = deviceService.getDevice(fromUserId); + Device device = deviceService.getDeviceByDeviceId(fromUserId); if (device == null) { - logger.warn("[收到ACK]:来自{},目标为({})的推流信息为找到流体服务[{}]信息",fromUserId, toUserId, sendRtpItem.getMediaServerId()); + log.warn("[收到ACK]:来自{},目标为({})的推流信息为找到流体服务[{}]信息",fromUserId, toUserId, sendRtpItem.getMediaServerId()); return; } // 设置为收到ACK后发送语音的设备已经在发送200OK开始发流了 if (!device.isBroadcastPushAfterAck()) { return; } - if (mediaInfo == null) { - logger.warn("[收到ACK]:来自{},目标为({})的推流信息为找到流体服务[{}]信息",fromUserId, toUserId, sendRtpItem.getMediaServerId()); + if (mediaServer == null) { + log.warn("[收到ACK]:来自{},目标为({})的推流信息为找到流体服务[{}]信息",fromUserId, toUserId, sendRtpItem.getMediaServerId()); return; } try { if (sendRtpItem.isTcpActive()) { - mediaServerService.startSendRtpPassive(mediaInfo, sendRtpItem, null); + mediaServerService.startSendRtpPassive(mediaServer, sendRtpItem, null); } else { - mediaServerService.startSendRtp(mediaInfo, sendRtpItem); + mediaServerService.startSendRtp(mediaServer, sendRtpItem); } }catch (ControllerException e) { - logger.error("RTP推流失败: {}", e.getMessage()); + log.error("RTP推流失败: {}", e.getMessage()); playService.startSendRtpStreamFailHand(sendRtpItem, null, callIdHeader); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java index 178186fde..6d905f18f 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java @@ -5,23 +5,21 @@ import com.genersoft.iot.vmp.common.InviteSessionType; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.service.*; import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.service.*; +import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import gov.nist.javax.sip.message.SIPRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -36,17 +34,17 @@ import java.text.ParseException; /** * SIP命令类型: BYE请求 */ +@Slf4j @Component public class ByeRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { - private final Logger logger = LoggerFactory.getLogger(ByeRequestProcessor.class); private final String method = "BYE"; @Autowired private ISIPCommander cmder; @Autowired - private ISIPCommanderForPlatform commanderForPlatform; + private ISendRtpServerService sendRtpServerService; @Autowired private IRedisCatchStorage redisCatchStorage; @@ -60,14 +58,14 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @Autowired private IDeviceService deviceService; + @Autowired + private IDeviceChannelService deviceChannelService; + @Autowired private AudioBroadcastManager audioBroadcastManager; @Autowired - private IDeviceChannelService channelService; - - @Autowired - private IVideoManagerStorage storager; + private IGbChannelService channelService; @Autowired private IMediaServerService mediaServerService; @@ -76,7 +74,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In private SIPProcessorObserver sipProcessorObserver; @Autowired - private VideoStreamSessionManager streamSession; + private SipInviteSessionManager sessionManager; @Autowired private IPlayService playService; @@ -84,9 +82,6 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @Autowired private UserSetting userSetting; - @Autowired - private IStreamPushService pushService; - @Autowired private IRedisRpcService redisRpcService; @@ -106,41 +101,43 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In try { responseAck(request, Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[回复BYE信息失败],{}", e.getMessage()); + log.error("[回复BYE信息失败],{}", e.getMessage()); } CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId()); + SendRtpInfo sendRtpItem = sendRtpServerService.queryByCallId(callIdHeader.getCallId()); // 收流端发送的停止 if (sendRtpItem != null){ - logger.info("[收到bye] 来自{},停止通道:{}, 类型: {}, callId: {}", sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), sendRtpItem.getPlayType(), callIdHeader.getCallId()); + CommonGBChannel channel = channelService.getOne(sendRtpItem.getChannelId()); + log.info("[收到bye] 来自{},停止通道:{}, 类型: {}, callId: {}", sendRtpItem.getTargetId(), channel.getGbDeviceId(), sendRtpItem.getPlayType(), callIdHeader.getCallId()); String streamId = sendRtpItem.getStream(); - logger.info("[收到bye] 停止推流:{}, 媒体节点: {}", streamId, sendRtpItem.getMediaServerId()); + log.info("[收到bye] 停止推流:{}, 媒体节点: {}", streamId, sendRtpItem.getMediaServerId()); if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) { // 不是本平台的就发送redis消息让其他wvp停止发流 - ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId()); + Platform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getTargetId()); if (platform != null) { - redisCatchStorage.sendPlatformStopPlayMsg(sendRtpItem, platform); + redisCatchStorage.sendPlatformStopPlayMsg(sendRtpItem, platform, channel); if (!userSetting.getServerId().equals(sendRtpItem.getServerId())) { - redisRpcService.stopSendRtp(sendRtpItem.getRedisKey()); - redisCatchStorage.deleteSendRTPServer(null, null, sendRtpItem.getCallId(), null); + redisRpcService.stopSendRtp(sendRtpItem.getCallId()); + sendRtpServerService.deleteByCallId(sendRtpItem.getCallId()); }else { MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); - redisCatchStorage.deleteSendRTPServer(null, null, callIdHeader.getCallId(), null); - mediaServerService.stopSendRtp(mediaServer, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc()); - if (userSetting.getUseCustomSsrcForParentInvite()) { - mediaServerService.releaseSsrc(mediaServer.getId(), sendRtpItem.getSsrc()); + sendRtpServerService.deleteByCallId(callIdHeader.getCallId()); + if (mediaServer != null) { + mediaServerService.stopSendRtp(mediaServer, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc()); + if (userSetting.getUseCustomSsrcForParentInvite()) { + mediaServerService.releaseSsrc(mediaServer.getId(), sendRtpItem.getSsrc()); + } } } }else { - logger.info("[上级平台停止观看] 未找到平台{}的信息,发送redis消息失败", sendRtpItem.getPlatformId()); + log.info("[上级平台停止观看] 未找到平台{}的信息,发送redis消息失败", sendRtpItem.getTargetId()); } }else { MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); - redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), - callIdHeader.getCallId(), null); + sendRtpServerService.delete(sendRtpItem); mediaServerService.stopSendRtp(mediaInfo, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc()); if (userSetting.getUseCustomSsrcForParentInvite()) { mediaServerService.releaseSsrc(mediaInfo.getId(), sendRtpItem.getSsrc()); @@ -148,104 +145,109 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In } MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); if (mediaServer != null) { - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getChannelId()); if (audioBroadcastCatch != null && audioBroadcastCatch.getSipTransactionInfo().getCallId().equals(callIdHeader.getCallId())) { // 来自上级平台的停止对讲 - logger.info("[停止对讲] 来自上级,平台:{}, 通道:{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); - audioBroadcastManager.del(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); + log.info("[停止对讲] 来自上级,平台:{}, 通道:{}", sendRtpItem.getTargetId(), sendRtpItem.getChannelId()); + audioBroadcastManager.del(sendRtpItem.getChannelId()); } MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServer, sendRtpItem.getApp(), streamId); if (mediaInfo.getReaderCount() <= 0) { - logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId); + log.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId); if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) { - Device device = deviceService.getDevice(sendRtpItem.getDeviceId()); + Device device = deviceService.getDeviceByDeviceId(sendRtpItem.getTargetId()); if (device == null) { - logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId); + log.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId); + return; + } + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(sendRtpItem.getChannelId()); + if (deviceChannel == null) { + log.info("[收到bye] {} 通知设备停止推流时未找到通道信息", streamId); + return; } try { - logger.info("[停止点播] {}/{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); - cmder.streamByeCmd(device, sendRtpItem.getChannelId(), streamId, null); + log.info("[停止点播] {}/{}", sendRtpItem.getTargetId(), sendRtpItem.getChannelId()); + cmder.streamByeCmd(device, deviceChannel.getDeviceId(), streamId, null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { - logger.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage()); + log.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage()); } } } } } - System.out.println(callIdHeader.getCallId()); // 可能是设备发送的停止 - SsrcTransaction ssrcTransaction = streamSession.getSsrcTransactionByCallId(callIdHeader.getCallId()); + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByCallId(callIdHeader.getCallId()); if (ssrcTransaction == null) { return; } - logger.info("[收到bye] 来自设备:{}, 通道: {}, 类型: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getType()); + log.info("[收到bye] 来自:{}, 通道: {}, 类型: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getType()); - ParentPlatform platform = platformService.queryPlatformByServerGBId(ssrcTransaction.getDeviceId()); - if (platform != null ) { + + if (ssrcTransaction.getPlatformId() != null ) { + Platform platform = platformService.queryPlatformByServerGBId(ssrcTransaction.getDeviceId()); if (ssrcTransaction.getType().equals(InviteSessionType.BROADCAST)) { - logger.info("[收到bye] 上级停止语音对讲,来自:{}, 通道已停止推流: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); - DeviceChannel channel = storager.queryChannelInParentPlatform(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); + log.info("[收到bye] 上级停止语音对讲,来自:{}, 通道已停止推流: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); + CommonGBChannel channel = channelService.getOne(ssrcTransaction.getChannelId()); if (channel == null) { - logger.info("[收到bye] 未找到通道,设备:{}, 通道:{}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); + log.info("[收到bye] 未找到通道,设备:{}, 通道:{}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); return; } String mediaServerId = ssrcTransaction.getMediaServerId(); platformService.stopBroadcast(platform, channel, ssrcTransaction.getStream(), false, mediaServerService.getOne(mediaServerId)); - - playService.stopAudioBroadcast(channel.getDeviceId(), channel.getChannelId()); + Device device = deviceService.getDeviceByDeviceId(ssrcTransaction.getDeviceId()); + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(ssrcTransaction.getChannelId()); + playService.stopAudioBroadcast(device, deviceChannel); } }else { - Device device = deviceService.getDevice(ssrcTransaction.getDeviceId()); + Device device = deviceService.getDeviceByDeviceId(ssrcTransaction.getDeviceId()); if (device == null) { - logger.info("[收到bye] 未找到设备:{} ", ssrcTransaction.getDeviceId()); + log.info("[收到bye] 未找到设备:{} ", ssrcTransaction.getDeviceId()); return; } - DeviceChannel channel = channelService.getOne(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); + DeviceChannel channel = deviceChannelService.getOneForSourceById(ssrcTransaction.getChannelId()); if (channel == null) { - logger.info("[收到bye] 未找到通道,设备:{}, 通道:{}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); + log.info("[收到bye] 未找到通道,设备:{}, 通道:{}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); return; } switch (ssrcTransaction.getType()){ case PLAY: case PLAYBACK: case DOWNLOAD: - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId()); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); if (inviteInfo != null) { - storager.stopPlay(device.getDeviceId(), channel.getChannelId()); + deviceChannelService.stopPlay(channel.getId()); inviteStreamService.removeInviteInfo(inviteInfo); if (inviteInfo.getStreamInfo() != null) { - mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStreamInfo().getStream()); + mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServer(), inviteInfo.getStreamInfo().getStream()); } } break; case BROADCAST: case TALK: // 查找来源的对讲设备,发送停止 - Device sourceDevice = storager.queryVideoDeviceByPlatformIdAndChannelId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(ssrcTransaction.getDeviceId(), channel.getChannelId()); + Device sourceDevice = deviceService.getDeviceByChannelId(ssrcTransaction.getChannelId()); + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(channel.getId()); if (sourceDevice != null) { - playService.stopAudioBroadcast(sourceDevice.getDeviceId(), channel.getChannelId()); + playService.stopAudioBroadcast(sourceDevice, channel); } if (audioBroadcastCatch != null) { // 来自上级平台的停止对讲 - logger.info("[停止对讲] 来自上级,平台:{}, 通道:{}", ssrcTransaction.getDeviceId(), channel.getChannelId()); - audioBroadcastManager.del(ssrcTransaction.getDeviceId(), channel.getChannelId()); + log.info("[停止对讲] 来自上级,平台:{}, 通道:{}", ssrcTransaction.getDeviceId(), channel.getDeviceId()); + audioBroadcastManager.del(channel.getId()); } break; - - } // 释放ssrc MediaServer mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId()); if (mediaServerItem != null) { mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc()); } - streamSession.removeByCallId(device.getDeviceId(), channel.getChannelId(), ssrcTransaction.getCallId()); + sessionManager.removeByCallId(ssrcTransaction.getCallId()); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java index 411c40fd2..bf1e17eb7 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java @@ -1,55 +1,37 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; -import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.InviteSessionType; -import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.service.*; import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; -import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; -import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.media.event.hook.Hook; import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; -import com.genersoft.iot.vmp.media.event.hook.HookType; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; -import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; -import com.genersoft.iot.vmp.service.IPlayService; -import com.genersoft.iot.vmp.service.IStreamProxyService; -import com.genersoft.iot.vmp.service.IStreamPushService; import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; -import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService; -import com.genersoft.iot.vmp.service.bean.ErrorCallback; +import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.service.bean.InviteErrorCode; -import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; -import com.genersoft.iot.vmp.service.bean.SSRCInfo; import com.genersoft.iot.vmp.service.redisMsg.RedisPushStreamResponseListener; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; -import com.genersoft.iot.vmp.utils.DateUtil; import gov.nist.javax.sdp.TimeDescriptionImpl; import gov.nist.javax.sdp.fields.TimeField; import gov.nist.javax.sdp.fields.URIField; import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPResponse; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; @@ -63,39 +45,42 @@ import javax.sip.header.CallIdHeader; import javax.sip.message.Response; import java.text.ParseException; import java.time.Instant; -import java.util.Map; -import java.util.Random; +import java.util.List; import java.util.Vector; -import java.util.*; /** * SIP命令类型: INVITE请求 */ +@Slf4j @SuppressWarnings("rawtypes") @Component public class InviteRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { - private final static Logger logger = LoggerFactory.getLogger(InviteRequestProcessor.class); - private final String method = "INVITE"; @Autowired private ISIPCommanderForPlatform cmderFroPlatform; @Autowired - private IVideoManagerStorage storager; + private IDeviceChannelService deviceChannelService; @Autowired - private IStreamPushService streamPushService; + private IDeviceService deviceService; @Autowired - private IStreamProxyService streamProxyService; + private IGbChannelService channelService; + + @Autowired + private IGbChannelPlayService channelPlayService; + + @Autowired + private ISendRtpServerService sendRtpServerService; @Autowired private IRedisCatchStorage redisCatchStorage; @Autowired - private IRedisRpcService redisRpcService; + private IMediaServerService mediaServerService; @Autowired private RedisTemplate redisTemplate; @@ -110,14 +95,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements private IPlayService playService; @Autowired - private SIPSender sipSender; + private IPlatformService platformService; @Autowired private AudioBroadcastManager audioBroadcastManager; - @Autowired - private IMediaServerService mediaServerService; - @Autowired private HookSubscribe hookSubscribe; @@ -131,7 +113,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements private SipConfig config; @Autowired - private VideoStreamSessionManager streamSession; + private SipInviteSessionManager sessionManager; @Autowired private SendRtpPortManager sendRtpPortManager; @@ -153,941 +135,470 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements */ @Override public void process(RequestEvent evt) { - // Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令 + + SIPRequest request = (SIPRequest)evt.getRequest(); try { - SIPRequest request = (SIPRequest)evt.getRequest(); - String channelIdFromSub = SipUtils.getChannelIdFromRequest(request); - - // 解析sdp消息, 使用jainsip 自带的sdp解析方式 - String contentString = new String(request.getRawContent()); - Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); - SessionDescription sdp = gb28181Sdp.getBaseSdb(); - String sessionName = sdp.getSessionName().getValue(); - String channelIdFromSdp = null; - if(StringUtils.equalsIgnoreCase("Playback", sessionName)){ - URIField uriField = (URIField)sdp.getURI(); - channelIdFromSdp = uriField.getURI().split(":")[0]; - } - final String channelId = StringUtils.isNotBlank(channelIdFromSdp) ? channelIdFromSdp : channelIdFromSub; - - String requesterId = SipUtils.getUserIdFromFromHeader(request); - CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); - if (requesterId == null || channelId == null) { - logger.info("无法从请求中获取到平台id,返回400"); - // 参数不全, 发400,请求错误 - try { - responseAck(request, Response.BAD_REQUEST); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage()); - } - return; - } - - logger.info("[INVITE] requesterId: {}, callId: {}, 来自:{}:{}", - requesterId, callIdHeader.getCallId(), request.getRemoteAddress(), request.getRemotePort()); + InviteInfo inviteInfo = decode(evt); // 查询请求是否来自上级平台\设备 - ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId); + Platform platform = platformService.queryPlatformByServerGBId(inviteInfo.getRequesterId()); if (platform == null) { - inviteFromDeviceHandle(request, requesterId, channelId); - + inviteFromDeviceHandle(request, inviteInfo); } else { // 查询平台下是否有该通道 - DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId); - GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId); - PlatformCatalog catalog = storager.getCatalog(requesterId, channelId); - - MediaServer mediaServerItem = null; - StreamPushItem streamPushItem = null; - StreamProxyItem proxyByAppAndStream = null; - // 不是通道可能是直播流 - if (channel != null && gbStream == null) { - // 通道存在,发100,TRYING - try { - responseAck(request, Response.TRYING); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite TRYING: {}", e.getMessage()); - } - } else if (channel == null && gbStream != null) { - - String mediaServerId = gbStream.getMediaServerId(); - mediaServerItem = mediaServerService.getOne(mediaServerId); - if (mediaServerItem == null) { - if ("proxy".equals(gbStream.getStreamType())) { - logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId); - try { - responseAck(request, Response.GONE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite GONE: {}", e.getMessage()); - } - return; - } else { - streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream()); - if (streamPushItem != null) { - mediaServerItem = mediaServerService.getOne(streamPushItem.getMediaServerId()); - } - if (mediaServerItem == null) { - mediaServerItem = mediaServerService.getDefaultMediaServer(); - } - } - } else { - if ("push".equals(gbStream.getStreamType())) { - streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream()); - if (streamPushItem == null) { - logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId); - try { - responseAck(request, Response.GONE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite GONE: {}", e.getMessage()); - } - return; - } - } else if ("proxy".equals(gbStream.getStreamType())) { - proxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(gbStream.getApp(), gbStream.getStream()); - if (proxyByAppAndStream == null) { - logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId); - try { - responseAck(request, Response.GONE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite GONE: {}", e.getMessage()); - } - return; - } - } - } - try { - responseAck(request, Response.CALL_IS_BEING_FORWARDED); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite CALL_IS_BEING_FORWARDED: {}", e.getMessage()); - } - } else if (catalog != null) { - try { - // 目录不支持点播 - responseAck(request, Response.BAD_REQUEST, "catalog channel can not play"); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite 目录不支持点播: {}", e.getMessage()); - } - return; - } else { - logger.info("通道不存在,返回404: {}", channelId); + CommonGBChannel channel= channelService.queryOneWithPlatform(platform.getId(), inviteInfo.getTargetChannelId()); + if (channel == null) { + log.info("[上级INVITE] 通道不存在,返回404: {}", inviteInfo.getTargetChannelId()); try { // 通道不存在,发404,资源不存在 responseAck(request, Response.NOT_FOUND); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite 通道不存在: {}", e.getMessage()); + log.error("[命令发送失败] invite 通道不存在: {}", e.getMessage()); } return; } - - Long startTime = null; - Long stopTime = null; - Instant start = null; - Instant end = null; - if (sdp.getTimeDescriptions(false) != null && sdp.getTimeDescriptions(false).size() > 0) { - TimeDescriptionImpl timeDescription = (TimeDescriptionImpl) (sdp.getTimeDescriptions(false).get(0)); - TimeField startTimeFiled = (TimeField) timeDescription.getTime(); - startTime = startTimeFiled.getStartTime(); - stopTime = startTimeFiled.getStopTime(); - - start = Instant.ofEpochSecond(startTime); - end = Instant.ofEpochSecond(stopTime); - } - // 获取支持的格式 - Vector mediaDescriptions = sdp.getMediaDescriptions(true); - // 查看是否支持PS 负载96 - //String ip = null; - int port = -1; - boolean mediaTransmissionTCP = false; - Boolean tcpActive = null; - for (Object description : mediaDescriptions) { - MediaDescription mediaDescription = (MediaDescription) description; - Media media = mediaDescription.getMedia(); - - Vector mediaFormats = media.getMediaFormats(false); - if (mediaFormats.contains("96")) { - port = media.getMediaPort(); - //String mediaType = media.getMediaType(); - String protocol = media.getProtocol(); - - // 区分TCP发流还是udp, 当前默认udp - if ("TCP/RTP/AVP".equalsIgnoreCase(protocol)) { - String setup = mediaDescription.getAttribute("setup"); - if (setup != null) { - mediaTransmissionTCP = true; - if ("active".equalsIgnoreCase(setup)) { - tcpActive = true; - } else if ("passive".equalsIgnoreCase(setup)) { - tcpActive = false; - } - } - } - break; - } - } - if (port == -1) { - logger.info("不支持的媒体格式,返回415"); - // 回复不支持的格式 - try { - // 不支持的格式,发415 - responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite 不支持的格式: {}", e.getMessage()); - } - return; - } - String username = sdp.getOrigin().getUsername(); - String addressStr; - if(StringUtils.isEmpty(platform.getSendStreamIp())){ - addressStr = sdp.getConnection().getAddress(); - }else { - addressStr = platform.getSendStreamIp(); + log.info("[上级Invite] 平台:{}, 通道:{}({}), 收流地址:{}:{},收流方式:{}, 点播类型:{}, ssrc:{}", + platform.getName(), channel.getGbName(), channel.getGbDeviceId(), inviteInfo.getIp(), + inviteInfo.getPort(), inviteInfo.isTcp()?(inviteInfo.isTcpActive()?"TCP主动":"TCP被动"): "UDP", + inviteInfo.getSessionName(), inviteInfo.getSsrc()); + // 通道存在,发100,TRYING + try { + responseAck(request, Response.TRYING); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 上级Invite TRYING: {}", e.getMessage()); } - Device device = null; - // 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标 - if (channel != null) { - device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId); - if (device == null) { - logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel); + channelPlayService.start(channel, inviteInfo, platform, ((code, msg, streamInfo) -> { + if (code != InviteErrorCode.SUCCESS.getCode()) { try { - responseAck(request, Response.SERVER_INTERNAL_ERROR); + responseAck(request, code, msg); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite 未找到设备信息: {}", e.getMessage()); + log.error("[命令发送失败] 上级Invite 点播失败: {}", e.getMessage()); } - return; - } - mediaServerItem = playService.getNewMediaServerItem(device); - if (mediaServerItem == null) { - logger.warn("未找到可用的zlm"); - try { - responseAck(request, Response.BUSY_HERE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite BUSY_HERE: {}", e.getMessage()); - } - return; - } - - String ssrc; - if (userSetting.getUseCustomSsrcForParentInvite() || gb28181Sdp.getSsrc() == null) { - // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 - ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); }else { - ssrc = gb28181Sdp.getSsrc(); - } - String streamTypeStr = null; - if (mediaTransmissionTCP) { - if (tcpActive) { - streamTypeStr = "TCP-ACTIVE"; - } else { - streamTypeStr = "TCP-PASSIVE"; - } - } else { - streamTypeStr = "UDP"; - } - logger.info("[上级Invite] {}, 平台:{}, 通道:{}, 收流地址:{}:{},收流方式:{}, ssrc:{}", - sessionName, username, channelId, addressStr, port, streamTypeStr, ssrc); - SendRtpItem sendRtpItem = mediaServerService.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, - device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp()); + // 点播成功, TODO 可以在此处检测cancel命令是否存在,存在则不发送 - if (tcpActive != null) { - sendRtpItem.setTcpActive(tcpActive); - } - if (sendRtpItem == null) { - logger.warn("服务器端口资源不足"); - try { - responseAck(request, Response.BUSY_HERE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); + // 构建sendRTP内容 + SendRtpInfo sendRtpItem = sendRtpServerService.createSendRtpInfo(streamInfo.getMediaServer(), + inviteInfo.getIp(), inviteInfo.getPort(), inviteInfo.getSsrc(), platform.getServerGBId(), + streamInfo.getApp(), streamInfo.getStream(), + channel.getGbId(), inviteInfo.isTcp(), platform.isRtcp()); + if (inviteInfo.isTcp() && inviteInfo.isTcpActive()) { + sendRtpItem.setTcpActive(true); } - return; - } - sendRtpItem.setCallId(callIdHeader.getCallId()); - sendRtpItem.setPlayType("Play".equalsIgnoreCase(sessionName) ? InviteStreamType.PLAY : InviteStreamType.PLAYBACK); - - Long finalStartTime = startTime; - Long finalStopTime = stopTime; - ErrorCallback hookEvent = (code, msg, data) -> { - StreamInfo streamInfo = (StreamInfo)data; - MediaServer mediaServerItemInUSe = mediaServerService.getOne(streamInfo.getMediaServerId()); - logger.info("[上级Invite]下级已经开始推流。 回复200OK(SDP), {}/{}", streamInfo.getApp(), streamInfo.getStream()); - // * 0 等待设备推流上来 - // * 1 下级已经推流,等待上级平台回复ack - // * 2 推流中 sendRtpItem.setStatus(1); - redisCatchStorage.updateSendRTPSever(sendRtpItem); - String sdpIp = mediaServerItemInUSe.getSdpIp(); + sendRtpItem.setCallId(inviteInfo.getCallId()); + sendRtpItem.setPlayType("Play".equalsIgnoreCase(inviteInfo.getSessionName()) ? InviteStreamType.PLAY : InviteStreamType.PLAYBACK); + sendRtpItem.setServerId(streamInfo.getServerId()); + sendRtpServerService.update(sendRtpItem); + String sdpIp = streamInfo.getMediaServer().getSdpIp(); if (!ObjectUtils.isEmpty(platform.getSendStreamIp())) { sdpIp = platform.getSendStreamIp(); } - StringBuffer content = new StringBuffer(200); - content.append("v=0\r\n"); - content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n"); - content.append("s=" + sessionName + "\r\n"); - content.append("c=IN IP4 " + sdpIp + "\r\n"); - if ("Playback".equalsIgnoreCase(sessionName)) { - content.append("t=" + finalStartTime + " " + finalStopTime + "\r\n"); - } else { - content.append("t=0 0\r\n"); - } - int localPort = sendRtpItem.getLocalPort(); - if (localPort == 0) { - // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口 - localPort = new Random().nextInt(65535) + 1; - } - 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"); - content.append("f=\r\n"); - - + String content = createSendSdp(sendRtpItem, inviteInfo, sdpIp); + // 超时未收到Ack应该回复bye,当前等待时间为10秒 + dynamicTask.startDelay(inviteInfo.getCallId(), () -> { + log.info("[Ack ] 等待超时, {}/{}", inviteInfo.getCallId(), channel.getGbDeviceId()); + mediaServerService.releaseSsrc(streamInfo.getMediaServer().getId(), sendRtpItem.getSsrc()); + // 回复bye + sendBye(platform, inviteInfo.getCallId()); + }, 60 * 1000); try { - // 超时未收到Ack应该回复bye,当前等待时间为10秒 - dynamicTask.startDelay(callIdHeader.getCallId(), () -> { - logger.info("Ack 等待超时"); - mediaServerService.releaseSsrc(mediaServerItemInUSe.getId(), sendRtpItem.getSsrc()); - // 回复bye - try { - cmderFroPlatform.streamByeCmd(platform, callIdHeader.getCallId()); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); - } - }, 60 * 1000); - responseSdpAck(request, content.toString(), platform); - // tcp主动模式,回复sdp后开启监听 - if (sendRtpItem.isTcpActive()) { - MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); - try { - mediaServerService.startSendRtpPassive(mediaServer, sendRtpItem, 5); - redisCatchStorage.sendPlatformStartPlayMsg(sendRtpItem, platform); - }catch (ControllerException e) {} - } + responseSdpAck(request, content, platform); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 回复SdpAck", e); - } - }; - ErrorCallback errorEvent = ((statusCode, msg, data) -> { - logger.info("[上级Invite] {}, 失败, 平台:{}, 通道:{}, code: {}, msg;{}", sessionName, username, channelId, statusCode, msg); - // 未知错误。直接转发设备点播的错误 - try { - Response response = getMessageFactory().createResponse(statusCode, evt.getRequest()); - sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); - } catch (ParseException | SipException e) { - logger.error("未处理的异常 ", e); - } - }); - sendRtpItem.setApp("rtp"); - if ("Playback".equalsIgnoreCase(sessionName)) { - sendRtpItem.setPlayType(InviteStreamType.PLAYBACK); - String startTimeStr = DateUtil.urlFormatter.format(start); - String endTimeStr = DateUtil.urlFormatter.format(end); - String stream = device.getDeviceId() + "_" + channelId + "_" + startTimeStr + "_" + endTimeStr; - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, stream, null, device.isSsrcCheck(), true, 0,false,!channel.getHasAudio(), false, device.getStreamModeForParam()); - sendRtpItem.setStream(stream); - // 写入redis, 超时时回复 - redisCatchStorage.updateSendRTPSever(sendRtpItem); - playService.playBack(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start), - DateUtil.formatter.format(end), - (code, msg, data) -> { - if (code == InviteErrorCode.SUCCESS.getCode()) { - hookEvent.run(code, msg, data); - } else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()) { - logger.info("[录像回放]超时, 用户:{}, 通道:{}", username, channelId); - redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null); - errorEvent.run(code, msg, data); - } else { - errorEvent.run(code, msg, data); - } - }); - } else if ("Download".equalsIgnoreCase(sessionName)) { - // 获取指定的下载速度 - Vector sdpMediaDescriptions = sdp.getMediaDescriptions(true); - MediaDescription mediaDescription = null; - String downloadSpeed = "1"; - if (sdpMediaDescriptions.size() > 0) { - mediaDescription = (MediaDescription) sdpMediaDescriptions.get(0); - } - if (mediaDescription != null) { - downloadSpeed = mediaDescription.getAttribute("downloadspeed"); + log.error("[命令发送失败] 上级Invite 发送 200(SDP): {}", e.getMessage()); } - sendRtpItem.setPlayType(InviteStreamType.DOWNLOAD); - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false,!channel.getHasAudio(), false, device.getStreamModeForParam()); - sendRtpItem.setStream(ssrcInfo.getStream()); - // 写入redis, 超时时回复 - redisCatchStorage.updateSendRTPSever(sendRtpItem); - playService.download(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start), - DateUtil.formatter.format(end), Integer.parseInt(downloadSpeed), - (code, msg, data) -> { - if (code == InviteErrorCode.SUCCESS.getCode()) { - hookEvent.run(code, msg, data); - } else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()) { - logger.info("[录像下载]超时, 用户:{}, 通道:{}", username, channelId); - redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null); - errorEvent.run(code, msg, data); - } else { - errorEvent.run(code, msg, data); - } - }); - } else { - sendRtpItem.setPlayType(InviteStreamType.PLAY); - String streamId = String.format("%s_%s", device.getDeviceId(), channelId); - sendRtpItem.setStream(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); - } else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()) { - logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, channelId); - redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null); - errorEvent.run(code, msg, data); - } else { - errorEvent.run(code, msg, data); - } - })); - sendRtpItem.setSsrc(ssrcInfo.getSsrc()); - redisCatchStorage.updateSendRTPSever(sendRtpItem); - - } - } else if (gbStream != null) { - SendRtpItem sendRtpItem = new SendRtpItem(); - if (!userSetting.getUseCustomSsrcForParentInvite() && gb28181Sdp.getSsrc() != null) { - sendRtpItem.setSsrc(gb28181Sdp.getSsrc()); - } - - if (tcpActive != null) { - sendRtpItem.setTcpActive(tcpActive); - } - sendRtpItem.setTcp(mediaTransmissionTCP); - sendRtpItem.setRtcp(platform.isRtcp()); - sendRtpItem.setPlatformName(platform.getName()); - sendRtpItem.setPlatformId(platform.getServerGBId()); - sendRtpItem.setMediaServerId(mediaServerItem.getId()); - sendRtpItem.setChannelId(channelId); - sendRtpItem.setIp(addressStr); - sendRtpItem.setPort(port); - sendRtpItem.setUsePs(true); - sendRtpItem.setApp(gbStream.getApp()); - sendRtpItem.setStream(gbStream.getStream()); - sendRtpItem.setCallId(callIdHeader.getCallId()); - sendRtpItem.setFromTag(request.getFromTag()); - sendRtpItem.setOnlyAudio(false); - sendRtpItem.setStatus(0); - sendRtpItem.setSessionName(sessionName); - // 清理可能存在的缓存避免用到旧的数据 - List sendRtpItemList = redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, gbStream.getStream()); - if (!sendRtpItemList.isEmpty()) { - for (SendRtpItem rtpItem : sendRtpItemList) { - redisCatchStorage.deleteSendRTPServer(rtpItem); - } - } - if ("push".equals(gbStream.getStreamType())) { - sendRtpItem.setPlayType(InviteStreamType.PUSH); - if (streamPushItem != null) { - // 从redis查询是否正在接收这个推流 - OnStreamChangedHookParam pushListItem = redisCatchStorage.getPushListItem(gbStream.getApp(), gbStream.getStream()); - if (pushListItem != null) { - sendRtpItem.setServerId(pushListItem.getSeverId()); - sendRtpItem.setMediaServerId(pushListItem.getMediaServerId()); - - redisCatchStorage.updateSendRTPSever(sendRtpItem); - // 开始推流 - sendPushStream(sendRtpItem, mediaServerItem, platform, request); - }else { - if (!platform.isStartOfflinePush()) { - // 平台设置中关闭了拉起离线的推流则直接回复 - try { - logger.info("[上级点播] 失败,推流设备未推流,channel: {}, app: {}, stream: {}", sendRtpItem.getChannelId(), sendRtpItem.getApp(), sendRtpItem.getStream()); - responseAck(request, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing"); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage()); - } - return; + // tcp主动模式,回复sdp后开启监听 + if (sendRtpItem.isTcpActive()) { + MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + try { + mediaServerService.startSendRtpPassive(mediaServer, sendRtpItem, 5); + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(sendRtpItem.getChannelId()); + if (deviceChannel != null) { + redisCatchStorage.sendPlatformStartPlayMsg(sendRtpItem, deviceChannel, platform); } - notifyPushStreamOnline(sendRtpItem, mediaServerItem, platform, request); - } - } - } else if ("proxy".equals(gbStream.getStreamType())) { - if (null != proxyByAppAndStream) { - sendRtpItem.setServerId(userSetting.getServerId()); - if (sendRtpItem.getSsrc() == null) { - // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 - String ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); - sendRtpItem.setSsrc(ssrc); - } - MediaInfo mediaInfo = redisCatchStorage.getProxyStream(gbStream.getApp(), gbStream.getStream()); - if (mediaInfo != null) { - sendProxyStream(sendRtpItem, mediaServerItem, platform, request); - } else { - //开启代理拉流 - notifyProxyStreamOnline(sendRtpItem, mediaServerItem, platform, request); + }catch (ControllerException e) { + log.warn("[上级Invite] tcp主动模式 发流失败", e); + sendBye(platform, inviteInfo.getCallId()); } } + } - } + })); } - } catch (SdpParseException e) { - logger.error("sdp解析错误", e); } catch (SdpException e) { - logger.error("未处理的异常 ", e); - } - } - - private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform, - JSONObject jsonObject, Map param, CallIdHeader callIdHeader) { - if (jsonObject == null) { - logger.error("下级TCP被动启动监听失败: 请检查ZLM服务"); - } else if (jsonObject.getInteger("code") == 0) { - logger.info("调用ZLM-TCP被动推流接口, 结果: {}", jsonObject); - logger.info("启动监听TCP被动推流成功[ {}/{} ],{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port")); - } else { - logger.error("启动监听TCP被动推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(param)); - } - } - - /** - * 安排推流 - */ - private void sendProxyStream(SendRtpItem sendRtpItem, MediaServer mediaServerItem, ParentPlatform platform, SIPRequest request) { - MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStream()); - - if (mediaInfo != null) { - - // 自平台内容 - int localPort = sendRtpPortManager.getNextPort(mediaServerItem); - if (localPort == 0) { - logger.warn("服务器端口资源不足"); - try { - responseAck(request, Response.BUSY_HERE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); - } - return; - } - sendRtpItem.setPlayType(InviteStreamType.PROXY); - // 写入redis, 超时时回复 - sendRtpItem.setStatus(1); - sendRtpItem.setLocalIp(mediaServerItem.getSdpIp()); - - SIPResponse response = sendStreamAck(request, sendRtpItem, platform); - if (response != null) { - sendRtpItem.setToTag(response.getToTag()); - } - redisCatchStorage.updateSendRTPSever(sendRtpItem); - } - } - - private void sendPushStream(SendRtpItem sendRtpItem, MediaServer mediaServerItem, ParentPlatform platform, SIPRequest request) { - // 推流 - if (sendRtpItem.getServerId().equals(userSetting.getServerId())) { - MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStream()); - if (mediaInfo != null ) { - // 自平台内容 - int localPort = sendRtpPortManager.getNextPort(mediaServerItem); - if (localPort == 0) { - logger.warn("服务器端口资源不足"); - try { - responseAck(request, Response.BUSY_HERE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); - } - return; - } - // 写入redis, 超时时回复 - sendRtpItem.setStatus(1); - SIPResponse response = sendStreamAck(request, sendRtpItem, platform); - if (response != null) { - sendRtpItem.setToTag(response.getToTag()); - } - if (sendRtpItem.getSsrc() == null) { - // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 - String ssrc = "Play".equalsIgnoreCase(sendRtpItem.getSessionName()) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); - sendRtpItem.setSsrc(ssrc); - } - redisCatchStorage.updateSendRTPSever(sendRtpItem); - } else { - // 不在线 拉起 - notifyPushStreamOnline(sendRtpItem, mediaServerItem, platform, request); - } - } else { - // 其他平台内容 - otherWvpPushStream(sendRtpItem, request, platform); - } - } - - /** - * 通知流上线 - */ - private void notifyProxyStreamOnline(SendRtpItem sendRtpItem, MediaServer mediaServerItem, ParentPlatform platform, SIPRequest request) { - // TODO 控制启用以使设备上线 - logger.info("[ app={}, stream={} ]通道未推流,启用流后开始推流", sendRtpItem.getApp(), sendRtpItem.getStream()); - // 监听流上线 - Hook hook = Hook.getInstance(HookType.on_media_arrival, sendRtpItem.getApp(), sendRtpItem.getStream(), mediaServerItem.getId()); - hookSubscribe.addSubscribe(hook, (hookData)->{ - logger.info("[上级点播]拉流代理已经就绪, {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream()); - dynamicTask.stop(sendRtpItem.getCallId()); - sendProxyStream(sendRtpItem, mediaServerItem, platform, request); - }); - dynamicTask.startDelay(sendRtpItem.getCallId(), () -> { - logger.info("[ app={}, stream={} ] 等待拉流代理流超时", sendRtpItem.getApp(), sendRtpItem.getStream()); - hookSubscribe.removeSubscribe(hook); - }, userSetting.getPlatformPlayTimeout()); - boolean start = streamProxyService.start(sendRtpItem.getApp(), sendRtpItem.getStream()); - if (!start) { + // 参数不全, 发400,请求错误 try { - responseAck(request, Response.BUSY_HERE, "channel [" + sendRtpItem.getChannelId() + "] offline"); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage()); + responseAck(request, Response.BAD_REQUEST); + } catch (SipException | InvalidArgumentException | ParseException sendException) { + log.error("[命令发送失败] invite BAD_REQUEST: {}", sendException.getMessage()); } - hookSubscribe.removeSubscribe(hook); - dynamicTask.stop(sendRtpItem.getCallId()); - } - } - - /** - * 通知流上线 - */ - private void notifyPushStreamOnline(SendRtpItem sendRtpItem, MediaServer mediaServerItem, ParentPlatform platform, SIPRequest request) { - // 发送redis消息以使设备上线,流上线后被 - logger.info("[ app={}, stream={} ]通道未推流,发送redis信息控制设备开始推流", sendRtpItem.getApp(), sendRtpItem.getStream()); - MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, - sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(), sendRtpItem.getPlatformId(), - platform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId()); - redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); - // 设置超时 - dynamicTask.startDelay(sendRtpItem.getCallId(), () -> { - redisRpcService.stopWaitePushStreamOnline(sendRtpItem); - logger.info("[ app={}, stream={} ] 等待设备开始推流超时", sendRtpItem.getApp(), sendRtpItem.getStream()); + } catch (InviteDecodeException e) { try { - responseAck(request, Response.REQUEST_TIMEOUT); // 超时 - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("未处理的异常 ", e); + responseAck(request, e.getCode(), e.getMsg()); + } catch (SipException | InvalidArgumentException | ParseException sendException) { + log.error("[命令发送失败] invite BAD_REQUEST: {}", sendException.getMessage()); } - }, userSetting.getPlatformPlayTimeout()); - // - long key = redisRpcService.waitePushStreamOnline(sendRtpItem, (sendRtpItemKey) -> { - dynamicTask.stop(sendRtpItem.getCallId()); - if (sendRtpItemKey == null) { - logger.warn("[级联点播] 等待推流得到结果未空: {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream()); - try { - responseAck(request, Response.BUSY_HERE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("未处理的异常 ", e); - } - return; + }catch (PlayException e) { + try { + responseAck(request, e.getCode(), e.getMsg()); + } catch (SipException | InvalidArgumentException | ParseException sendException) { + log.error("[命令发送失败] invite 点播失败: {}", sendException.getMessage()); } - SendRtpItem sendRtpItemFromRedis = (SendRtpItem)redisTemplate.opsForValue().get(sendRtpItemKey); - if (sendRtpItemFromRedis == null) { - logger.warn("[级联点播] 等待推流, 未找到redis中缓存的发流信息: {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream()); - try { - responseAck(request, Response.BUSY_HERE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("未处理的异常 ", e); - } - return; - } - if (sendRtpItemFromRedis.getServerId().equals(userSetting.getServerId())) { - logger.info("[级联点播] 等待的推流在本平台上线 {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream()); - int localPort = sendRtpPortManager.getNextPort(mediaServerItem); - if (localPort == 0) { - logger.warn("上级点时创建sendRTPItem失败,可能是服务器端口资源不足"); - try { - responseAck(request, Response.BUSY_HERE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("未处理的异常 ", e); + } + } + + private InviteInfo decode(RequestEvent evt) throws SdpException { + + InviteInfo inviteInfo = new InviteInfo(); + SIPRequest request = (SIPRequest)evt.getRequest(); + String[] channelIdArrayFromSub = SipUtils.getChannelIdFromRequest(request); + + // 解析sdp消息, 使用jainsip 自带的sdp解析方式 + String contentString = new String(request.getRawContent()); + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); + SessionDescription sdp = gb28181Sdp.getBaseSdb(); + String sessionName = sdp.getSessionName().getValue(); + String channelIdFromSdp = null; + if(StringUtils.equalsIgnoreCase("Playback", sessionName)){ + URIField uriField = (URIField)sdp.getURI(); + channelIdFromSdp = uriField.getURI().split(":")[0]; + } + final String channelId = StringUtils.isNotBlank(channelIdFromSdp) ? channelIdFromSdp : + (channelIdArrayFromSub != null? channelIdArrayFromSub[0]: null); + String requesterId = SipUtils.getUserIdFromFromHeader(request); + CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); + if (requesterId == null || channelId == null) { + log.warn("[解析INVITE消息] 无法从请求中获取到来源id,返回400错误"); + throw new InviteDecodeException(Response.BAD_REQUEST, "request decode fail"); + } + log.info("[INVITE] 来源ID: {}, callId: {}, 来自:{}:{}", + requesterId, callIdHeader.getCallId(), request.getRemoteAddress(), request.getRemotePort()); + inviteInfo.setRequesterId(requesterId); + inviteInfo.setTargetChannelId(channelId); + if (channelIdArrayFromSub != null && channelIdArrayFromSub.length == 2) { + inviteInfo.setSourceChannelId(channelIdArrayFromSub[1]); + } + inviteInfo.setSessionName(sessionName); + inviteInfo.setSsrc(gb28181Sdp.getSsrc()); + inviteInfo.setCallId(callIdHeader.getCallId()); + + // 如果是录像回放,则会存在录像的开始时间与结束时间 + Long startTime = null; + Long stopTime = null; + Instant start = null; + Instant end = null; + if (sdp.getTimeDescriptions(false) != null && sdp.getTimeDescriptions(false).size() > 0) { + TimeDescriptionImpl timeDescription = (TimeDescriptionImpl) (sdp.getTimeDescriptions(false).get(0)); + TimeField startTimeFiled = (TimeField) timeDescription.getTime(); + startTime = startTimeFiled.getStartTime(); + stopTime = startTimeFiled.getStopTime(); + + start = Instant.ofEpochSecond(startTime); + end = Instant.ofEpochSecond(stopTime); + } + // 获取支持的格式 + Vector mediaDescriptions = sdp.getMediaDescriptions(true); + // 查看是否支持PS 负载96 + //String ip = null; + int port = -1; + boolean mediaTransmissionTCP = false; + Boolean tcpActive = null; + for (Object description : mediaDescriptions) { + MediaDescription mediaDescription = (MediaDescription) description; + Media media = mediaDescription.getMedia(); + + Vector mediaFormats = media.getMediaFormats(false); + if (mediaFormats.contains("96")) { + port = media.getMediaPort(); + //String mediaType = media.getMediaType(); + String protocol = media.getProtocol(); + + // 区分TCP发流还是udp, 当前默认udp + if ("TCP/RTP/AVP".equalsIgnoreCase(protocol)) { + String setup = mediaDescription.getAttribute("setup"); + if (setup != null) { + mediaTransmissionTCP = true; + if ("active".equalsIgnoreCase(setup)) { + tcpActive = true; + } else if ("passive".equalsIgnoreCase(setup)) { + tcpActive = false; + } } - return; - } - sendRtpItem.setLocalPort(localPort); - if (!ObjectUtils.isEmpty(platform.getSendStreamIp())) { - sendRtpItem.setLocalIp(platform.getSendStreamIp()); } + break; + } + } + if (port == -1) { + log.info("[解析INVITE消息] 不支持的媒体格式,返回415"); + throw new InviteDecodeException(Response.UNSUPPORTED_MEDIA_TYPE, "unsupported media type"); + } + inviteInfo.setTcp(mediaTransmissionTCP); + inviteInfo.setTcpActive(tcpActive != null? tcpActive: false); + inviteInfo.setStartTime(startTime); + inviteInfo.setStopTime(stopTime); + String username = sdp.getOrigin().getUsername(); +// String addressStr; +// if(StringUtils.isEmpty(platform.getSendStreamIp())){ +// addressStr = sdp.getConnection().getAddress(); +// }else { +// addressStr = platform.getSendStreamIp(); +// } + + Vector sdpMediaDescriptions = sdp.getMediaDescriptions(true); + MediaDescription mediaDescription = null; + String downloadSpeed = "1"; + if (!sdpMediaDescriptions.isEmpty()) { + mediaDescription = (MediaDescription) sdpMediaDescriptions.get(0); + } + if (mediaDescription != null) { + downloadSpeed = mediaDescription.getAttribute("downloadspeed"); + } + inviteInfo.setIp(sdp.getConnection().getAddress()); + inviteInfo.setPort(port); + inviteInfo.setDownloadSpeed(downloadSpeed); + + return inviteInfo; - // 写入redis, 超时时回复 - sendRtpItem.setStatus(1); - SIPResponse response = sendStreamAck(request, sendRtpItem, platform); - if (response != null) { - sendRtpItem.setToTag(response.getToTag()); - } - redisCatchStorage.updateSendRTPSever(sendRtpItem); - } else { - // 其他平台内容 - otherWvpPushStream(sendRtpItemFromRedis, request, platform); - } - }); - // 添加回复的拒绝或者错误的通知 - // redis消息例如: PUBLISH VM_MSG_STREAM_PUSH_RESPONSE '{"code":1,"msg":"失败","app":"1","stream":"2"}' - redisPushStreamResponseListener.addEvent(sendRtpItem.getApp(), sendRtpItem.getStream(), response -> { - if (response.getCode() != 0) { - dynamicTask.stop(sendRtpItem.getCallId()); - redisRpcService.stopWaitePushStreamOnline(sendRtpItem); - redisRpcService.removeCallback(key); - try { - responseAck(request, Response.TEMPORARILY_UNAVAILABLE, response.getMsg()); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 点播回复: {}", e.getMessage()); - } - } - }); } - - - /** - * 来自其他wvp的推流 - */ - private void otherWvpPushStream(SendRtpItem sendRtpItem, SIPRequest request, ParentPlatform platform) { - logger.info("[级联点播] 来自其他wvp的推流 {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream()); - sendRtpItem = redisRpcService.getSendRtpItem(sendRtpItem.getRedisKey()); - if (sendRtpItem == null) { - return; - } - // 写入redis, 超时时回复 - sendRtpItem.setStatus(1); - SIPResponse response = sendStreamAck(request, sendRtpItem, platform); - if (response != null) { - sendRtpItem.setToTag(response.getToTag()); - } - redisCatchStorage.updateSendRTPSever(sendRtpItem); - } - - public SIPResponse sendStreamAck(SIPRequest request, SendRtpItem sendRtpItem, ParentPlatform platform) { - - String sdpIp = sendRtpItem.getLocalIp(); - if (!ObjectUtils.isEmpty(platform.getSendStreamIp())) { - sdpIp = platform.getSendStreamIp(); - } - StringBuffer content = new StringBuffer(200); + private String createSendSdp(SendRtpInfo sendRtpItem, InviteInfo inviteInfo, String sdpIp) { + StringBuilder content = new StringBuilder(200); content.append("v=0\r\n"); - content.append("o=" + sendRtpItem.getChannelId() + " 0 0 IN IP4 " + sdpIp + "\r\n"); - content.append("s=Play\r\n"); + content.append("o=" + inviteInfo.getTargetChannelId() + " 0 0 IN IP4 " + sdpIp + "\r\n"); + content.append("s=" + inviteInfo.getSessionName() + "\r\n"); content.append("c=IN IP4 " + sdpIp + "\r\n"); - content.append("t=0 0\r\n"); - // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口 - int localPort = sendRtpItem.getLocalPort(); - if (localPort == 0) { - localPort = new Random().nextInt(65535) + 1; + if ("Playback".equalsIgnoreCase(inviteInfo.getSessionName())) { + content.append("t=" + inviteInfo.getStartTime() + " " + inviteInfo.getStopTime() + "\r\n"); + } else { + content.append("t=0 0\r\n"); } - 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"); if (sendRtpItem.isTcp()) { - content.append("a=connection:new\r\n"); + content.append("m=video " + sendRtpItem.getLocalPort() + " 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 " + sendRtpItem.getLocalPort() + " 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"); content.append("f=\r\n"); - - try { - return responseSdpAck(request, content.toString(), platform); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("未处理的异常 ", e); - } - return null; + return content.toString(); } - public void inviteFromDeviceHandle(SIPRequest request, String requesterId, String channelId) { + private void sendBye(Platform platform, String callId) { + try { + SendRtpInfo sendRtpItem = sendRtpServerService.queryByCallId(callId); + if (sendRtpItem == null) { + return; + } + CommonGBChannel channel = channelService.getOne(sendRtpItem.getChannelId()); + if (channel == null) { + return; + } + cmderFroPlatform.streamByeCmd(platform, sendRtpItem, channel); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 上级Invite 发送BYE: {}", e.getMessage()); + } + } - String realChannelId = null; + public void inviteFromDeviceHandle(SIPRequest request, InviteInfo inviteInfo) { + if (inviteInfo.getSourceChannelId() == null) { + log.warn("来自设备的Invite请求,无法从请求信息中确定请求来自的通道,已忽略,requesterId: {}", inviteInfo.getRequesterId()); + try { + responseAck(request, Response.FORBIDDEN); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 来自设备的Invite请求,无法从请求信息中确定所属设备 FORBIDDEN: {}", e.getMessage()); + } + return; + } // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备) - Device device = redisCatchStorage.getDevice(requesterId); + Device device = redisCatchStorage.getDevice(inviteInfo.getRequesterId()); // 判断requesterId是设备还是通道 if (device == null) { - device = storager.queryVideoDeviceByChannelId(requesterId); - realChannelId = requesterId; - }else { - realChannelId = channelId; + device = deviceService.getDeviceBySourceChannelDeviceId(inviteInfo.getRequesterId()); } if (device == null) { // 检查channelID是否可用 - device = redisCatchStorage.getDevice(channelId); - if (device == null) { - device = storager.queryVideoDeviceByChannelId(channelId); - realChannelId = channelId; - } + device = deviceService.getDeviceBySourceChannelDeviceId(inviteInfo.getSourceChannelId()); } if (device == null) { - logger.warn("来自设备的Invite请求,无法从请求信息中确定所属设备,已忽略,requesterId: {}/{}", requesterId, channelId); + log.warn("来自设备的Invite请求,无法从请求信息中确定所属设备,已忽略,requesterId: {}/{}", inviteInfo.getRequesterId(), + inviteInfo.getSourceChannelId()); try { responseAck(request, Response.FORBIDDEN); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 来自设备的Invite请求,无法从请求信息中确定所属设备 FORBIDDEN: {}", e.getMessage()); + log.error("[命令发送失败] 来自设备的Invite请求,无法从请求信息中确定所属设备 FORBIDDEN: {}", e.getMessage()); } return; } - - AudioBroadcastCatch broadcastCatch = audioBroadcastManager.get(device.getDeviceId(), realChannelId); + DeviceChannel deviceChannel = deviceChannelService.getOne(device.getDeviceId(), inviteInfo.getSourceChannelId()); + if (deviceChannel == null) { + List audioBroadcastCatchList = audioBroadcastManager.getByDeviceId(device.getDeviceId()); + if (audioBroadcastCatchList.isEmpty()) { + log.warn("来自设备的Invite请求,无法从请求信息中确定所属通道,已忽略,requesterId: {}/{}", inviteInfo.getRequesterId(), inviteInfo.getSourceChannelId()); + try { + responseAck(request, Response.FORBIDDEN); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 来自设备的Invite请求,无法从请求信息中确定所属设备 FORBIDDEN: {}", e.getMessage()); + } + return; + }else { + deviceChannel = deviceChannelService.getOneForSourceById(audioBroadcastCatchList.get(0).getChannelId()); + } + } + AudioBroadcastCatch broadcastCatch = audioBroadcastManager.get(deviceChannel.getId()); if (broadcastCatch == null) { - logger.warn("来自设备的Invite请求非语音广播,已忽略,requesterId: {}/{}", requesterId, channelId); + log.warn("来自设备的Invite请求非语音广播,已忽略,requesterId: {}/{}", inviteInfo.getRequesterId(), inviteInfo.getSourceChannelId()); try { responseAck(request, Response.FORBIDDEN); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 来自设备的Invite请求非语音广播 FORBIDDEN: {}", e.getMessage()); + log.error("[命令发送失败] 来自设备的Invite请求非语音广播 FORBIDDEN: {}", e.getMessage()); } return; } - if (device != null) { - logger.info("收到设备" + requesterId + "的语音广播Invite请求"); - String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId(); - if (!SipUtils.isFrontEnd(device.getDeviceId())) { - key += broadcastCatch.getChannelId(); + log.info("收到设备" + inviteInfo.getRequesterId() + "的语音广播Invite请求"); + String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId(); + if (!SipUtils.isFrontEnd(device.getDeviceId())) { + key += broadcastCatch.getChannelId(); + } + dynamicTask.stop(key); + try { + responseAck(request, Response.TRYING); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage()); + playService.stopAudioBroadcast(device, deviceChannel); + return; + } + String contentString = new String(request.getRawContent()); + + try { + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); + SessionDescription sdp = gb28181Sdp.getBaseSdb(); + // 获取支持的格式 + Vector mediaDescriptions = sdp.getMediaDescriptions(true); + + // 查看是否支持PS 负载96 + int port = -1; + boolean mediaTransmissionTCP = false; + Boolean tcpActive = null; + for (int i = 0; i < mediaDescriptions.size(); i++) { + MediaDescription mediaDescription = (MediaDescription) mediaDescriptions.get(i); + Media media = mediaDescription.getMedia(); + + Vector mediaFormats = media.getMediaFormats(false); +// if (mediaFormats.contains("8")) { + port = media.getMediaPort(); + String protocol = media.getProtocol(); + // 区分TCP发流还是udp, 当前默认udp + if ("TCP/RTP/AVP".equals(protocol)) { + String setup = mediaDescription.getAttribute("setup"); + if (setup != null) { + mediaTransmissionTCP = true; + if ("active".equals(setup)) { + tcpActive = true; + } else if ("passive".equals(setup)) { + tcpActive = false; + } + } + } + break; +// } } - dynamicTask.stop(key); - try { - responseAck(request, Response.TRYING); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage()); - playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); + if (port == -1) { + log.info("不支持的媒体格式,返回415"); + // 回复不支持的格式 + try { + responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] invite 不支持的媒体格式: {}", e.getMessage()); + playService.stopAudioBroadcast(device, deviceChannel); + return; + } return; } - String contentString = new String(request.getRawContent()); + String addressStr = sdp.getOrigin().getAddress(); + log.info("设备{}请求语音流,地址:{}:{},ssrc:{}, {}", inviteInfo.getRequesterId(), addressStr, port, gb28181Sdp.getSsrc(), + mediaTransmissionTCP ? (tcpActive ? "TCP主动" : "TCP被动") : "UDP"); - try { - Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); - SessionDescription sdp = gb28181Sdp.getBaseSdb(); - // 获取支持的格式 - Vector mediaDescriptions = sdp.getMediaDescriptions(true); - - // 查看是否支持PS 负载96 - int port = -1; - boolean mediaTransmissionTCP = false; - Boolean tcpActive = null; - for (int i = 0; i < mediaDescriptions.size(); i++) { - MediaDescription mediaDescription = (MediaDescription) mediaDescriptions.get(i); - Media media = mediaDescription.getMedia(); - - Vector mediaFormats = media.getMediaFormats(false); -// if (mediaFormats.contains("8")) { - port = media.getMediaPort(); - String protocol = media.getProtocol(); - // 区分TCP发流还是udp, 当前默认udp - if ("TCP/RTP/AVP".equals(protocol)) { - String setup = mediaDescription.getAttribute("setup"); - if (setup != null) { - mediaTransmissionTCP = true; - if ("active".equals(setup)) { - tcpActive = true; - } else if ("passive".equals(setup)) { - tcpActive = false; - } - } - } - break; -// } + MediaServer mediaServerItem = broadcastCatch.getMediaServerItem(); + if (mediaServerItem == null) { + log.warn("未找到语音喊话使用的zlm"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] invite 未找到可用的zlm: {}", e.getMessage()); + playService.stopAudioBroadcast(device, deviceChannel); } - if (port == -1) { - logger.info("不支持的媒体格式,返回415"); - // 回复不支持的格式 - try { - responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite 不支持的媒体格式: {}", e.getMessage()); - playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); - return; - } - return; - } - String addressStr = sdp.getOrigin().getAddress(); - logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}, {}", requesterId, addressStr, port, gb28181Sdp.getSsrc(), - mediaTransmissionTCP ? (tcpActive ? "TCP主动" : "TCP被动") : "UDP"); - - MediaServer mediaServerItem = broadcastCatch.getMediaServerItem(); - if (mediaServerItem == null) { - logger.warn("未找到语音喊话使用的zlm"); - try { - responseAck(request, Response.BUSY_HERE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite 未找到可用的zlm: {}", e.getMessage()); - playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); - } - return; - } - logger.info("设备{}请求语音流, 收流地址:{}:{},ssrc:{}, {}, 对讲方式:{}", requesterId, addressStr, port, gb28181Sdp.getSsrc(), - mediaTransmissionTCP ? (tcpActive ? "TCP主动" : "TCP被动") : "UDP", sdp.getSessionName().getValue()); - CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); - - SendRtpItem sendRtpItem = mediaServerService.createSendRtpItem(mediaServerItem, addressStr, port, gb28181Sdp.getSsrc(), requesterId, - device.getDeviceId(), broadcastCatch.getChannelId(), - mediaTransmissionTCP, false); - - if (sendRtpItem == null) { - logger.warn("服务器端口资源不足"); - try { - responseAck(request, Response.BUSY_HERE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); - playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); - return; - } - return; - } - - - sendRtpItem.setPlayType(InviteStreamType.BROADCAST); - sendRtpItem.setCallId(callIdHeader.getCallId()); - sendRtpItem.setPlatformId(requesterId); - sendRtpItem.setStatus(1); - sendRtpItem.setApp(broadcastCatch.getApp()); - sendRtpItem.setStream(broadcastCatch.getStream()); - sendRtpItem.setPt(8); - sendRtpItem.setUsePs(false); - sendRtpItem.setRtcp(false); - sendRtpItem.setOnlyAudio(true); - sendRtpItem.setTcp(mediaTransmissionTCP); - if (tcpActive != null) { - sendRtpItem.setTcpActive(tcpActive); - } - - redisCatchStorage.updateSendRTPSever(sendRtpItem); - - Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, broadcastCatch.getApp(), broadcastCatch.getStream()); - if (streamReady) { - sendOk(device, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, gb28181Sdp.getSsrc()); - } else { - logger.warn("[语音通话], 未发现待推送的流,app={},stream={}", broadcastCatch.getApp(), broadcastCatch.getStream()); - try { - responseAck(request, Response.GONE); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 语音通话 回复410失败, {}", e.getMessage()); - return; - } - playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); - } - } catch (SdpException e) { - logger.error("[SDP解析异常]", e); - playService.stopAudioBroadcast(device.getDeviceId(), broadcastCatch.getChannelId()); + return; } - } else { - logger.warn("来自无效设备/平台的请求"); - try { - responseAck(request, Response.BAD_REQUEST); - ; // 不支持的格式,发415 - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] invite 来自无效设备/平台的请求, {}", e.getMessage()); + log.info("设备{}请求语音流, 收流地址:{}:{},ssrc:{}, {}, 对讲方式:{}", inviteInfo.getRequesterId(), addressStr, port, gb28181Sdp.getSsrc(), + mediaTransmissionTCP ? (tcpActive ? "TCP主动" : "TCP被动") : "UDP", sdp.getSessionName().getValue()); + CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); + + SendRtpInfo sendRtpItem = sendRtpServerService.createSendRtpInfo(mediaServerItem, addressStr, port, gb28181Sdp.getSsrc(), inviteInfo.getRequesterId(), + device.getDeviceId(), deviceChannel.getId(), + mediaTransmissionTCP, false); + + if (sendRtpItem == null) { + log.warn("服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); + playService.stopAudioBroadcast(device, deviceChannel); + return; + } + return; } + + sendRtpItem.setPlayType(InviteStreamType.BROADCAST); + sendRtpItem.setCallId(callIdHeader.getCallId()); + sendRtpItem.setStatus(1); + sendRtpItem.setApp(broadcastCatch.getApp()); + sendRtpItem.setStream(broadcastCatch.getStream()); + sendRtpItem.setPt(8); + sendRtpItem.setUsePs(false); + sendRtpItem.setRtcp(false); + sendRtpItem.setOnlyAudio(true); + sendRtpItem.setTcp(mediaTransmissionTCP); + if (tcpActive != null) { + sendRtpItem.setTcpActive(tcpActive); + } + + sendRtpServerService.update(sendRtpItem); + + Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, broadcastCatch.getApp(), broadcastCatch.getStream()); + if (streamReady) { + sendOk(device, deviceChannel, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, gb28181Sdp.getSsrc()); + } else { + log.warn("[语音通话], 未发现待推送的流,app={},stream={}", broadcastCatch.getApp(), broadcastCatch.getStream()); + try { + responseAck(request, Response.GONE); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 语音通话 回复410失败, {}", e.getMessage()); + return; + } + playService.stopAudioBroadcast(device, deviceChannel); + } + } catch (SdpException e) { + log.error("[SDP解析异常]", e); + playService.stopAudioBroadcast(device, deviceChannel); } } - SIPResponse sendOk(Device device, SendRtpItem sendRtpItem, SessionDescription sdp, SIPRequest request, MediaServer mediaServerItem, boolean mediaTransmissionTCP, String ssrc) { + SIPResponse sendOk(Device device, DeviceChannel channel, SendRtpInfo sendRtpItem, SessionDescription sdp, SIPRequest request, MediaServer mediaServerItem, boolean mediaTransmissionTCP, String ssrc) { SIPResponse sipResponse = null; try { sendRtpItem.setStatus(2); - redisCatchStorage.updateSendRTPSever(sendRtpItem); + sendRtpServerService.update(sendRtpItem); StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); content.append("o=" + config.getId() + " " + sdp.getOrigin().getSessionId() + " " + sdp.getOrigin().getSessionVersion() + " IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); @@ -1115,27 +626,28 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements content.append("y=" + ssrc + "\r\n"); content.append("f=v/////a/1/8/1\r\n"); - ParentPlatform parentPlatform = new ParentPlatform(); - parentPlatform.setServerIP(device.getIp()); + Platform parentPlatform = new Platform(); + parentPlatform.setServerIp(device.getIp()); parentPlatform.setServerPort(device.getPort()); parentPlatform.setServerGBId(device.getDeviceId()); sipResponse = responseSdpAck(request, content.toString(), parentPlatform); - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(device.getDeviceId(), sendRtpItem.getChannelId()); + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getChannelId()); audioBroadcastCatch.setStatus(AudioBroadcastCatchStatus.Ok); - audioBroadcastCatch.setSipTransactionInfoByRequset(sipResponse); + audioBroadcastCatch.setSipTransactionInfoByRequest(sipResponse); audioBroadcastManager.update(audioBroadcastCatch); - streamSession.put(device.getDeviceId(), sendRtpItem.getChannelId(), request.getCallIdHeader().getCallId(), sendRtpItem.getStream(), sendRtpItem.getSsrc(), sendRtpItem.getMediaServerId(), sipResponse, InviteSessionType.BROADCAST); + SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), sendRtpItem.getChannelId(), request.getCallIdHeader().getCallId(), sendRtpItem.getStream(), sendRtpItem.getSsrc(), sendRtpItem.getMediaServerId(), sipResponse, InviteSessionType.BROADCAST); + sessionManager.put(ssrcTransaction); // 开启发流,大华在收到200OK后就会开始建立连接 if (!device.isBroadcastPushAfterAck()) { - logger.info("[语音喊话] 回复200OK后发现 BroadcastPushAfterAck为False,现在开始推流"); - playService.startPushStream(sendRtpItem, sipResponse, parentPlatform, request.getCallIdHeader()); + log.info("[语音喊话] 回复200OK后发现 BroadcastPushAfterAck为False,现在开始推流"); + playService.startPushStream(sendRtpItem, channel, sipResponse, parentPlatform, request.getCallIdHeader()); } } catch (SipException | InvalidArgumentException | ParseException | SdpParseException e) { - logger.error("[命令发送失败] 语音喊话 回复200OK(SDP): {}", e.getMessage()); + log.error("[命令发送失败] 语音喊话 回复200OK(SDP): {}", e.getMessage()); } return sipResponse; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java index 155d5f73d..00f7fd25d 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java @@ -1,23 +1,17 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; -import com.genersoft.iot.vmp.conf.DynamicTask; -import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.HandlerCatchData; +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; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; 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 lombok.extern.slf4j.Slf4j; import org.dom4j.DocumentException; import org.dom4j.Element; -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; @@ -25,29 +19,21 @@ import org.springframework.transaction.annotation.Transactional; import javax.sip.RequestEvent; import javax.sip.header.FromHeader; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CopyOnWriteArrayList; /** * SIP命令类型: NOTIFY请求中的目录请求处理 */ +@Slf4j @Component public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent { - - private final static Logger logger = LoggerFactory.getLogger(NotifyRequestForCatalogProcessor.class); - - private final List updateChannelOnlineList = new CopyOnWriteArrayList<>(); - private final List updateChannelOfflineList = new CopyOnWriteArrayList<>(); - private final Map updateChannelMap = new ConcurrentHashMap<>(); - - private final Map addChannelMap = new ConcurrentHashMap<>(); - private final List deleteChannelList = new CopyOnWriteArrayList<>(); + private final ConcurrentLinkedQueue channelList = new ConcurrentLinkedQueue<>(); private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @@ -63,16 +49,14 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent @Autowired private IDeviceChannelService deviceChannelService; - @Autowired - private DynamicTask dynamicTask; +// @Scheduled(fixedRate = 2000) //每400毫秒执行一次 +// public void showSize(){ +// log.warn("[notify-目录订阅] 待处理消息数量: {}", taskQueue.size() ); +// } - @Autowired - private SipConfig sipConfig; - - @Transactional public void process(RequestEvent evt) { if (taskQueue.size() >= userSetting.getMaxNotifyCountQueue()) { - logger.error("[notify-目录订阅] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue()); + log.error("[notify-目录订阅] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue()); return; } taskQueue.offer(new HandlerCatchData(evt, null, null)); @@ -83,29 +67,40 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent if (taskQueue.isEmpty()) { return; } - for (HandlerCatchData take : taskQueue) { + List handlerCatchDataList = new ArrayList<>(); + int size = taskQueue.size(); + for (int i = 0; i < size; i++) { + HandlerCatchData poll = taskQueue.poll(); + if (poll != null) { + handlerCatchDataList.add(poll); + } + } + if (handlerCatchDataList.isEmpty()) { + return; + } + for (HandlerCatchData take : handlerCatchDataList) { if (take == null) { continue; } RequestEvent evt = take.getEvt(); try { - long start = System.currentTimeMillis(); FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader); Device device = redisCatchStorage.getDevice(deviceId); if (device == null || !device.isOnLine()) { - logger.warn("[收到目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId() : "")); - return; + log.warn("[收到目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId() : "")); + continue; } Element rootElement = getRootElement(evt, device.getCharset()); if (rootElement == null) { - logger.warn("[ 收到目录订阅 ] content cannot be null, {}", evt.getRequest()); - return; + log.warn("[ 收到目录订阅 ] content cannot be null, {}", evt.getRequest()); + continue; } Element deviceListElement = rootElement.element("DeviceList"); if (deviceListElement == null) { - return; + log.warn("[ 收到目录订阅 ] content cannot be null, {}", evt.getRequest()); + continue; } Iterator deviceListIterator = deviceListElement.elementIterator(); if (deviceListIterator != null) { @@ -113,210 +108,176 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent // 遍历DeviceList while (deviceListIterator.hasNext()) { Element itemDevice = deviceListIterator.next(); - Element eventElement = itemDevice.element("Event"); - String event; - if (eventElement == null) { - logger.warn("[收到目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId() : "")); - event = CatalogEvent.ADD; - } else { - event = eventElement.getText().toUpperCase(); - } - DeviceChannel channel = XmlUtil.channelContentHandler(itemDevice, device, event); - if (channel == null) { - logger.info("[收到目录订阅]:但是解析失败 {}", new String(evt.getRequest().getRawContent())); + CatalogChannelEvent catalogChannelEvent = null; + try { + catalogChannelEvent = CatalogChannelEvent.decode(itemDevice); + if (catalogChannelEvent.getChannel() == null) { + log.info("[解析CatalogChannelEvent]成功:但是解析通道信息失败, 原文如下: \n{}", new String(evt.getRequest().getRawContent())); + continue; + } + catalogChannelEvent.getChannel().setDeviceDbId(device.getId()); + } catch (InvocationTargetException | NoSuchMethodException | InstantiationException | + IllegalAccessException e) { + log.error("[解析CatalogChannelEvent]失败,", e); + log.error("[解析CatalogChannelEvent]失败原文: \n{}", new String(evt.getRequest().getRawContent(), Charset.forName(device.getCharset()))); continue; + } + if (log.isDebugEnabled()){ + log.debug("[收到目录订阅]:{}/{}-{}", device.getDeviceId(), + catalogChannelEvent.getChannel().getDeviceId(), catalogChannelEvent.getEvent()); } - if (channel.getParentId() != null && channel.getParentId().equals(sipConfig.getId())) { - channel.setParentId(null); - } - channel.setDeviceId(device.getDeviceId()); - logger.info("[收到目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId()); - switch (event) { + DeviceChannel channel = catalogChannelEvent.getChannel(); + switch (catalogChannelEvent.getEvent()) { case CatalogEvent.ON: // 上线 - logger.info("[收到通道上线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); - updateChannelOnlineList.add(channel); + log.info("[收到通道上线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId()); + channel.setStatus("ON"); + channelList.add(NotifyCatalogChannel.getInstance(NotifyCatalogChannel.Type.STATUS_CHANGED, channel)); if (userSetting.getDeviceStatusNotify()) { // 发送redis消息 - redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), true); + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId(), true); } break; case CatalogEvent.OFF: // 离线 - logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + log.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId()); if (userSetting.getRefuseChannelStatusChannelFormNotify()) { - logger.info("[收到通道离线通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + log.info("[收到通道离线通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId()); } else { - updateChannelOfflineList.add(channel); + channel.setStatus("OFF"); + channelList.add(NotifyCatalogChannel.getInstance(NotifyCatalogChannel.Type.STATUS_CHANGED, channel)); if (userSetting.getDeviceStatusNotify()) { // 发送redis消息 - redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false); + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId(), false); } } break; case CatalogEvent.VLOST: // 视频丢失 - logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + log.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId()); if (userSetting.getRefuseChannelStatusChannelFormNotify()) { - logger.info("[收到通道视频丢失通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + log.info("[收到通道视频丢失通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId()); } else { - updateChannelOfflineList.add(channel); + channel.setStatus("OFF"); + channelList.add(NotifyCatalogChannel.getInstance(NotifyCatalogChannel.Type.STATUS_CHANGED, channel)); if (userSetting.getDeviceStatusNotify()) { // 发送redis消息 - redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false); + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId(), false); } } break; case CatalogEvent.DEFECT: // 故障 - logger.info("[收到通道视频故障通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + log.info("[收到通道视频故障通知] 来自设备: {}, 通道 {}", device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId()); if (userSetting.getRefuseChannelStatusChannelFormNotify()) { - logger.info("[收到通道视频故障通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + log.info("[收到通道视频故障通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId()); } else { - updateChannelOfflineList.add(channel); + channel.setStatus("OFF"); + channelList.add(NotifyCatalogChannel.getInstance(NotifyCatalogChannel.Type.STATUS_CHANGED, channel)); if (userSetting.getDeviceStatusNotify()) { // 发送redis消息 - redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false); + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId(), false); } } break; case CatalogEvent.ADD: // 增加 - logger.info("[收到增加通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + log.info("[收到增加通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId()); // 判断此通道是否存在 - DeviceChannel deviceChannel = deviceChannelService.getOne(deviceId, channel.getChannelId()); + DeviceChannel deviceChannel = deviceChannelService.getOneForSource(device.getId(), catalogChannelEvent.getChannel().getDeviceId()); if (deviceChannel != null) { - logger.info("[增加通道] 已存在,不发送通知只更新,设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + log.info("[增加通道] 已存在,不发送通知只更新,设备: {}, 通道 {}", device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId()); channel.setId(deviceChannel.getId()); - channel.setHasAudio(null); - updateChannelMap.put(channel.getChannelId(), channel); + channel.setHasAudio(deviceChannel.isHasAudio()); + channel.setUpdateTime(DateUtil.getNow()); + channelList.add(NotifyCatalogChannel.getInstance(NotifyCatalogChannel.Type.UPDATE, channel)); } else { - addChannelMap.put(channel.getChannelId(), channel); + catalogChannelEvent.getChannel().setUpdateTime(DateUtil.getNow()); + catalogChannelEvent.getChannel().setCreateTime(DateUtil.getNow()); + channelList.add(NotifyCatalogChannel.getInstance(NotifyCatalogChannel.Type.ADD, channel)); if (userSetting.getDeviceStatusNotify()) { // 发送redis消息 - redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), channel.getChannelId(), true); + redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId(), true); } } break; case CatalogEvent.DEL: // 删除 - logger.info("[收到删除通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); - deleteChannelList.add(channel); + log.info("[收到删除通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId()); + channelList.add(NotifyCatalogChannel.getInstance(NotifyCatalogChannel.Type.DELETE, channel)); if (userSetting.getDeviceStatusNotify()) { // 发送redis消息 - redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), channel.getChannelId(), false); + redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId(), false); } break; case CatalogEvent.UPDATE: // 更新 - logger.info("[收到更新通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + log.info("[收到更新通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId()); // 判断此通道是否存在 - DeviceChannel deviceChannelForUpdate = deviceChannelService.getOne(deviceId, channel.getChannelId()); + DeviceChannel deviceChannelForUpdate = deviceChannelService.getOneForSource(device.getId(), catalogChannelEvent.getChannel().getDeviceId()); if (deviceChannelForUpdate != null) { channel.setId(deviceChannelForUpdate.getId()); + channel.setHasAudio(deviceChannelForUpdate.isHasAudio()); channel.setUpdateTime(DateUtil.getNow()); - channel.setHasAudio(null); - updateChannelMap.put(channel.getChannelId(), channel); + channel.setUpdateTime(DateUtil.getNow()); + channelList.add(NotifyCatalogChannel.getInstance(NotifyCatalogChannel.Type.UPDATE, channel)); } else { - addChannelMap.put(channel.getChannelId(), channel); + catalogChannelEvent.getChannel().setCreateTime(DateUtil.getNow()); + catalogChannelEvent.getChannel().setUpdateTime(DateUtil.getNow()); + channelList.add(NotifyCatalogChannel.getInstance(NotifyCatalogChannel.Type.ADD, channel)); if (userSetting.getDeviceStatusNotify()) { // 发送redis消息 - redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), channel.getChannelId(), true); + redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), catalogChannelEvent.getChannel().getDeviceId(), true); } } break; default: - logger.warn("[ NotifyCatalog ] event not found : {}", event); + log.warn("[ NotifyCatalog ] event not found : {}", catalogChannelEvent.getEvent()); } // 转发变化信息 - eventPublisher.catalogEventPublish(null, channel, event); + eventPublisher.catalogEventPublish(null, catalogChannelEvent.getChannel(), catalogChannelEvent.getEvent()); } } } catch (DocumentException e) { - logger.error("未处理的异常 ", e); + log.error("未处理的异常 ", e); } } - taskQueue.clear(); - if (!updateChannelMap.keySet().isEmpty() - || !addChannelMap.keySet().isEmpty() - || !updateChannelOnlineList.isEmpty() - || !updateChannelOfflineList.isEmpty() - || !deleteChannelList.isEmpty()) { + if (!channelList.isEmpty()) { executeSave(); } } - public void executeSave(){ - try { - executeSaveForAdd(); - } catch (Exception e) { - logger.error("[存储收到的增加通道] 异常: ", e ); + @Transactional + public void executeSave() { + int size = channelList.size(); + List channelListForSave = new ArrayList<>(); + for (int i = 0; i < size; i++) { + channelListForSave.add(channelList.poll()); } - try { - executeSaveForOnline(); - } catch (Exception e) { - logger.error("[存储收到的通道上线] 异常: ", e ); - } - try { - executeSaveForOffline(); - } catch (Exception e) { - logger.error("[存储收到的通道离线] 异常: ", e ); - } - try { - executeSaveForUpdate(); - } catch (Exception e) { - logger.error("[存储收到的更新通道] 异常: ", e ); - } - try { - executeSaveForDelete(); - } catch (Exception e) { - logger.error("[存储收到的删除通道] 异常: ", e ); + + for (NotifyCatalogChannel notifyCatalogChannel : channelListForSave) { + try { + switch (notifyCatalogChannel.getType()) { + case STATUS_CHANGED: + deviceChannelService.updateChannelStatus(notifyCatalogChannel.getChannel()); + break; + case ADD: + deviceChannelService.addChannel(notifyCatalogChannel.getChannel()); + break; + case UPDATE: + deviceChannelService.updateChannelForNotify(notifyCatalogChannel.getChannel()); + break; + case DELETE: + deviceChannelService.delete(notifyCatalogChannel.getChannel()); + break; + } + }catch (Exception e) { + log.error("[存储收到的通道]类型:{},编号:{}", notifyCatalogChannel.getType(), + notifyCatalogChannel.getChannel().getDeviceId(), e); + } } } - - private void executeSaveForUpdate(){ - if (!updateChannelMap.values().isEmpty()) { - logger.info("[存储收到的更新通道], 数量: {}", updateChannelMap.size()); - ArrayList deviceChannels = new ArrayList<>(updateChannelMap.values()); - deviceChannelService.batchUpdateChannel(deviceChannels); - updateChannelMap.clear(); - } - } - - private void executeSaveForAdd(){ - if (!addChannelMap.values().isEmpty()) { - ArrayList deviceChannels = new ArrayList<>(addChannelMap.values()); - addChannelMap.clear(); - deviceChannelService.batchAddChannel(deviceChannels); - } - } - - private void executeSaveForDelete(){ - if (!deleteChannelList.isEmpty()) { - deviceChannelService.deleteChannels(deleteChannelList); - deleteChannelList.clear(); - } - } - - private void executeSaveForOnline(){ - if (!updateChannelOnlineList.isEmpty()) { - deviceChannelService.channelsOnline(updateChannelOnlineList); - updateChannelOnlineList.clear(); - } - } - - private void executeSaveForOffline(){ - if (!updateChannelOfflineList.isEmpty()) { - deviceChannelService.channelsOffline(updateChannelOfflineList); - updateChannelOfflineList.clear(); - } - } - -// @Scheduled(fixedRate = 10000) //每1秒执行一次 -// public void execute(){ -// logger.info("[待处理Notify-目录订阅消息数量]: {}", taskQueue.size()); -// } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForMobilePositionProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForMobilePositionProcessor.java index 9c414a855..c4124cad3 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForMobilePositionProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForMobilePositionProcessor.java @@ -10,14 +10,13 @@ import com.genersoft.iot.vmp.gb28181.event.EventPublisher; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; -import com.genersoft.iot.vmp.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import com.genersoft.iot.vmp.service.IMobilePositionService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.utils.DateUtil; +import lombok.extern.slf4j.Slf4j; import org.dom4j.DocumentException; import org.dom4j.Element; -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; @@ -25,18 +24,17 @@ import org.springframework.util.ObjectUtils; import javax.sip.RequestEvent; import javax.sip.header.FromHeader; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; /** * SIP命令类型: NOTIFY请求中的移动位置请求处理 */ +@Slf4j @Component public class NotifyRequestForMobilePositionProcessor extends SIPRequestProcessorParent { - - private final static Logger logger = LoggerFactory.getLogger(NotifyRequestForMobilePositionProcessor.class); - private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @Autowired @@ -57,7 +55,7 @@ public class NotifyRequestForMobilePositionProcessor extends SIPRequestProcessor public void process(RequestEvent evt) { if (taskQueue.size() >= userSetting.getMaxNotifyCountQueue()) { - logger.error("[notify-移动位置] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue()); + log.error("[notify-移动位置] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue()); return; } taskQueue.offer(new HandlerCatchData(evt, null, null)); @@ -68,7 +66,14 @@ public class NotifyRequestForMobilePositionProcessor extends SIPRequestProcessor if (taskQueue.isEmpty()) { return; } - for (HandlerCatchData take : taskQueue) { + List handlerCatchDataList = new ArrayList<>(); + while (!taskQueue.isEmpty()) { + handlerCatchDataList.add(taskQueue.poll()); + } + if (handlerCatchDataList.isEmpty()) { + return; + } + for (HandlerCatchData take : handlerCatchDataList) { if (take == null) { continue; } @@ -80,27 +85,33 @@ public class NotifyRequestForMobilePositionProcessor extends SIPRequestProcessor // 回复 200 OK Element rootElement = getRootElement(evt); if (rootElement == null) { - logger.error("处理MobilePosition移动位置Notify时未获取到消息体,{}", evt.getRequest()); - return; + log.error("处理MobilePosition移动位置Notify时未获取到消息体,{}", evt.getRequest()); + continue; } Device device = redisCatchStorage.getDevice(deviceId); if (device == null) { - logger.error("处理MobilePosition移动位置Notify时未获取到device,{}", deviceId); - return; + log.error("处理MobilePosition移动位置Notify时未获取到device,{}", deviceId); + continue; } MobilePosition mobilePosition = new MobilePosition(); mobilePosition.setDeviceId(device.getDeviceId()); mobilePosition.setDeviceName(device.getName()); mobilePosition.setCreateTime(DateUtil.getNow()); + + DeviceChannel deviceChannel = null; List elements = rootElement.elements(); - for (Element element : elements) { + readDocument: for (Element element : elements) { switch (element.getName()){ case "DeviceID": String channelId = element.getStringValue(); - if (!deviceId.equals(channelId)) { - mobilePosition.setChannelId(channelId); + deviceChannel = deviceChannelService.getOne(device.getDeviceId(), channelId); + if (deviceChannel != null) { + mobilePosition.setChannelId(deviceChannel.getId()); + }else { + log.error("[notify-移动位置] 未找到通道 {}/{}", device.getDeviceId(), channelId); + break readDocument; } - continue; + break; case "Time": String timeVal = element.getStringValue(); if (ObjectUtils.isEmpty(timeVal)) { @@ -108,13 +119,13 @@ public class NotifyRequestForMobilePositionProcessor extends SIPRequestProcessor } else { mobilePosition.setTime(SipUtils.parseTime(timeVal)); } - continue; + break; case "Longitude": mobilePosition.setLongitude(Double.parseDouble(element.getStringValue())); - continue; + break; case "Latitude": mobilePosition.setLatitude(Double.parseDouble(element.getStringValue())); - continue; + break; case "Speed": String speedVal = element.getStringValue(); if (NumericUtil.isDouble(speedVal)) { @@ -122,7 +133,7 @@ public class NotifyRequestForMobilePositionProcessor extends SIPRequestProcessor } else { mobilePosition.setSpeed(0.0); } - continue; + break; case "Direction": String directionVal = element.getStringValue(); if (NumericUtil.isDouble(directionVal)) { @@ -130,7 +141,7 @@ public class NotifyRequestForMobilePositionProcessor extends SIPRequestProcessor } else { mobilePosition.setDirection(0.0); } - continue; + break; case "Altitude": String altitudeVal = element.getStringValue(); if (NumericUtil.isDouble(altitudeVal)) { @@ -138,12 +149,15 @@ public class NotifyRequestForMobilePositionProcessor extends SIPRequestProcessor } else { mobilePosition.setAltitude(0.0); } - continue; + break; } } + if (deviceChannel == null) { + continue; + } - logger.debug("[收到移动位置订阅通知]:{}/{}->{}.{}, 时间: {}", mobilePosition.getDeviceId(), mobilePosition.getChannelId(), + log.info("[收到移动位置订阅通知]:{}/{}->{}.{}, 时间: {}", mobilePosition.getDeviceId(), mobilePosition.getChannelId(), mobilePosition.getLongitude(), mobilePosition.getLatitude(), System.currentTimeMillis() - startTime); mobilePosition.setReportSource("Mobile Position"); @@ -152,16 +166,16 @@ public class NotifyRequestForMobilePositionProcessor extends SIPRequestProcessor try { eventPublisher.mobilePositionEventPublish(mobilePosition); }catch (Exception e) { - logger.error("[向上级转发移动位置失败] ", e); + log.error("[向上级转发移动位置失败] ", e); } - if (mobilePosition.getChannelId() == null || mobilePosition.getChannelId().equals(mobilePosition.getDeviceId())) { + if (mobilePosition.getChannelId() == null) { List channels = deviceChannelService.queryChaneListByDeviceId(mobilePosition.getDeviceId()); channels.forEach(channel -> { // 发送redis消息。 通知位置信息的变化 JSONObject jsonObject = new JSONObject(); jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(mobilePosition.getTime())); - jsonObject.put("serial", channel.getDeviceId()); - jsonObject.put("code", channel.getChannelId()); + jsonObject.put("serial", device.getDeviceId()); + jsonObject.put("code", channel.getDeviceId()); jsonObject.put("longitude", mobilePosition.getLongitude()); jsonObject.put("latitude", mobilePosition.getLatitude()); jsonObject.put("altitude", mobilePosition.getAltitude()); @@ -171,22 +185,23 @@ public class NotifyRequestForMobilePositionProcessor extends SIPRequestProcessor }); }else { // 发送redis消息。 通知位置信息的变化 - JSONObject jsonObject = new JSONObject(); - jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(mobilePosition.getTime())); - jsonObject.put("serial", mobilePosition.getDeviceId()); - jsonObject.put("code", mobilePosition.getChannelId()); - jsonObject.put("longitude", mobilePosition.getLongitude()); - jsonObject.put("latitude", mobilePosition.getLatitude()); - jsonObject.put("altitude", mobilePosition.getAltitude()); - jsonObject.put("direction", mobilePosition.getDirection()); - jsonObject.put("speed", mobilePosition.getSpeed()); - redisCatchStorage.sendMobilePositionMsg(jsonObject); + if (deviceChannel != null) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(mobilePosition.getTime())); + jsonObject.put("serial", mobilePosition.getDeviceId()); + jsonObject.put("code", deviceChannel.getDeviceId()); + jsonObject.put("longitude", mobilePosition.getLongitude()); + jsonObject.put("latitude", mobilePosition.getLatitude()); + jsonObject.put("altitude", mobilePosition.getAltitude()); + jsonObject.put("direction", mobilePosition.getDirection()); + jsonObject.put("speed", mobilePosition.getSpeed()); + redisCatchStorage.sendMobilePositionMsg(jsonObject); + } } } catch (DocumentException e) { - logger.error("未处理的异常 ", e); + log.error("未处理的异常 ", e); } } - taskQueue.clear(); } // @Scheduled(fixedRate = 10000) // public void execute(){ diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java index 8da07a15c..ba1cd6236 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java @@ -9,14 +9,13 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; 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.gb28181.service.IDeviceChannelService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.utils.DateUtil; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.DocumentException; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -31,12 +30,10 @@ import java.text.ParseException; /** * SIP命令类型: NOTIFY请求,这是作为上级发送订阅请求后,设备才会响应的 */ +@Slf4j @Component public class NotifyRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { - - private final static Logger logger = LoggerFactory.getLogger(NotifyRequestProcessor.class); - @Autowired private SipConfig sipConfig; @@ -72,7 +69,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null); Element rootElement = getRootElement(evt); if (rootElement == null) { - logger.error("处理NOTIFY消息时未获取到消息体,{}", evt.getRequest()); + log.error("处理NOTIFY消息时未获取到消息体,{}", evt.getRequest()); responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null); return; } @@ -85,10 +82,10 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements } else if (CmdType.MOBILE_POSITION.equals(cmd)) { notifyRequestForMobilePositionProcessor.process(evt); } else { - logger.info("接收到消息:" + cmd); + log.info("接收到消息:" + cmd); } } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("未处理的异常 ", e); + log.error("未处理的异常 ", e); } catch (DocumentException e) { throw new RuntimeException(e); } @@ -107,7 +104,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements Element rootElement = getRootElement(evt); if (rootElement == null) { - logger.error("处理alarm设备报警Notify时未获取到消息体{}", evt.getRequest()); + log.error("处理alarm设备报警Notify时未获取到消息体{}", evt.getRequest()); return; } Element deviceIdElement = rootElement.element("DeviceID"); @@ -115,12 +112,12 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements Device device = redisCatchStorage.getDevice(deviceId); if (device == null) { - logger.warn("[ NotifyAlarm ] 未找到设备:{}", deviceId); + log.warn("[ NotifyAlarm ] 未找到设备:{}", deviceId); return; } rootElement = getRootElement(evt, device.getCharset()); if (rootElement == null) { - logger.warn("[ NotifyAlarm ] content cannot be null, {}", evt.getRequest()); + log.warn("[ NotifyAlarm ] content cannot be null, {}", evt.getRequest()); return; } DeviceAlarm deviceAlarm = new DeviceAlarm(); @@ -129,7 +126,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod")); String alarmTime = XmlUtil.getText(rootElement, "AlarmTime"); if (alarmTime == null) { - logger.warn("[ NotifyAlarm ] AlarmTime cannot be null"); + log.warn("[ NotifyAlarm ] AlarmTime cannot be null"); return; } deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime)); @@ -148,33 +145,28 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements } else { deviceAlarm.setLatitude(0.00); } - logger.info("[收到Notify-Alarm]:{}/{}", device.getDeviceId(), deviceAlarm.getChannelId()); + log.info("[收到Notify-Alarm]:{}/{}", device.getDeviceId(), deviceAlarm.getChannelId()); if ("4".equals(deviceAlarm.getAlarmMethod())) { - MobilePosition mobilePosition = new MobilePosition(); - mobilePosition.setChannelId(channelId); - mobilePosition.setCreateTime(DateUtil.getNow()); - mobilePosition.setDeviceId(deviceAlarm.getDeviceId()); - mobilePosition.setTime(deviceAlarm.getAlarmTime()); - mobilePosition.setLongitude(deviceAlarm.getLongitude()); - mobilePosition.setLatitude(deviceAlarm.getLatitude()); - mobilePosition.setReportSource("GPS Alarm"); + DeviceChannel deviceChannel = deviceChannelService.getOne(device.getDeviceId(), channelId); + if (deviceChannel == null) { + log.warn("[解析报警通知] 未找到通道:{}/{}", device.getDeviceId(), channelId); + }else { + MobilePosition mobilePosition = new MobilePosition(); + mobilePosition.setChannelId(deviceChannel.getId()); + mobilePosition.setCreateTime(DateUtil.getNow()); + mobilePosition.setDeviceId(deviceAlarm.getDeviceId()); + mobilePosition.setTime(deviceAlarm.getAlarmTime()); + mobilePosition.setLongitude(deviceAlarm.getLongitude()); + mobilePosition.setLatitude(deviceAlarm.getLatitude()); + mobilePosition.setReportSource("GPS Alarm"); - // 更新device channel 的经纬度 - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setDeviceId(device.getDeviceId()); - deviceChannel.setChannelId(channelId); - deviceChannel.setLongitude(mobilePosition.getLongitude()); - deviceChannel.setLatitude(mobilePosition.getLatitude()); - deviceChannel.setGpsTime(mobilePosition.getTime()); + // 更新device channel 的经纬度 + deviceChannel.setLongitude(mobilePosition.getLongitude()); + deviceChannel.setLatitude(mobilePosition.getLatitude()); + deviceChannel.setGpsTime(mobilePosition.getTime()); - deviceChannel = deviceChannelService.updateGps(deviceChannel, device); - - mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); - mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); - mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); - mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); - - deviceChannelService.updateChannelGPS(device, deviceChannel, mobilePosition); + deviceChannelService.updateChannelGPS(device, deviceChannel, mobilePosition); + } } // 回复200 OK @@ -182,7 +174,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements publisher.deviceAlarmEventPublish(deviceAlarm); } } catch (DocumentException e) { - logger.error("未处理的异常 ", e); + log.error("未处理的异常 ", e); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java index 1f0ba5393..23a6e42a5 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java @@ -4,15 +4,15 @@ import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper; import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.GbSipDate; import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; -import com.genersoft.iot.vmp.gb28181.bean.GbSipDate; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; -import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.utils.DateUtil; import gov.nist.javax.sip.RequestEventExt; import gov.nist.javax.sip.address.AddressImpl; @@ -20,16 +20,12 @@ import gov.nist.javax.sip.address.SipUri; import gov.nist.javax.sip.header.SIPDateHeader; import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; -import javax.sip.*; -import javax.sip.header.*; -import javax.sip.message.Request; import javax.sip.RequestEvent; import javax.sip.SipException; import javax.sip.header.AuthorizationHeader; @@ -46,11 +42,10 @@ import java.util.Locale; /** * SIP命令类型: REGISTER请求 */ +@Slf4j @Component public class RegisterRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { - private final Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class); - public final String method = "REGISTER"; @Autowired @@ -98,17 +93,17 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen SipUri uri = (SipUri) address.getURI(); String deviceId = uri.getUser(); - Device device = deviceService.getDevice(deviceId); + Device device = deviceService.getDeviceByDeviceId(deviceId); RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress()); String requestAddress = remoteAddressInfo.getIp() + ":" + remoteAddressInfo.getPort(); String title = registerFlag ? "[注册请求]": "[注销请求]"; - logger.info(title + "设备:{}, 开始处理: {}", deviceId, requestAddress); + log.info(title + "设备:{}, 开始处理: {}", deviceId, requestAddress); if (device != null && device.getSipTransactionInfo() != null && request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) { - logger.info(title + "设备:{}, 注册续订: {}",device.getDeviceId(), device.getDeviceId()); + log.info(title + "设备:{}, 注册续订: {}",device.getDeviceId(), device.getDeviceId()); if (registerFlag) { device.setExpires(request.getExpires().getExpires()); device.setIp(remoteAddressInfo.getIp()); @@ -133,7 +128,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword(); AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); if (authHead == null && !ObjectUtils.isEmpty(password)) { - logger.info(title + " 设备:{}, 回复401: {}",deviceId, requestAddress); + log.info(title + " 设备:{}, 回复401: {}",deviceId, requestAddress); response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request); new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain()); sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); @@ -148,7 +143,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen // 注册失败 response = getMessageFactory().createResponse(Response.FORBIDDEN, request); response.setReasonPhrase("wrong password"); - logger.info(title + " 设备:{}, 密码/SIP服务器ID错误, 回复403: {}", deviceId, requestAddress); + log.info(title + " 设备:{}, 密码/SIP服务器ID错误, 回复403: {}", deviceId, requestAddress); sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); return; } @@ -212,16 +207,16 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen // 注册成功 // 保存到redis if (registerFlag) { - logger.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress); + log.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress); device.setRegisterTime(DateUtil.getNow()); SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)response); deviceService.online(device, sipTransactionInfo); } else { - logger.info("[注销成功] deviceId: {}->{}" ,deviceId, requestAddress); + log.info("[注销成功] deviceId: {}->{}" ,deviceId, requestAddress); deviceService.offline(deviceId, "主动注销"); } } catch (SipException | NoSuchAlgorithmException | ParseException e) { - logger.error("未处理的异常 ", e); + log.error("未处理的异常 ", e); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java index c392a129d..39d04c13d 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java @@ -1,26 +1,21 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; -import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.conf.DynamicTask; -import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.CmdType; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder; import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo; +import com.genersoft.iot.vmp.gb28181.service.IPlatformService; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; -import com.genersoft.iot.vmp.service.IPlatformService; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPResponse; +import lombok.extern.slf4j.Slf4j; import org.dom4j.DocumentException; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -36,18 +31,15 @@ import java.text.ParseException; * SIP命令类型: SUBSCRIBE请求 * @author lin */ +@Slf4j @Component public class SubscribeRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { - private final Logger logger = LoggerFactory.getLogger(SubscribeRequestProcessor.class); private final String method = "SUBSCRIBE"; @Autowired private SIPProcessorObserver sipProcessorObserver; - @Autowired - private IVideoManagerStorage storager; - @Autowired private SubscribeHolder subscribeHolder; @@ -75,7 +67,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme try { Element rootElement = getRootElement(evt); if (rootElement == null) { - logger.error("处理SUBSCRIBE请求 未获取到消息体{}", evt.getRequest()); + log.error("处理SUBSCRIBE请求 未获取到消息体{}", evt.getRequest()); return; } String cmd = XmlUtil.getText(rootElement, "CmdType"); @@ -87,18 +79,18 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme } else if (CmdType.CATALOG.equals(cmd)) { processNotifyCatalogList(request, rootElement); } else { - logger.info("接收到消息:" + cmd); + log.info("接收到消息:" + cmd); Response response = getMessageFactory().createResponse(200, request); if (response != null) { ExpiresHeader expireHeader = getHeaderFactory().createExpiresHeader(30); response.setExpires(expireHeader); } - logger.info("response : " + response); + log.info("response : " + response); sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); } } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - logger.error("未处理的异常 ", e); + log.error("未处理的异常 ", e); } } @@ -112,14 +104,14 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme } String platformId = SipUtils.getUserIdFromFromHeader(request); String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); + Platform platform = platformService.queryPlatformByServerGBId(platformId); SubscribeInfo subscribeInfo = new SubscribeInfo(request, platformId); if (platform == null) { return; } String sn = XmlUtil.getText(rootElement, "SN"); - logger.info("[回复上级的移动位置订阅请求]: {}", platformId); + log.info("[回复上级的移动位置订阅请求]: {}", platformId); StringBuilder resultXml = new StringBuilder(200); resultXml.append("\r\n") .append("\r\n") @@ -141,8 +133,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme } try { - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformId); - SIPResponse response = responseXmlAck(request, resultXml.toString(), parentPlatform, subscribeInfo.getExpires()); + SIPResponse response = responseXmlAck(request, resultXml.toString(), platform, subscribeInfo.getExpires()); if (subscribeInfo.getExpires() == 0) { subscribeHolder.removeMobilePositionSubscribe(platformId); }else { @@ -153,7 +144,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme } } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("未处理的异常 ", e); + log.error("未处理的异常 ", e); } } @@ -167,14 +158,14 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme } String platformId = SipUtils.getUserIdFromFromHeader(request); String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); + Platform platform = platformService.queryPlatformByServerGBId(platformId); if (platform == null){ return; } SubscribeInfo subscribeInfo = new SubscribeInfo(request, platformId); String sn = XmlUtil.getText(rootElement, "SN"); - logger.info("[回复上级的目录订阅请求]: {}/{}", platformId, deviceId); + log.info("[回复上级的目录订阅请求]: {}/{}", platformId, deviceId); StringBuilder resultXml = new StringBuilder(200); resultXml.append("\r\n") .append("\r\n") @@ -190,7 +181,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme subscribeHolder.removeCatalogSubscribe(platformId); } try { - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformId); + Platform parentPlatform = platformService.queryPlatformByServerGBId(platformId); SIPResponse response = responseXmlAck(request, resultXml.toString(), parentPlatform, subscribeInfo.getExpires()); if (subscribeInfo.getExpires() == 0) { subscribeHolder.removeCatalogSubscribe(platformId); @@ -199,9 +190,10 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme subscribeHolder.putCatalogSubscribe(platformId, subscribeInfo); } } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("未处理的异常 ", e); + log.error("未处理的异常 ", e); } - if (subscribeHolder.getCatalogSubscribe(platformId) == null && platform.isAutoPushChannel()) { + if (subscribeHolder.getCatalogSubscribe(platformId) == null + && platform.getAutoPushChannel() != null && platform.getAutoPushChannel()) { platformService.addSimulatedSubscribeInfo(platform); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java index 92175ac29..238cda239 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java @@ -1,21 +1,17 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.info; -import com.genersoft.iot.vmp.common.InviteInfo; -import com.genersoft.iot.vmp.common.InviteSessionType; 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.service.*; +import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; -import com.genersoft.iot.vmp.gb28181.utils.SipUtils; -import com.genersoft.iot.vmp.service.IInviteStreamService; +import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import gov.nist.javax.sip.message.SIPRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -28,18 +24,20 @@ import javax.sip.header.ContentTypeHeader; import javax.sip.message.Response; import java.text.ParseException; +/** + * INFO 一般用于国标级联时的回放控制 + */ +@Slf4j @Component public class InfoRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { - private final static Logger logger = LoggerFactory.getLogger(InfoRequestProcessor.class); - private final String method = "INFO"; @Autowired private SIPProcessorObserver sipProcessorObserver; @Autowired - private IVideoManagerStorage storage; + private IPlatformService platformService; @Autowired private SipSubscribe sipSubscribe; @@ -51,13 +49,22 @@ public class InfoRequestProcessor extends SIPRequestProcessorParent implements I private IInviteStreamService inviteStreamService; @Autowired - private IVideoManagerStorage storager; + private IDeviceService deviceService; + + @Autowired + private IGbChannelService channelService; + + @Autowired + private IDeviceChannelService deviceChannelService; @Autowired private SIPCommander cmder; @Autowired - private VideoStreamSessionManager sessionManager; + private SipInviteSessionManager sessionManager; + + @Autowired + private ISendRtpServerService sendRtpServerService; @Override public void afterPropertiesSet() throws Exception { @@ -67,83 +74,80 @@ public class InfoRequestProcessor extends SIPRequestProcessorParent implements I @Override public void process(RequestEvent evt) { - logger.debug("接收到消息:" + evt.getRequest()); SIPRequest request = (SIPRequest) evt.getRequest(); - String deviceId = SipUtils.getUserIdFromFromHeader(request); CallIdHeader callIdHeader = request.getCallIdHeader(); // 先从会话内查找 - SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null); - - // 兼容海康 媒体通知 消息from字段不是设备ID的问题 - if (ssrcTransaction != null) { - deviceId = ssrcTransaction.getDeviceId(); - } - // 查询设备是否存在 - Device device = redisCatchStorage.getDevice(deviceId); - // 查询上级平台是否存在 - ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId); try { - if (device != null && parentPlatform != null) { - logger.warn("[重复]平台与设备编号重复:{}", deviceId); - String hostAddress = request.getRemoteAddress().getHostAddress(); - int remotePort = request.getRemotePort(); - if (device.getHostAddress().equals(hostAddress + ":" + remotePort)) { - parentPlatform = null; - }else { - device = null; - } - } - if (device == null && parentPlatform == null) { + SendRtpInfo sendRtpInfo = sendRtpServerService.queryByCallId(callIdHeader.getCallId()); + if (sendRtpInfo == null || !sendRtpInfo.isSendToPlatform()) { // 不存在则回复404 - responseAck(request, Response.NOT_FOUND, "device "+ deviceId +" not found"); - logger.warn("[设备未找到 ]: {}", deviceId); - if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null){ - DeviceNotFoundEvent deviceNotFoundEvent = new DeviceNotFoundEvent(evt.getDialog()); - deviceNotFoundEvent.setCallId(callIdHeader.getCallId()); - SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(deviceNotFoundEvent); - sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult); - }; - }else { - ContentTypeHeader header = (ContentTypeHeader)evt.getRequest().getHeader(ContentTypeHeader.NAME); - String contentType = header.getContentType(); - String contentSubType = header.getContentSubType(); - if ("Application".equalsIgnoreCase(contentType) && "MANSRTSP".equalsIgnoreCase(contentSubType)) { - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId()); - String streamId = sendRtpItem.getStream(); - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); - if (null == inviteInfo) { - responseAck(request, Response.NOT_FOUND, "stream " + streamId + " not found"); - return; - } - Device device1 = storager.queryVideoDevice(inviteInfo.getDeviceId()); - if (inviteInfo.getStreamInfo() != null) { - cmder.playbackControlCmd(device1,inviteInfo.getStreamInfo(),new String(evt.getRequest().getRawContent()),eventResult -> { - // 失败的回复 - try { - responseAck(request, eventResult.statusCode, eventResult.msg); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 录像控制: {}", e.getMessage()); - } - }, eventResult -> { - // 成功的回复 - try { - responseAck(request, eventResult.statusCode); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 录像控制: {}", e.getMessage()); - } - }); - } + log.warn("[INFO 消息] 事务未找到, callID: {}", callIdHeader.getCallId()); + responseAck(request, Response.NOT_FOUND, "transaction not found"); + return; + } + // 查询上级平台是否存在 + Platform platform = platformService.queryPlatformByServerGBId(sendRtpInfo.getTargetId()); + if (platform == null || !platform.isStatus()) { + // 不存在则回复404 + log.warn("[INFO 消息] 平台未找到或者已离线: 平台: {}", sendRtpInfo.getTargetId()); + responseAck(request, Response.NOT_FOUND, "platform "+ sendRtpInfo.getTargetId() +" not found or offline"); + return; + } + CommonGBChannel channel = channelService.getOne(sendRtpInfo.getChannelId()); + if (channel == null) { + // 不存在则回复404 + log.warn("[INFO 消息] 通道不存在: 通道ID: {}", sendRtpInfo.getChannelId()); + responseAck(request, Response.NOT_FOUND, "channel not found or offline"); + return; + } + // 判断通道类型 + if (channel.getGbDeviceDbId() == null) { + // 非国标通道不支持录像回放控制 + log.warn("[INFO 消息] 非国标通道不支持录像回放控制: 通道ID: {}", sendRtpInfo.getChannelId()); + responseAck(request, Response.FORBIDDEN, ""); + return; + } - } + // 根据通道ID,获取所属设备 + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); + if (device == null) { + // 不存在则回复404 + log.warn("[INFO 消息] 通道所属设备不存在, 通道ID: {}", sendRtpInfo.getChannelId()); + responseAck(request, Response.NOT_FOUND, "platform "+ sendRtpInfo.getChannelId() +" not found or offline"); + return; + } + // 获取通道的原始信息 + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(sendRtpInfo.getChannelId()); + // 向原始通道转发控制消息 + ContentTypeHeader header = (ContentTypeHeader)evt.getRequest().getHeader(ContentTypeHeader.NAME); + String contentType = header.getContentType(); + String contentSubType = header.getContentSubType(); + if ("Application".equalsIgnoreCase(contentType) && "MANSRTSP".equalsIgnoreCase(contentSubType)) { + log.info("[INFO 消息] 平台: {}->{}({})/{}", platform.getServerGBId(), device.getName(), + device.getDeviceId(), deviceChannel.getId()); + // 不解析协议, 直接转发给对应的设备 + cmder.playbackControlCmd(device, deviceChannel, sendRtpInfo.getStream(), new String(evt.getRequest().getRawContent()), eventResult -> { + // 失败的回复 + try { + responseAck(request, eventResult.statusCode, eventResult.msg); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 国标级联 录像控制: {}", e.getMessage()); + } + }, eventResult -> { + // 成功的回复 + try { + responseAck(request, eventResult.statusCode); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 国标级联 录像控制: {}", e.getMessage()); + } + }); } } catch (SipException e) { - logger.warn("SIP 回复错误", e); + log.warn("SIP 回复错误", e); } catch (InvalidArgumentException e) { - logger.warn("参数无效", e); + log.warn("参数无效", e); } catch (ParseException e) { - logger.warn("SIP回复时解析异常", e); + log.warn("SIP回复时解析异常", e); } } - - } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/IMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/IMessageHandler.java index 7eba0d52a..085f9212c 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/IMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/IMessageHandler.java @@ -1,7 +1,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import org.dom4j.Element; import javax.sip.RequestEvent; @@ -19,5 +19,5 @@ public interface IMessageHandler { * @param evt * @param parentPlatform */ - void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element); + void handForPlatform(RequestEvent evt, Platform parentPlatform, Element element); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageHandlerAbstract.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageHandlerAbstract.java index 855395c0b..9dbf4a4ed 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageHandlerAbstract.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageHandlerAbstract.java @@ -1,14 +1,13 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.service.IPlatformService; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd.CatalogQueryMessageHandler; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import javax.sip.InvalidArgumentException; @@ -21,14 +20,13 @@ import java.util.concurrent.ConcurrentHashMap; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; +@Slf4j public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent implements IMessageHandler{ - private Logger logger = LoggerFactory.getLogger(MessageHandlerAbstract.class); - public Map messageHandlerMap = new ConcurrentHashMap<>(); @Autowired - private IVideoManagerStorage storage; + private IPlatformService platformService; public void addHandler(String cmdType, IMessageHandler messageHandler) { messageHandlerMap.put(cmdType, messageHandler); @@ -41,7 +39,7 @@ public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent i try { responseAck((SIPRequest) evt.getRequest(), Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 回复200 OK: {}", e.getMessage()); + log.error("[命令发送失败] 回复200 OK: {}", e.getMessage()); } return; } @@ -51,7 +49,7 @@ public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent i //两个国标平台互相级联时由于上一步判断导致本该在平台处理的消息 放到了设备的处理逻辑 //所以对目录查询单独做了校验 if(messageHandler instanceof CatalogQueryMessageHandler){ - ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(device.getDeviceId()); + Platform parentPlatform = platformService.queryPlatformByServerGBId(device.getDeviceId()); messageHandler.handForPlatform(evt, parentPlatform, element); return; } @@ -60,7 +58,7 @@ public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent i } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element element) { String cmd = getText(element, "CmdType"); IMessageHandler messageHandler = messageHandlerMap.get(cmd); if (messageHandler != null) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java index 34d7077e8..237eab162 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java @@ -2,21 +2,21 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceNotFoundEvent; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.event.sip.SipEvent; +import com.genersoft.iot.vmp.gb28181.service.IPlatformService; +import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.DocumentException; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -30,11 +30,10 @@ import java.text.ParseException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +@Slf4j @Component public class MessageRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { - private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class); - private final String method = "MESSAGE"; private static final Map messageHandlerMap = new ConcurrentHashMap<>(); @@ -43,7 +42,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement private SIPProcessorObserver sipProcessorObserver; @Autowired - private IVideoManagerStorage storage; + private IPlatformService platformService; @Autowired private SipSubscribe sipSubscribe; @@ -52,7 +51,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement private IRedisCatchStorage redisCatchStorage; @Autowired - private VideoStreamSessionManager sessionManager; + private SipInviteSessionManager sessionManager; @Override public void afterPropertiesSet() throws Exception { @@ -71,7 +70,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest()); CallIdHeader callIdHeader = sipRequest.getCallIdHeader(); // 先从会话内查找 - SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null); + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByCallId(callIdHeader.getCallId()); // 兼容海康 媒体通知 消息from字段不是设备ID的问题 if (ssrcTransaction != null) { deviceId = ssrcTransaction.getDeviceId(); @@ -80,7 +79,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement // 查询设备是否存在 Device device = redisCatchStorage.getDevice(deviceId); // 查询上级平台是否存在 - ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId); + Platform parentPlatform = platformService.queryPlatformByServerGBId(deviceId); try { if (device != null && parentPlatform != null) { String hostAddress = request.getRemoteAddress().getHostAddress(); @@ -94,19 +93,20 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement if (device == null && parentPlatform == null) { // 不存在则回复404 responseAck(request, Response.NOT_FOUND, "device "+ deviceId +" not found"); - logger.warn("[设备未找到 ]deviceId: {}, callId: {}", deviceId, callIdHeader.getCallId()); - if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null){ + log.warn("[设备未找到 ]deviceId: {}, callId: {}", deviceId, callIdHeader.getCallId()); + SipEvent sipEvent = sipSubscribe.getSubscribe(callIdHeader.getCallId()); + if (sipEvent != null && sipEvent.getErrorEvent() != null){ DeviceNotFoundEvent deviceNotFoundEvent = new DeviceNotFoundEvent(evt.getDialog()); deviceNotFoundEvent.setCallId(callIdHeader.getCallId()); SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(deviceNotFoundEvent); - sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult); + sipEvent.getErrorEvent().response(eventResult); } }else { Element rootElement; try { rootElement = getRootElement(evt); if (rootElement == null) { - logger.error("处理MESSAGE请求 未获取到消息体{}", evt.getRequest()); + log.error("处理MESSAGE请求 未获取到消息体{}", evt.getRequest()); responseAck(request, Response.BAD_REQUEST, "content is null"); return; } @@ -124,17 +124,17 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE, "Unsupported message type, must Control/Notify/Query/Response"); } } catch (DocumentException e) { - logger.warn("解析XML消息内容异常", e); + log.warn("解析XML消息内容异常", e); // 不存在则回复404 responseAck(request, Response.BAD_REQUEST, e.getMessage()); } } } catch (SipException e) { - logger.warn("SIP 回复错误", e); + log.warn("SIP 回复错误", e); } catch (InvalidArgumentException e) { - logger.warn("参数无效", e); + log.warn("参数无效", e); } catch (ParseException e) { - logger.warn("SIP回复时解析异常", e); + log.warn("SIP回复时解析异常", e); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java index 23adac13e..cb1cced65 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java @@ -1,58 +1,55 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.cmd; import com.genersoft.iot.vmp.common.enums.DeviceControlType; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DragZoomRequest; -import com.genersoft.iot.vmp.gb28181.bean.HomePositionRequest; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; 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.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.ControlMessageHandler; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; -import javax.sip.*; +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; import javax.sip.address.SipURI; import javax.sip.message.Response; import java.text.ParseException; import java.util.List; -import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.*; +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.loadElement; +@Slf4j @Component public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(DeviceControlQueryMessageHandler.class); private final String cmdType = "DeviceControl"; @Autowired private ControlMessageHandler controlMessageHandler; @Autowired - private IVideoManagerStorage storager; + private IGbChannelService channelService; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IDeviceChannelService deviceChannelService; @Autowired private SIPCommander cmder; - @Autowired - private SIPCommanderFroPlatform cmderFroPlatform; - - @Qualifier("taskExecutor") - @Autowired - private ThreadPoolTaskExecutor taskExecutor; - @Override public void afterPropertiesSet() throws Exception { controlMessageHandler.addHandler(cmdType, this); @@ -64,7 +61,7 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + public void handForPlatform(RequestEvent evt, Platform platform, Element rootElement) { SIPRequest request = (SIPRequest) evt.getRequest(); @@ -73,79 +70,59 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent String channelId = getText(rootElement, "DeviceID"); // 远程启动功能 if (!ObjectUtils.isEmpty(getText(rootElement, "TeleBoot"))) { - // TODO 拒绝远程启动命令 - logger.warn("[国标级联]收到平台的远程启动命令, 不处理"); - -// if (parentPlatform.getServerGBId().equals(targetGBId)) { -// // 远程启动本平台:需要在重新启动程序后先对SipStack解绑 -// logger.info("执行远程启动本平台命令"); -// try { -// cmderFroPlatform.unregister(parentPlatform, null, null); -// } catch (InvalidArgumentException | ParseException | SipException e) { -// logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); -// } -// taskExecutor.execute(() -> { -// // 远程启动 -//// try { -//// Thread.sleep(3000); -//// SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider"); -//// SipStackImpl stack = (SipStackImpl)up.getSipStack(); -//// stack.stop(); -//// Iterator listener = stack.getListeningPoints(); -//// while (listener.hasNext()) { -//// stack.deleteListeningPoint((ListeningPoint) listener.next()); -//// } -//// Iterator providers = stack.getSipProviders(); -//// while (providers.hasNext()) { -//// stack.deleteSipProvider((SipProvider) providers.next()); -//// } -//// VManageBootstrap.restart(); -//// } catch (InterruptedException | ObjectInUseException e) { -//// logger.error("[任务执行失败] 服务重启: {}", e.getMessage()); -//// } -// }); -// } + // 拒绝远程启动命令 + log.warn("[deviceControl] 远程启动命令, 禁用,不允许上级平台随意重启下级平台"); + try { + responseAck(request, Response.FORBIDDEN); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; } DeviceControlType deviceControlType = DeviceControlType.typeOf(rootElement); - logger.info("[接受deviceControl命令] 命令: {}", deviceControlType); - if (!ObjectUtils.isEmpty(deviceControlType) && !parentPlatform.getServerGBId().equals(targetGBId)) { - //判断是否存在该通道 - Device deviceForPlatform = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId); - if (deviceForPlatform == null) { - try { - responseAck(request, Response.NOT_FOUND); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 错误信息: {}", e.getMessage()); - } - return; + + CommonGBChannel channel = channelService.queryOneWithPlatform(platform.getId(), channelId); + if (channel == null) { + log.warn("[deviceControl] 未找到通道, 平台: {}({}),通道编号:{}", platform.getName(), + platform.getServerGBId(), channelId); + try { + responseAck(request, Response.NOT_FOUND, "channel not found"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); } + return; + } + log.info("[deviceControl] 命令: {}, 平台: {}({})->{}", deviceControlType, platform.getName(), + platform.getServerGBId(), channel.getGbId()); + + if (!ObjectUtils.isEmpty(deviceControlType)) { switch (deviceControlType) { case PTZ: - handlePtzCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.PTZ); + handlePtzCmd(channel, rootElement, request, DeviceControlType.PTZ); break; case ALARM: - handleAlarmCmd(deviceForPlatform, rootElement, request); + handleAlarmCmd(channel, rootElement, request); break; case GUARD: - handleGuardCmd(deviceForPlatform, rootElement, request, DeviceControlType.GUARD); + handleGuardCmd(channel, rootElement, request, DeviceControlType.GUARD); break; case RECORD: - handleRecordCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.RECORD); + handleRecordCmd(channel, rootElement, request, DeviceControlType.RECORD); break; case I_FRAME: - handleIFameCmd(deviceForPlatform, request, channelId); + handleIFameCmd(channel, request); break; case TELE_BOOT: - handleTeleBootCmd(deviceForPlatform, request); + handleTeleBootCmd(channel, request); break; case DRAG_ZOOM_IN: - handleDragZoom(deviceForPlatform, channelId, rootElement, request, DeviceControlType.DRAG_ZOOM_IN); + handleDragZoom(channel, rootElement, request, DeviceControlType.DRAG_ZOOM_IN); break; case DRAG_ZOOM_OUT: - handleDragZoom(deviceForPlatform, channelId, rootElement, request, DeviceControlType.DRAG_ZOOM_OUT); + handleDragZoom(channel, rootElement, request, DeviceControlType.DRAG_ZOOM_OUT); break; case HOME_POSITION: - handleHomePositionCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.HOME_POSITION); + handleHomePositionCmd(channel, rootElement, request, DeviceControlType.HOME_POSITION); break; default: break; @@ -155,62 +132,177 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent /** * 处理云台指令 - * - * @param device 设备 - * @param channelId 通道id - * @param rootElement - * @param request */ - private void handlePtzCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) { + private void handlePtzCmd(CommonGBChannel channel, Element rootElement, SIPRequest request, DeviceControlType type) { + if (channel.getGbDeviceDbId() == 0) { + // 只支持国标的云台控制 + log.warn("[INFO 消息] 只支持国标的云台控制, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.FORBIDDEN, ""); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + // 根据通道ID,获取所属设备 + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); + if (device == null) { + // 不存在则回复404 + log.warn("[INFO 消息] 通道所属设备不存在, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.NOT_FOUND, "device not found"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId()); + if (deviceChannel == null) { + log.warn("[deviceControl] 未找到设备原始通道, 设备: {}({}),通道编号:{}", device.getName(), + device.getDeviceId(), channel.getGbId()); + try { + responseAck(request, Response.NOT_FOUND, "channel not found"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + log.info("[deviceControl] 命令: {}, 设备: {}({}), 通道{}({}", type, device.getName(), device.getDeviceId(), + deviceChannel.getName(), deviceChannel.getDeviceId()); String cmdString = getText(rootElement, type.getVal()); try { - cmder.fronEndCmd(device, channelId, cmdString, + cmder.fronEndCmd(device, deviceChannel.getDeviceId(), cmdString, errorResult -> onError(request, errorResult), okResult -> onOk(request, okResult)); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 云台/前端: {}", e.getMessage()); + log.error("[命令发送失败] 云台/前端: {}", e.getMessage()); } } /** * 处理强制关键帧 - * - * @param device 设备 - * @param channelId 通道id */ - private void handleIFameCmd(Device device, SIPRequest request, String channelId) { + private void handleIFameCmd(CommonGBChannel channel, SIPRequest request) { + if (channel.getGbDeviceDbId() == 0) { + // 只支持国标的云台控制 + log.warn("[INFO 消息] 只支持国标的处理强制关键帧, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.FORBIDDEN, ""); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + // 根据通道ID,获取所属设备 + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); + if (device == null) { + // 不存在则回复404 + log.warn("[INFO 消息] 通道所属设备不存在, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.NOT_FOUND, "device not found"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId()); + if (deviceChannel == null) { + log.warn("[deviceControl] 未找到设备原始通道, 设备: {}({}),通道编号:{}", device.getName(), + device.getDeviceId(), channel.getGbId()); + try { + responseAck(request, Response.NOT_FOUND, "channel not found"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + log.info("[deviceControl] 命令: 强制关键帧, 设备: {}({}), 通道{}({}", device.getName(), device.getDeviceId(), + deviceChannel.getName(), deviceChannel.getDeviceId()); try { - cmder.iFrameCmd(device, channelId); + cmder.iFrameCmd(device, deviceChannel.getDeviceId()); responseAck(request, Response.OK); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 强制关键帧: {}", e.getMessage()); + log.error("[命令发送失败] 强制关键帧: {}", e.getMessage()); } } /** * 处理重启命令 - * - * @param device 设备信息 */ - private void handleTeleBootCmd(Device device, SIPRequest request) { + private void handleTeleBootCmd(CommonGBChannel channel, SIPRequest request) { + if (channel.getGbDeviceDbId() == 0) { + // 只支持国标的云台控制 + log.warn("[INFO 消息] 只支持国标的重启命令, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.FORBIDDEN, ""); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + // 根据通道ID,获取所属设备 + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); + if (device == null) { + // 不存在则回复404 + log.warn("[INFO 消息] 通道所属设备不存在, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.NOT_FOUND, "device not found"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } try { cmder.teleBootCmd(device); responseAck(request, Response.OK); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 重启: {}", e.getMessage()); + log.error("[命令发送失败] 重启: {}", e.getMessage()); } } /** - * 处理拉框控制*** - * - * @param device 设备信息 - * @param channelId 通道id - * @param rootElement 根节点 - * @param type 消息类型 + * 处理拉框控制 */ - private void handleDragZoom(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) { + private void handleDragZoom(CommonGBChannel channel, Element rootElement, SIPRequest request, DeviceControlType type) { + if (channel.getGbDeviceDbId() == 0) { + // 只支持国标的云台控制 + log.warn("[INFO 消息] 只支持国标的拉框控制, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.FORBIDDEN, ""); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + // 根据通道ID,获取所属设备 + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); + if (device == null) { + // 不存在则回复404 + log.warn("[INFO 消息] 通道所属设备不存在, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.NOT_FOUND, "device not found"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId()); + if (deviceChannel == null) { + log.warn("[deviceControl] 未找到设备原始通道, 设备: {}({}),通道编号:{}", device.getName(), + device.getDeviceId(), channel.getGbId()); + try { + responseAck(request, Response.NOT_FOUND, "channel not found"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + log.info("[deviceControl] 命令: {}, 设备: {}({}), 通道{}({}", type, device.getName(), device.getDeviceId(), + deviceChannel.getName(), deviceChannel.getDeviceId()); try { DragZoomRequest dragZoomRequest = loadElement(rootElement, DragZoomRequest.class); DragZoomRequest.DragZoom dragZoom = dragZoomRequest.getDragZoomIn(); @@ -226,44 +318,92 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent cmdXml.append("" + dragZoom.getLengthX() + "\r\n"); cmdXml.append("" + dragZoom.getLengthY() + "\r\n"); cmdXml.append("\r\n"); - cmder.dragZoomCmd(device, channelId, cmdXml.toString()); + cmder.dragZoomCmd(device, deviceChannel.getDeviceId(), cmdXml.toString()); responseAck(request, Response.OK); } catch (Exception e) { - logger.error("[命令发送失败] 拉框控制: {}", e.getMessage()); + log.error("[命令发送失败] 拉框控制: {}", e.getMessage()); } } /** - * 处理看守位命令*** - * - * @param device 设备信息 - * @param channelId 通道id - * @param rootElement 根节点 - * @param request 请求信息 - * @param type 消息类型 + * 处理看守位命令 */ - private void handleHomePositionCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) { + private void handleHomePositionCmd(CommonGBChannel channel, Element rootElement, SIPRequest request, DeviceControlType type) { + if (channel.getGbDeviceDbId() == 0) { + // 只支持国标的云台控制 + log.warn("[INFO 消息] 只支持国标的看守位命令, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.FORBIDDEN, ""); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + // 根据通道ID,获取所属设备 + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); + if (device == null) { + // 不存在则回复404 + log.warn("[INFO 消息] 通道所属设备不存在, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.NOT_FOUND, "device not found"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId()); + if (deviceChannel == null) { + log.warn("[deviceControl] 未找到设备原始通道, 设备: {}({}),通道编号:{}", device.getName(), + device.getDeviceId(), channel.getGbId()); + try { + responseAck(request, Response.NOT_FOUND, "channel not found"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + log.info("[deviceControl] 命令: {}, 设备: {}({}), 通道{}({}", type, device.getName(), device.getDeviceId(), + deviceChannel.getName(), deviceChannel.getDeviceId()); try { HomePositionRequest homePosition = loadElement(rootElement, HomePositionRequest.class); //获取整个消息主体,我们只需要修改请求头即可 HomePositionRequest.HomePosition info = homePosition.getHomePosition(); - cmder.homePositionCmd(device, channelId, !"0".equals(info.getEnabled()), Integer.parseInt(info.getResetTime()), Integer.parseInt(info.getPresetIndex()), + cmder.homePositionCmd(device, deviceChannel.getDeviceId(), !"0".equals(info.getEnabled()), Integer.parseInt(info.getResetTime()), Integer.parseInt(info.getPresetIndex()), errorResult -> onError(request, errorResult), okResult -> onOk(request, okResult)); } catch (Exception e) { - logger.error("[命令发送失败] 看守位设置: {}", e.getMessage()); + log.error("[命令发送失败] 看守位设置: {}", e.getMessage()); } } /** - * 处理告警消息*** - * - * @param device 设备信息 - * @param rootElement 根节点 - * @param request 请求信息 + * 处理告警消息 */ - private void handleAlarmCmd(Device device, Element rootElement, SIPRequest request) { + private void handleAlarmCmd(CommonGBChannel channel, Element rootElement, SIPRequest request) { + if (channel.getGbDeviceDbId() == 0) { + // 只支持国标的云台控制 + log.warn("[INFO 消息] 只支持国标的告警消息, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.FORBIDDEN, ""); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + // 根据通道ID,获取所属设备 + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); + if (device == null) { + // 不存在则回复404 + log.warn("[INFO 消息] 通道所属设备不存在, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.NOT_FOUND, "device not found"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } //告警方法 String alarmMethod = ""; //告警类型 @@ -280,40 +420,88 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent errorResult -> onError(request, errorResult), okResult -> onOk(request, okResult)); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 告警消息: {}", e.getMessage()); + log.error("[命令发送失败] 告警消息: {}", e.getMessage()); } } /** * 处理录像控制 - * - * @param device 设备信息 - * @param channelId 通道id - * @param rootElement 根节点 - * @param request 请求信息 - * @param type 消息类型 */ - private void handleRecordCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) { + private void handleRecordCmd(CommonGBChannel channel, Element rootElement, SIPRequest request, DeviceControlType type) { + if (channel.getGbDeviceDbId() == 0) { + // 只支持国标的云台控制 + log.warn("[INFO 消息] 只支持国标的息录像控制, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.FORBIDDEN, ""); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + // 根据通道ID,获取所属设备 + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); + if (device == null) { + // 不存在则回复404 + log.warn("[INFO 消息] 通道所属设备不存在, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.NOT_FOUND, "device not found"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId()); + if (deviceChannel == null) { + // 拒绝远程启动命令 + log.warn("[deviceControl] 未找到设备原始通道, 设备: {}({}),通道编号:{}", device.getName(), + device.getDeviceId(), channel.getGbId()); + try { + responseAck(request, Response.NOT_FOUND, "channel not found"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + log.info("[deviceControl] 命令: {}, 设备: {}({}), 通道{}({}", type, device.getName(), device.getDeviceId(), + deviceChannel.getName(), deviceChannel.getDeviceId()); //获取整个消息主体,我们只需要修改请求头即可 String cmdString = getText(rootElement, type.getVal()); try { - cmder.recordCmd(device, channelId, cmdString, + cmder.recordCmd(device, deviceChannel.getDeviceId(), cmdString, errorResult -> onError(request, errorResult), okResult -> onOk(request, okResult)); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 录像控制: {}", e.getMessage()); + log.error("[命令发送失败] 录像控制: {}", e.getMessage()); } } /** * 处理报警布防/撤防命令 - * - * @param device 设备信息 - * @param rootElement 根节点 - * @param request 请求信息 - * @param type 消息类型 */ - private void handleGuardCmd(Device device, Element rootElement, SIPRequest request, DeviceControlType type) { + private void handleGuardCmd(CommonGBChannel channel, Element rootElement, SIPRequest request, DeviceControlType type) { + if (channel.getGbDeviceDbId() == 0) { + // 只支持国标的云台控制 + log.warn("[INFO 消息] 只支持国标的报警布防/撤防命令, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.FORBIDDEN, ""); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + // 根据通道ID,获取所属设备 + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); + if (device == null) { + // 不存在则回复404 + log.warn("[INFO 消息] 通道所属设备不存在, 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.NOT_FOUND, "device not found"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } //获取整个消息主体,我们只需要修改请求头即可 String cmdString = getText(rootElement, type.getVal()); try { @@ -321,7 +509,7 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent errorResult -> onError(request, errorResult), okResult -> onOk(request, okResult)); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 布防/撤防命令: {}", e.getMessage()); + log.error("[命令发送失败] 布防/撤防命令: {}", e.getMessage()); } } @@ -337,7 +525,7 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent try { responseAck(request, eventResult.statusCode, eventResult.msg); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 回复: {}", e.getMessage()); + log.error("[命令发送失败] 回复: {}", e.getMessage()); } } @@ -352,7 +540,7 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent try { responseAck(request, eventResult.statusCode); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 回复: {}", e.getMessage()); + log.error("[命令发送失败] 回复: {}", e.getMessage()); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java index ec4a3993f..293f3e374 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java @@ -6,20 +6,18 @@ import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.service.IDeviceAlarmService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler; import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; -import com.genersoft.iot.vmp.service.IDeviceAlarmService; -import com.genersoft.iot.vmp.service.IDeviceChannelService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.utils.DateUtil; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -39,10 +37,10 @@ import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; /** * 报警事件的处理,参考:9.4 */ +@Slf4j @Component public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private final Logger logger = LoggerFactory.getLogger(AlarmNotifyMessageHandler.class); private final String cmdType = "Alarm"; @Autowired @@ -57,9 +55,6 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme @Autowired private SipConfig sipConfig; - @Autowired - private IVideoManagerStorage storager; - @Autowired private IRedisCatchStorage redisCatchStorage; @@ -75,9 +70,6 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme @Autowired private ThreadPoolTaskExecutor taskExecutor; - @Autowired - private EventPublisher eventPublisher; - @Override public void afterPropertiesSet() throws Exception { @@ -86,18 +78,19 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme @Override public void handForDevice(RequestEvent evt, Device device, Element rootElement) { - logger.info("[收到报警通知]设备:{}", device.getDeviceId()); boolean isEmpty = taskQueue.isEmpty(); taskQueue.offer(new SipMsgInfo(evt, device, rootElement)); // 回复200 OK try { responseAck((SIPRequest) evt.getRequest(), Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 报警通知回复: {}", e.getMessage()); + log.error("[命令发送失败] 报警通知回复: {}", e.getMessage()); } if (isEmpty) { taskExecutor.execute(() -> { - logger.info("[处理报警通知]待处理数量:{}", taskQueue.size() ); + if (log.isDebugEnabled()) { + log.info("[处理报警通知]待处理数量:{}", taskQueue.size() ); + } while (!taskQueue.isEmpty()) { try { SipMsgInfo sipMsgInfo = taskQueue.poll(); @@ -135,32 +128,25 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme deviceAlarm.setLatitude(0.00); } - if (!ObjectUtils.isEmpty(deviceAlarm.getAlarmMethod())) { - if ( deviceAlarm.getAlarmMethod().contains(DeviceAlarmMethod.GPS.getVal() + "")) { + if (!ObjectUtils.isEmpty(deviceAlarm.getAlarmMethod()) && deviceAlarm.getAlarmMethod().contains(DeviceAlarmMethod.GPS.getVal() + "")) { + DeviceChannel deviceChannel = deviceChannelService.getOne(device.getDeviceId(), channelId); + if (deviceChannel == null) { + log.warn("[解析报警消息] 未找到通道:{}/{}", device.getDeviceId(), channelId); + } else { MobilePosition mobilePosition = new MobilePosition(); mobilePosition.setCreateTime(DateUtil.getNow()); mobilePosition.setDeviceId(deviceAlarm.getDeviceId()); - mobilePosition.setChannelId(channelId); + mobilePosition.setChannelId(deviceChannel.getId()); mobilePosition.setTime(deviceAlarm.getAlarmTime()); mobilePosition.setLongitude(deviceAlarm.getLongitude()); mobilePosition.setLatitude(deviceAlarm.getLatitude()); mobilePosition.setReportSource("GPS Alarm"); // 更新device channel 的经纬度 - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setDeviceId(sipMsgInfo.getDevice().getDeviceId()); - deviceChannel.setChannelId(channelId); deviceChannel.setLongitude(mobilePosition.getLongitude()); deviceChannel.setLatitude(mobilePosition.getLatitude()); deviceChannel.setGpsTime(mobilePosition.getTime()); - deviceChannel = deviceChannelService.updateGps(deviceChannel, sipMsgInfo.getDevice()); - - mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); - mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); - mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); - mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); - deviceChannelService.updateChannelGPS(device, deviceChannel, mobilePosition); } } @@ -169,11 +155,13 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme deviceAlarm.setAlarmType(getText(sipMsgInfo.getRootElement().element("Info"), "AlarmType")); } } - logger.info("[收到报警通知]内容:{}", JSON.toJSONString(deviceAlarm)); + if (log.isDebugEnabled()) { + log.debug("[收到报警通知]设备:{}, 内容:{}", device.getDeviceId(), JSON.toJSONString(deviceAlarm)); + } // 作者自用判断,其他小伙伴需要此消息可以自行修改,但是不要提在pr里 if (DeviceAlarmMethod.Other.getVal() == Integer.parseInt(deviceAlarm.getAlarmMethod())) { // 发送给平台的报警信息。 发送redis通知 - logger.info("[发送给平台的报警信息]内容:{}", JSONObject.toJSONString(deviceAlarm)); + log.info("[发送给平台的报警信息]内容:{}", JSONObject.toJSONString(deviceAlarm)); AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage(); if (deviceAlarm.getAlarmMethod() != null) { alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod())); @@ -187,7 +175,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme continue; } - logger.debug("存储报警信息、报警分类"); + log.debug("存储报警信息、报警分类"); // 存储报警信息、报警分类 if (sipConfig.isAlarm()) { deviceAlarmService.add(deviceAlarm); @@ -197,8 +185,8 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme publisher.deviceAlarmEventPublish(deviceAlarm); } }catch (Exception e) { - logger.error("未处理的异常 ", e); - logger.warn("[收到报警通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest()); + log.error("未处理的异常 ", e); + log.warn("[收到报警通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest()); } } }); @@ -206,13 +194,13 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { - logger.info("收到来自平台[{}]的报警通知", parentPlatform.getServerGBId()); + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element rootElement) { + log.info("收到来自平台[{}]的报警通知", parentPlatform.getServerGBId()); // 回复200 OK try { responseAck((SIPRequest) evt.getRequest(), Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 报警通知回复: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 报警通知回复: {}", e.getMessage()); } Element deviceIdElement = rootElement.element("DeviceID"); String channelId = deviceIdElement.getText().toString(); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java index bb1b37926..f278ef308 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.service.*; import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; @@ -9,15 +10,10 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessag import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.service.IDeviceService; -import com.genersoft.iot.vmp.service.IPlatformService; -import com.genersoft.iot.vmp.service.IPlayService; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.service.ISendRtpServerService; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -29,19 +25,19 @@ import javax.sip.message.Response; import java.text.ParseException; /** - * 状态信息(心跳)报送 + * 语音喊话请求 */ +@Slf4j @Component public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(BroadcastNotifyMessageHandler.class); private final static String cmdType = "Broadcast"; @Autowired private NotifyMessageHandler notifyMessageHandler; @Autowired - private IVideoManagerStorage storage; + private IGbChannelService channelService; @Autowired private ISIPCommanderForPlatform commanderForPlatform; @@ -55,6 +51,9 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp @Autowired private IDeviceService deviceService; + @Autowired + private IDeviceChannelService deviceChannelService; + @Autowired private IPlatformService platformService; @@ -62,7 +61,7 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp private AudioBroadcastManager audioBroadcastManager; @Autowired - private IRedisCatchStorage redisCatchStorage; + private ISendRtpServerService sendRtpServerService; @Override public void afterPropertiesSet() throws Exception { @@ -75,7 +74,7 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform platform, Element rootElement) { + public void handForPlatform(RequestEvent evt, Platform platform, Element rootElement) { // 来自上级平台的语音喊话请求 SIPRequest request = (SIPRequest) evt.getRequest(); try { @@ -93,44 +92,50 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp String targetId = targetIDElement.getText(); - logger.info("[国标级联 语音喊话] platform: {}, channel: {}", platform.getServerGBId(), targetId); + log.info("[国标级联 语音喊话] platform: {}, channel: {}", platform.getServerGBId(), targetId); - DeviceChannel deviceChannel = storage.queryChannelInParentPlatform(platform.getServerGBId(), targetId); - if (deviceChannel == null) { - logger.warn("[国标级联 语音喊话] 未找到通道 platform: {}, channel: {}", platform.getServerGBId(), targetId); + CommonGBChannel channel = channelService.queryOneWithPlatform(platform.getId(), targetId); + if (channel == null) { + log.warn("[国标级联 语音喊话] 未找到通道 platform: {}, channel: {}", platform.getServerGBId(), targetId); responseAck(request, Response.NOT_FOUND, "TargetID not found"); return; } // 向下级发送语音的喊话请求 - Device device = deviceService.getDevice(deviceChannel.getDeviceId()); + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); if (device == null) { responseAck(request, Response.NOT_FOUND, "device not found"); return; } + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId()); + if (deviceChannel == null) { + responseAck(request, Response.NOT_FOUND, "channel not found"); + return; + } responseAck(request, Response.OK); // 查看语音通道是否已经建立并且已经在使用 - if (playService.audioBroadcastInUse(device, targetId)) { - commanderForPlatform.broadcastResultCmd(platform, deviceChannel, sn, false,null, null); + if (playService.audioBroadcastInUse(device, deviceChannel)) { + commanderForPlatform.broadcastResultCmd(platform, channel, sn, false,null, null); return; } MediaServer mediaServerForMinimumLoad = mediaServerService.getMediaServerForMinimumLoad(null); - commanderForPlatform.broadcastResultCmd(platform, deviceChannel, sn, true, eventResult->{ - logger.info("[国标级联] 语音喊话 回复失败 platform: {}, 错误:{}/{}", platform.getServerGBId(), eventResult.statusCode, eventResult.msg); + commanderForPlatform.broadcastResultCmd(platform, channel, sn, true, eventResult->{ + log.info("[国标级联] 语音喊话 回复失败 platform: {}, 错误:{}/{}", platform.getServerGBId(), eventResult.statusCode, eventResult.msg); }, eventResult->{ // 消息发送成功, 向上级发送invite,获取推流 try { - platformService.broadcastInvite(platform, deviceChannel.getChannelId(), mediaServerForMinimumLoad, (hookData)->{ + platformService.broadcastInvite(platform, channel, mediaServerForMinimumLoad, (hookData)->{ // 上级平台推流成功 - AudioBroadcastCatch broadcastCatch = audioBroadcastManager.get(device.getDeviceId(), targetId); + AudioBroadcastCatch broadcastCatch = audioBroadcastManager.get(channel.getGbId()); if (broadcastCatch != null ) { - if (playService.audioBroadcastInUse(device, targetId)) { - logger.info("[国标级联] 语音喊话 设备正在使用中 platform: {}, channel: {}", - platform.getServerGBId(), deviceChannel.getChannelId()); + + if (playService.audioBroadcastInUse(device, deviceChannel)) { + log.info("[国标级联] 语音喊话 设备正在使用中 platform: {}, channel: {}", + platform.getServerGBId(), channel.getGbDeviceId()); // 查看语音通道已经建立且已经占用 回复BYE - platformService.stopBroadcast(platform, deviceChannel, hookData.getStream(), true, hookData.getMediaServer()); + platformService.stopBroadcast(platform, channel, hookData.getStream(), true, hookData.getMediaServer()); }else { // 查看语音通道已经建立但是未占用 broadcastCatch.setApp(hookData.getApp()); @@ -138,53 +143,53 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp broadcastCatch.setMediaServerItem(hookData.getMediaServer()); audioBroadcastManager.update(broadcastCatch); // 推流到设备 - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, targetId, hookData.getStream(), null); + SendRtpInfo sendRtpItem = sendRtpServerService.queryByStream(hookData.getStream(), targetId); if (sendRtpItem == null) { - logger.warn("[国标级联] 语音喊话 异常,未找到发流信息, channelId: {}, stream: {}", targetId, hookData.getStream()); - logger.info("[国标级联] 语音喊话 重新开始,channelId: {}, stream: {}", targetId, hookData.getStream()); + log.warn("[国标级联] 语音喊话 异常,未找到发流信息, channelId: {}, stream: {}", targetId, hookData.getStream()); + log.info("[国标级联] 语音喊话 重新开始,channelId: {}, stream: {}", targetId, hookData.getStream()); try { - playService.audioBroadcastCmd(device, targetId, hookData.getMediaServer(), hookData.getApp(), hookData.getStream(), 60, true, msg -> { - logger.info("[语音喊话] 通道建立成功, device: {}, channel: {}", device.getDeviceId(), targetId); + playService.audioBroadcastCmd(device, deviceChannel, hookData.getMediaServer(), hookData.getApp(), hookData.getStream(), 60, true, msg -> { + log.info("[语音喊话] 通道建立成功, device: {}, channel: {}", device.getDeviceId(), targetId); }); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.info("[消息发送失败] 国标级联 语音喊话 platform: {}", platform.getServerGBId()); + log.info("[消息发送失败] 国标级联 语音喊话 platform: {}", platform.getServerGBId()); } }else { // 发流 try { mediaServerService.startSendRtp(hookData.getMediaServer(), sendRtpItem); }catch (ControllerException e) { - logger.info("[语音喊话] 推流失败, 结果: {}", e.getMessage()); + log.info("[语音喊话] 推流失败, 结果: {}", e.getMessage()); return; } - logger.info("[语音喊话] 自动推流成功, device: {}, channel: {}", device.getDeviceId(), targetId); + log.info("[语音喊话] 自动推流成功, device: {}, channel: {}", device.getDeviceId(), targetId); } } }else { try { - playService.audioBroadcastCmd(device, targetId, hookData.getMediaServer(), hookData.getApp(), hookData.getStream(), 60, true, msg -> { - logger.info("[语音喊话] 通道建立成功, device: {}, channel: {}", device.getDeviceId(), targetId); + playService.audioBroadcastCmd(device, deviceChannel, hookData.getMediaServer(), hookData.getApp(), hookData.getStream(), 60, true, msg -> { + log.info("[语音喊话] 通道建立成功, device: {}, channel: {}", device.getDeviceId(), targetId); }); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.info("[消息发送失败] 国标级联 语音喊话 platform: {}", platform.getServerGBId()); + log.info("[消息发送失败] 国标级联 语音喊话 platform: {}", platform.getServerGBId()); } } }, eventResultForBroadcastInvite -> { // 收到错误 - logger.info("[国标级联-语音喊话] 与下级通道建立失败 device: {}, channel: {}, 错误:{}/{}", device.getDeviceId(), + log.info("[国标级联-语音喊话] 与下级通道建立失败 device: {}, channel: {}, 错误:{}/{}", device.getDeviceId(), targetId, eventResultForBroadcastInvite.statusCode, eventResultForBroadcastInvite.msg); }, (code, msg)->{ // 超时 - logger.info("[国标级联-语音喊话] 与下级通道建立超时 device: {}, channel: {}, 错误:{}/{}", device.getDeviceId(), + log.info("[国标级联-语音喊话] 与下级通道建立超时 device: {}, channel: {}, 错误:{}/{}", device.getDeviceId(), targetId, code, msg); }); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.info("[消息发送失败] 国标级联 语音喊话 invite消息 platform: {}", platform.getServerGBId()); + log.info("[消息发送失败] 国标级联 语音喊话 invite消息 platform: {}", platform.getServerGBId()); } }); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.info("[消息发送失败] 国标级联 语音喊话 platform: {}", platform.getServerGBId()); + log.info("[消息发送失败] 国标级联 语音喊话 platform: {}", platform.getServerGBId()); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java index 85f2da747..aa0e05e78 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java @@ -4,19 +4,18 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; -import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.utils.DateUtil; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -30,11 +29,11 @@ import java.text.ParseException; /** * 状态信息(心跳)报送 */ +@Slf4j @Component public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private final Logger logger = LoggerFactory.getLogger(KeepaliveNotifyMessageHandler.class); private final static String cmdType = "Keepalive"; @Autowired @@ -61,25 +60,25 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp return; } SIPRequest request = (SIPRequest) evt.getRequest(); - logger.info("[收到心跳] device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); + log.debug("[收到心跳] device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); if (userSetting.getGbDeviceOnline() == 0 && !device.isOnLine()) { - logger.warn("[收到心跳] 设备离线,心跳不进行回复, device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); + log.warn("[收到心跳] 设备离线,心跳不进行回复, device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); return; } // 回复200 OK try { responseAck(request, Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 心跳回复: {}", e.getMessage()); + log.error("[命令发送失败] 心跳回复: {}", e.getMessage()); } if (!ObjectUtils.isEmpty(device.getKeepaliveTime()) && DateUtil.getDifferenceForNow(device.getKeepaliveTime()) <= 3000L) { - logger.info("[收到心跳] 心跳发送过于频繁,已忽略 device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); + log.info("[收到心跳] 心跳发送过于频繁,已忽略 device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); return; } RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress()); if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort() || !request.getRemoteAddress().getHostAddress().equals(device.getLocalIp())) { - logger.info("[收到心跳] 设备{}地址变化, 远程地址为: {}:{}", device.getDeviceId(), remoteAddressInfo.getIp(), remoteAddressInfo.getPort()); + log.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()); @@ -120,12 +119,12 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element element) { // 个别平台保活不回复200OK会判定离线 try { responseAck((SIPRequest) evt.getRequest(), Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 心跳回复: {}", e.getMessage()); + log.error("[命令发送失败] 心跳回复: {}", e.getMessage()); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java index f8ff69cf1..ba248e263 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java @@ -2,27 +2,28 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify import com.genersoft.iot.vmp.common.InviteInfo; import com.genersoft.iot.vmp.common.InviteSessionType; -import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; +import com.genersoft.iot.vmp.gb28181.service.IPlatformService; +import com.genersoft.iot.vmp.gb28181.service.IPlayService; +import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; 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.transmit.cmd.impl.SIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler; import com.genersoft.iot.vmp.media.event.hook.Hook; import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; import com.genersoft.iot.vmp.media.event.hook.HookType; -import com.genersoft.iot.vmp.service.IInviteStreamService; +import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -39,10 +40,10 @@ import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; /** * 媒体通知 */ +@Slf4j @Component public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(MediaStatusNotifyMessageHandler.class); private final String cmdType = "MediaStatus"; @Autowired @@ -52,16 +53,13 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i private SIPCommander cmder; @Autowired - private SIPCommanderFroPlatform sipCommanderFroPlatform; + private SIPCommanderForPlatform sipCommanderFroPlatform; @Autowired private IRedisCatchStorage redisCatchStorage; @Autowired - private IVideoManagerStorage storage; - - @Autowired - private VideoStreamSessionManager sessionManager; + private IPlatformService platformService; @Autowired private HookSubscribe subscribe; @@ -70,7 +68,16 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i private IInviteStreamService inviteStreamService; @Autowired - private VideoStreamSessionManager streamSession; + private SipInviteSessionManager sessionManager; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private IPlayService playService; + + @Autowired + private ISendRtpServerService sendRtpServerService; @Override public void afterPropertiesSet() throws Exception { @@ -79,57 +86,49 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i @Override public void handForDevice(RequestEvent evt, Device device, Element rootElement) { - // 回复200 OK try { responseAck((SIPRequest) evt.getRequest(), Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 录像流推送完毕,回复200OK: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 录像流推送完毕,回复200OK: {}", e.getMessage()); } CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); String NotifyType =getText(rootElement, "NotifyType"); if ("121".equals(NotifyType)){ - logger.info("[录像流]推送完毕,收到关流通知"); + log.info("[录像流]推送完毕,收到关流通知"); - SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(null, null, callIdHeader.getCallId(), null); + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByCallId(callIdHeader.getCallId()); if (ssrcTransaction != null) { - logger.info("[录像流]推送完毕,关流通知, device: {}, channelId: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); - InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); - if (inviteInfo.getStreamInfo() != null) { - inviteInfo.getStreamInfo().setProgress(1); - inviteStreamService.updateInviteInfo(inviteInfo); - } - - try { - cmder.streamByeCmd(device, ssrcTransaction.getChannelId(), null, callIdHeader.getCallId()); - } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { - logger.error("[录像流]推送完毕,收到关流通知, 发送BYE失败 {}", e.getMessage()); + log.info("[录像流]推送完毕,关流通知, device: {}, channelId: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); + InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); + if (inviteInfo != null) { + playService.stop(inviteInfo); } // 去除监听流注销自动停止下载的监听 Hook hook = Hook.getInstance(HookType.on_media_arrival, "rtp", ssrcTransaction.getStream(), ssrcTransaction.getMediaServerId()); subscribe.removeSubscribe(hook); // 如果级联播放,需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题,需要将点播CallId进行上下级绑定 - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, ssrcTransaction.getChannelId(), null, null); + SendRtpInfo sendRtpItem = sendRtpServerService.queryByChannelId(ssrcTransaction.getChannelId(), ssrcTransaction.getPlatformId()); if (sendRtpItem != null) { - ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(sendRtpItem.getPlatformId()); + Platform parentPlatform = platformService.queryPlatformByServerGBId(sendRtpItem.getTargetId()); if (parentPlatform == null) { - logger.warn("[级联消息发送]:发送MediaStatus发现上级平台{}不存在", sendRtpItem.getPlatformId()); + log.warn("[级联消息发送]:发送MediaStatus发现上级平台{}不存在", sendRtpItem.getTargetId()); return; } try { sipCommanderFroPlatform.sendMediaStatusNotify(parentPlatform, sendRtpItem); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 录像播放完毕: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 录像播放完毕: {}", e.getMessage()); } } }else { - logger.info("[录像流]推送完毕,关流通知, 但是未找到对应的下载信息"); + log.info("[录像流]推送完毕,关流通知, 但是未找到对应的下载信息"); } } } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element element) { } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java index bc588c95b..f8bb55703 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java @@ -1,22 +1,17 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd; -import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.*; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler; import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; -import com.genersoft.iot.vmp.service.IDeviceChannelService; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.utils.DateUtil; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.DocumentException; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -36,30 +31,18 @@ import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; /** * 移动设备位置数据通知,设备主动发起,不需要上级订阅 */ +@Slf4j @Component public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(MobilePositionNotifyMessageHandler.class); private final String cmdType = "MobilePosition"; @Autowired private NotifyMessageHandler notifyMessageHandler; - @Autowired - private UserSetting userSetting; - - @Autowired - private IVideoManagerStorage storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - @Autowired private IDeviceChannelService deviceChannelService; - @Autowired - private EventPublisher eventPublisher; - private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @Qualifier("taskExecutor") @@ -80,7 +63,7 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen try { responseAck((SIPRequest) evt.getRequest(), Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 移动位置通知回复: {}", e.getMessage()); + log.error("[命令发送失败] 移动位置通知回复: {}", e.getMessage()); } if (isEmpty) { taskExecutor.execute(() -> { @@ -89,16 +72,24 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen try { Element rootElementAfterCharset = getRootElement(sipMsgInfo.getEvt(), sipMsgInfo.getDevice().getCharset()); if (rootElementAfterCharset == null) { - logger.warn("[移动位置通知] {}处理失败,未识别到信息体", device.getDeviceId()); + log.warn("[移动位置通知] {}处理失败,未识别到信息体", device.getDeviceId()); continue; } + String channelId = getText(rootElementAfterCharset, "DeviceID"); + DeviceChannel deviceChannel = deviceChannelService.getOne(device.getDeviceId(), channelId); + if (deviceChannel == null) { + log.warn("[解析报警消息] 未找到通道:{}/{}", device.getDeviceId(), channelId); + continue; + } + MobilePosition mobilePosition = new MobilePosition(); mobilePosition.setCreateTime(DateUtil.getNow()); if (!ObjectUtils.isEmpty(sipMsgInfo.getDevice().getName())) { mobilePosition.setDeviceName(sipMsgInfo.getDevice().getName()); } mobilePosition.setDeviceId(sipMsgInfo.getDevice().getDeviceId()); - mobilePosition.setChannelId(getText(rootElementAfterCharset, "DeviceID")); + + mobilePosition.setChannelId(deviceChannel.getId()); String time = getText(rootElementAfterCharset, "Time"); if (ObjectUtils.isEmpty(time)){ mobilePosition.setTime(DateUtil.getNow()); @@ -124,29 +115,18 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen } mobilePosition.setReportSource("Mobile Position"); - // 更新device channel 的经纬度 - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setDeviceId(sipMsgInfo.getDevice().getDeviceId()); - deviceChannel.setChannelId(mobilePosition.getChannelId()); deviceChannel.setLongitude(mobilePosition.getLongitude()); deviceChannel.setLatitude(mobilePosition.getLatitude()); deviceChannel.setGpsTime(mobilePosition.getTime()); - deviceChannel = deviceChannelService.updateGps(deviceChannel, sipMsgInfo.getDevice()); - - mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); - mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); - mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); - mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); - deviceChannelService.updateChannelGPS(device, deviceChannel, mobilePosition); } catch (DocumentException e) { - logger.error("未处理的异常 ", e); + log.error("未处理的异常 ", e); } catch (Exception e) { - logger.warn("[移动位置通知] 发现未处理的异常, \r\n{}", evt.getRequest()); - logger.error("[移动位置通知] 异常内容: ", e); + log.warn("[移动位置通知] 发现未处理的异常, \r\n{}", evt.getRequest()); + log.error("[移动位置通知] 异常内容: ", e); } } }); @@ -154,7 +134,7 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element element) { } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/AlarmQueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/AlarmQueryMessageHandler.java index ce8c9397a..adcfb868f 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/AlarmQueryMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/AlarmQueryMessageHandler.java @@ -1,18 +1,13 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd; -import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -23,27 +18,15 @@ import javax.sip.SipException; import javax.sip.message.Response; import java.text.ParseException; +@Slf4j @Component public class AlarmQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(AlarmQueryMessageHandler.class); private final String cmdType = "Alarm"; @Autowired private QueryMessageHandler queryMessageHandler; - @Autowired - private IVideoManagerStorage storager; - - @Autowired - private SIPCommanderFroPlatform cmderFroPlatform; - - @Autowired - private SipConfig config; - - @Autowired - private EventPublisher publisher; - @Override public void afterPropertiesSet() throws Exception { queryMessageHandler.addHandler(cmdType, this); @@ -55,13 +38,13 @@ public class AlarmQueryMessageHandler extends SIPRequestProcessorParent implemen } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element rootElement) { - logger.info("不支持alarm查询"); + log.info("不支持alarm查询"); try { responseAck((SIPRequest) evt.getRequest(), Response.NOT_FOUND, "not support alarm query"); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 alarm查询回复200OK: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 alarm查询回复200OK: {}", e.getMessage()); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java index 25a3df375..ee389cf0c 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java @@ -1,19 +1,17 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd; -import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; +import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -24,32 +22,26 @@ import javax.sip.SipException; import javax.sip.header.FromHeader; import javax.sip.message.Response; import java.text.ParseException; -import java.util.ArrayList; import java.util.List; +@Slf4j @Component public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(CatalogQueryMessageHandler.class); private final String cmdType = "Catalog"; @Autowired private QueryMessageHandler queryMessageHandler; @Autowired - private IVideoManagerStorage storager; + private IGbChannelService channelService; @Autowired - private SIPCommanderFroPlatform cmderFroPlatform; + private IPlatformChannelService platformChannelService; @Autowired - private SipConfig config; + private SIPCommanderForPlatform cmderFroPlatform; - @Autowired - private EventPublisher publisher; - - @Autowired - private IVideoManagerStorage storage; @Override public void afterPropertiesSet() throws Exception { @@ -58,73 +50,35 @@ public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implem @Override public void handForDevice(RequestEvent evt, Device device, Element element) { - + try { + // 回复200 OK + responseAck((SIPRequest) evt.getRequest(), Response.FORBIDDEN); + } catch (SipException | InvalidArgumentException | ParseException ignored) {} } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + public void handForPlatform(RequestEvent evt, Platform platform, Element rootElement) { FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); try { // 回复200 OK responseAck((SIPRequest) evt.getRequest(), Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 目录查询回复200OK: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 目录查询回复200OK: {}", e.getMessage()); } Element snElement = rootElement.element("SN"); String sn = snElement.getText(); - // 准备回复通道信息 - List deviceChannelInPlatforms = storager.queryChannelWithCatalog(parentPlatform.getServerGBId()); - // 查询关联的直播通道 - List gbStreams = storager.queryGbStreamListInPlatform(parentPlatform.getServerGBId()); - // 回复目录信息 - List catalogs = storager.queryCatalogInPlatform(parentPlatform.getServerGBId()); + List channelList = platformChannelService.queryByPlatform(platform); - List allChannels = new ArrayList<>(); - - // 回复平台 -// DeviceChannel deviceChannel = getChannelForPlatform(parentPlatform); -// allChannels.add(deviceChannel); - - // 回复目录 - if (catalogs.size() > 0) { - allChannels.addAll(catalogs); - } - // 回复级联的通道 - if (deviceChannelInPlatforms.size() > 0) { - allChannels.addAll(deviceChannelInPlatforms); - } - // 回复直播的通道 - if (gbStreams.size() > 0) { - allChannels.addAll(gbStreams); - } try { - if (allChannels.size() > 0) { - cmderFroPlatform.catalogQuery(allChannels, parentPlatform, sn, fromHeader.getTag()); + if (!channelList.isEmpty()) { + cmderFroPlatform.catalogQuery(channelList, platform, sn, fromHeader.getTag()); }else { // 回复无通道 - cmderFroPlatform.catalogQuery(null, parentPlatform, sn, fromHeader.getTag(), 0); + cmderFroPlatform.catalogQuery(null, platform, sn, fromHeader.getTag(), 0); } } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage()); } - - - - } - - private DeviceChannel getChannelForPlatform(ParentPlatform platform) { - DeviceChannel deviceChannel = new DeviceChannel(); - - deviceChannel.setChannelId(platform.getDeviceGBId()); - deviceChannel.setName(platform.getName()); - deviceChannel.setManufacture("wvp-pro"); - deviceChannel.setOwner("wvp-pro"); - deviceChannel.setCivilCode(platform.getAdministrativeDivision()); - deviceChannel.setAddress("wvp-pro"); - deviceChannel.setRegisterWay(0); - deviceChannel.setSecrecy("0"); - - return deviceChannel; } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java index 8b774e100..ca577440d 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java @@ -1,16 +1,18 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -24,19 +26,26 @@ import java.text.ParseException; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; +@Slf4j @Component public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(DeviceInfoQueryMessageHandler.class); private final String cmdType = "DeviceInfo"; @Autowired private QueryMessageHandler queryMessageHandler; @Autowired - private SIPCommanderFroPlatform cmderFroPlatform; + private SIPCommanderForPlatform cmderFroPlatform; + @Autowired - private IVideoManagerStorage storager; + private IDeviceService deviceService; + + @Autowired + private IGbChannelService channelService; + + @Autowired + private IDeviceChannelService deviceChannelService; @Override public void afterPropertiesSet() throws Exception { @@ -49,16 +58,11 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { - logger.info("[DeviceInfo查询]消息"); + public void handForPlatform(RequestEvent evt, Platform platform, Element rootElement) { + log.info("[DeviceInfo查询]消息"); + SIPRequest request = (SIPRequest) evt.getRequest(); FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); - try { - // 回复200 OK - responseAck((SIPRequest) evt.getRequest(), Response.OK); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] DeviceInfo查询回复: {}", e.getMessage()); - return; - } + String sn = rootElement.element("SN").getText(); /*根据WVP原有的数据结构,设备和通道是分开放置,设备信息都是存放在设备表里,通道表里的设备信息不可作为真实信息处理 @@ -67,19 +71,65 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp String channelId = getText(rootElement, "DeviceID"); // 查询这是通道id还是设备id - Device device = null; - // 如果id指向平台的国标编号,那么就是查询平台的信息 - if (!parentPlatform.getDeviceGBId().equals(channelId)) { - device = storager.queryDeviceInfoByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId); - if (device ==null){ - logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId); + if (platform.getDeviceGBId().equals(channelId)) { + // id指向平台的国标编号,那么就是查询平台的信息 + try { + cmderFroPlatform.deviceInfoResponse(platform, null, sn, fromHeader.getTag()); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 国标级联 DeviceInfo查询回复: {}", e.getMessage()); + } + return; + } + CommonGBChannel channel = channelService.queryOneWithPlatform(platform.getId(), channelId); + if (channel == null) { + // 不存在则回复404 + log.warn("[DeviceInfo] 通道不存在: 通道编号: {}", channelId); + try { + responseAck(request, Response.NOT_FOUND, "channel not found or offline"); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] DeviceInfo查询回复: {}", e.getMessage()); return; } + return; + } + // 判断通道类型 + if (channel.getGbDeviceDbId() == null) { + // 非国标通道不支持录像回放控制 + log.warn("[DeviceInfo] 非国标通道不支持录像回放控制: 通道ID: {}", channel.getGbId()); + try { + responseAck(request, Response.FORBIDDEN, ""); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] DeviceInfo查询回复: {}", e.getMessage()); + return; + } + return; + } + + // 根据通道ID,获取所属设备 + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); + if (device == null) { + // 不存在则回复404 + log.warn("[DeviceInfo] 通道所属设备不存在, 通道ID: {}", channel.getGbDeviceDbId()); + + try { + responseAck(request, Response.NOT_FOUND, "device not found "); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] DeviceInfo查询回复: {}", e.getMessage()); + return; + } + return; } try { - cmderFroPlatform.deviceInfoResponse(parentPlatform, device, sn, fromHeader.getTag()); + // 回复200 OK + responseAck((SIPRequest) evt.getRequest(), Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 DeviceInfo查询回复: {}", e.getMessage()); + log.error("[命令发送失败] DeviceInfo查询回复: {}", e.getMessage()); + return; + } + try { + cmderFroPlatform.deviceInfoResponse(platform, device, sn, fromHeader.getTag()); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 国标级联 DeviceInfo查询回复: {}", e.getMessage()); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceStatusQueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceStatusQueryMessageHandler.java index f9edfb396..2b3b8fccd 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceStatusQueryMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceStatusQueryMessageHandler.java @@ -1,19 +1,16 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd; -import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -27,26 +24,20 @@ import java.text.ParseException; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; +@Slf4j @Component public class DeviceStatusQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(DeviceStatusQueryMessageHandler.class); private final String cmdType = "DeviceStatus"; @Autowired private QueryMessageHandler queryMessageHandler; @Autowired - private IVideoManagerStorage storager; + private IGbChannelService channelService; @Autowired - private SIPCommanderFroPlatform cmderFroPlatform; - - @Autowired - private SipConfig config; - - @Autowired - private EventPublisher publisher; + private ISIPCommanderForPlatform cmderFroPlatform; @Override public void afterPropertiesSet() throws Exception { @@ -59,27 +50,27 @@ public class DeviceStatusQueryMessageHandler extends SIPRequestProcessorParent i } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element rootElement) { - logger.info("接收到DeviceStatus查询消息"); + log.info("接收到DeviceStatus查询消息"); FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); // 回复200 OK try { responseAck((SIPRequest) evt.getRequest(), Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 DeviceStatus查询回复200OK: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 DeviceStatus查询回复200OK: {}", e.getMessage()); } String sn = rootElement.element("SN").getText(); String channelId = getText(rootElement, "DeviceID"); - DeviceChannel deviceChannel = storager.queryChannelInParentPlatform(parentPlatform.getServerGBId(), channelId); - if (deviceChannel ==null){ - logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId); + CommonGBChannel channel= channelService.queryOneWithPlatform(parentPlatform.getId(), channelId); + if (channel ==null){ + log.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId); return; } try { - cmderFroPlatform.deviceStatusResponse(parentPlatform,channelId, sn, fromHeader.getTag(),deviceChannel.isStatus()); + cmderFroPlatform.deviceStatusResponse(parentPlatform, channelId, sn, fromHeader.getTag(), "ON".equalsIgnoreCase(channel.getGbStatus())); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 DeviceStatus查询回复: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 DeviceStatus查询回复: {}", e.getMessage()); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java index e2f9cda9e..79deb0423 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java @@ -1,21 +1,22 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd; -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.bean.*; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.event.record.RecordEndEventListener; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; 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.transmit.cmd.impl.SIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler; import com.genersoft.iot.vmp.utils.DateUtil; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; -import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -25,22 +26,27 @@ import javax.sip.RequestEvent; import javax.sip.SipException; import javax.sip.message.Response; import java.text.ParseException; -import java.util.List; +@Slf4j @Component public class RecordInfoQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(RecordInfoQueryMessageHandler.class); private final String cmdType = "RecordInfo"; @Autowired private QueryMessageHandler queryMessageHandler; @Autowired - private IVideoManagerStorage storager; + private IGbChannelService channelService; @Autowired - private SIPCommanderFroPlatform cmderFroPlatform; + private IDeviceService deviceService; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private SIPCommanderForPlatform cmderFroPlatform; @Autowired private SIPCommander commander; @@ -48,12 +54,6 @@ public class RecordInfoQueryMessageHandler extends SIPRequestProcessorParent imp @Autowired private RecordEndEventListener recordEndEventListener; - @Autowired - private SipConfig config; - - @Autowired - private EventPublisher publisher; - @Override public void afterPropertiesSet() throws Exception { queryMessageHandler.addHandler(cmdType, this); @@ -65,7 +65,7 @@ public class RecordInfoQueryMessageHandler extends SIPRequestProcessorParent imp } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + public void handForPlatform(RequestEvent evt, Platform platform, Element rootElement) { SIPRequest request = (SIPRequest) evt.getRequest(); Element snElement = rootElement.element("SN"); @@ -92,56 +92,67 @@ public class RecordInfoQueryMessageHandler extends SIPRequestProcessorParent imp if (typeElement != null) { type = typeElement.getText(); } - // 确认是直播还是国标, 国标直接请求下级,直播请求录像管理服务 - List channelSources = storager.getChannelSource(parentPlatform.getServerGBId(), channelId); - if (channelSources.get(0).getCount() > 0) { // 国标 - // 向国标设备请求录像数据 - Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId); - DeviceChannel deviceChannel = storager.queryChannelInParentPlatform(parentPlatform.getServerGBId(), channelId); - // 接收录像数据 - recordEndEventListener.addEndEventHandler(deviceChannel.getDeviceId(), channelId, (recordInfo)->{ - try { - logger.info("[国标级联] 录像查询收到数据, 通道: {},准备转发===", channelId); - cmderFroPlatform.recordInfo(deviceChannel, parentPlatform, request.getFromTag(), recordInfo); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 回复录像数据: {}", e.getMessage()); - } - }); - try { - commander.recordInfoQuery(device, channelId, DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTime), - DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTime), sn, secrecy, type, (eventResult -> { - // 回复200 OK - try { - responseAck(request, Response.OK); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 录像查询回复: {}", e.getMessage()); - } - }),(eventResult -> { - // 查询失败 - try { - responseAck(request, eventResult.statusCode, eventResult.msg); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 录像查询回复: {}", e.getMessage()); - } - })); - } catch (InvalidArgumentException | ParseException | SipException e) { - logger.error("[命令发送失败] 录像查询: {}", e.getMessage()); - } - - }else if (channelSources.get(1).getCount() > 0) { // 直播流 - // TODO - try { - responseAck(request, Response.NOT_IMPLEMENTED); // 回复未实现 - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 录像查询: {}", e.getMessage()); - } - }else { // 错误的请求 + // 向国标设备请求录像数据 + CommonGBChannel channel = channelService.queryOneWithPlatform(platform.getId(), channelId); + if (channel == null) { + log.info("[平台查询录像记录] 未找到通道 {}/{}", platform.getName(), channelId ); try { responseAck(request, Response.BAD_REQUEST); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 录像查询: {}", e.getMessage()); + log.error("[命令发送失败] [平台查询录像记录] 未找到通道: {}", e.getMessage()); } + return; + } + if (channel.getGbId() == 0 ) { + log.info("[平台查询录像记录] 不支持查询推流和拉流代理的录像数据 {}/{}", platform.getName(), channelId ); + try { + responseAck(request, Response.NOT_IMPLEMENTED); // 回复未实现 + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 平台查询录像记录: {}", e.getMessage()); + } + return; + } + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); + if (device == null) { + log.warn("[平台查询录像记录] 未找到通道对应的设备 {}/{}", platform.getName(), channelId ); + try { + responseAck(request, Response.BAD_REQUEST); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] [平台查询录像记录] 未找到通道对应的设备: {}", e.getMessage()); + } + return; + } + // 获取通道的原始信息 + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId()); + // 接收录像数据 + recordEndEventListener.addEndEventHandler(device.getDeviceId(), deviceChannel.getDeviceId(), (recordInfo)->{ + try { + log.info("[国标级联] 录像查询收到数据, 通道: {},准备转发===", channelId); + cmderFroPlatform.recordInfo(channel, platform, request.getFromTag(), recordInfo); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 国标级联 回复录像数据: {}", e.getMessage()); + } + }); + try { + commander.recordInfoQuery(device, deviceChannel.getDeviceId(), DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTime), + DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTime), sn, secrecy, type, (eventResult -> { + // 回复200 OK + try { + responseAck(request, Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 录像查询回复: {}", e.getMessage()); + } + }),(eventResult -> { + // 查询失败 + try { + responseAck(request, eventResult.statusCode, eventResult.msg); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 录像查询回复: {}", e.getMessage()); + } + })); + } catch (InvalidArgumentException | ParseException | SipException e) { + log.error("[命令发送失败] 录像查询: {}", e.getMessage()); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/AlarmResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/AlarmResponseMessageHandler.java index 3edf02fea..418534653 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/AlarmResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/AlarmResponseMessageHandler.java @@ -2,26 +2,25 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.respon import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.sip.RequestEvent; +@Slf4j @Component public class AlarmResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(AlarmResponseMessageHandler.class); private final String cmdType = "Alarm"; @Autowired @@ -42,8 +41,8 @@ public class AlarmResponseMessageHandler extends SIPRequestProcessorParent imple String key = DeferredResultHolder.CALLBACK_CMD_ALARM + device.getDeviceId() + channelId; JSONObject json = new JSONObject(); XmlUtil.node2Json(rootElement, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); + if (log.isDebugEnabled()) { + log.debug(json.toJSONString()); } RequestMessage msg = new RequestMessage(); msg.setKey(key); @@ -52,7 +51,7 @@ public class AlarmResponseMessageHandler extends SIPRequestProcessorParent imple } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element element) { } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java index c910451df..f0741c49d 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java @@ -1,19 +1,16 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; import com.genersoft.iot.vmp.conf.DynamicTask; -import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch; -import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatchStatus; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; -import com.genersoft.iot.vmp.service.IPlayService; +import com.genersoft.iot.vmp.gb28181.service.IPlayService; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -26,17 +23,17 @@ import java.text.ParseException; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; +@Slf4j @Component public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(BroadcastResponseMessageHandler.class); private final String cmdType = "Broadcast"; @Autowired private ResponseMessageHandler responseMessageHandler; @Autowired - private DynamicTask dynamicTask; + private IDeviceChannelService deviceChannelService; @Autowired private AudioBroadcastManager audioBroadcastManager; @@ -55,7 +52,19 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i SIPRequest request = (SIPRequest) evt.getRequest(); try { String channelId = getText(rootElement, "DeviceID"); - if (!audioBroadcastManager.exit(device.getDeviceId(), channelId)) { + DeviceChannel channel = null; + if (!channelId.equals(device.getDeviceId())) { + channel = deviceChannelService.getOne(device.getDeviceId(), channelId); + }else { + channel = deviceChannelService.getBroadcastChannel(device.getId()); + } + if (channel == null) { + log.info("[语音广播]回复: 未找到通道{}/{}", device.getDeviceId(), channelId ); + // 回复410 + responseAck((SIPRequest) evt.getRequest(), Response.NOT_FOUND); + return; + } + if (!audioBroadcastManager.exit(channel.getId())) { // 回复410 responseAck((SIPRequest) evt.getRequest(), Response.GONE); return; @@ -66,24 +75,24 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i if (infoElement != null) { reason = getText(infoElement, "Reason"); } - logger.info("[语音广播]回复:{}, {}/{}", reason == null? result : result + ": " + reason, device.getDeviceId(), channelId ); + log.info("[语音广播]回复:{}, {}/{}", reason == null? result : result + ": " + reason, device.getDeviceId(), channelId ); // 回复200 OK responseAck(request, Response.OK); if (result.equalsIgnoreCase("OK")) { - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(device.getDeviceId(), channelId); + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(channel.getId()); audioBroadcastCatch.setStatus(AudioBroadcastCatchStatus.WaiteInvite); audioBroadcastManager.update(audioBroadcastCatch); }else { - playService.stopAudioBroadcast(device.getDeviceId(), channelId); + playService.stopAudioBroadcast(device, channel); } } catch (ParseException | SipException | InvalidArgumentException e) { - logger.error("[命令发送失败] 国标级联 语音喊话: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 语音喊话: {}", e.getMessage()); } } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element element) { } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java index c16d7f567..9fc2d99fc 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java @@ -2,23 +2,23 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.respon import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.service.IGroupService; +import com.genersoft.iot.vmp.gb28181.service.IRegionService; import com.genersoft.iot.vmp.gb28181.session.CatalogDataCatch; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; -import com.genersoft.iot.vmp.gb28181.utils.SipUtils; -import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.DocumentException; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; @@ -34,11 +34,10 @@ import java.util.concurrent.atomic.AtomicBoolean; /** * 目录查询的回复 */ +@Slf4j @Component public class CatalogResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(CatalogResponseMessageHandler.class); - private final String cmdType = "Catalog"; @Autowired @@ -47,7 +46,13 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @Autowired - private IVideoManagerStorage storager; + private IDeviceChannelService deviceChannelService; + + @Autowired + private IRegionService regionService; + + @Autowired + private IGroupService groupService; @Autowired private CatalogDataCatch catalogDataCatch; @@ -66,13 +71,14 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp } @Override + @Transactional public void handForDevice(RequestEvent evt, Device device, Element element) { taskQueue.offer(new HandlerCatchData(evt, device, element)); // 回复200 OK try { responseAck((SIPRequest) evt.getRequest(), Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 目录查询回复: {}", e.getMessage()); + log.error("[命令发送失败] 目录查询回复: {}", e.getMessage()); } // 已经开启消息处理则跳过 if (processing.compareAndSet(false, true)) { @@ -85,11 +91,11 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp try { rootElement = getRootElement(take.getEvt(), take.getDevice().getCharset()); } catch (DocumentException e) { - logger.error("[xml解析] 失败: ", e); + log.error("[xml解析] 失败: ", e); continue; } if (rootElement == null) { - logger.warn("[ 收到通道 ] content cannot be null, {}", evt.getRequest()); + log.warn("[ 收到通道 ] content cannot be null, {}", evt.getRequest()); continue; } Element deviceListElement = rootElement.element("DeviceList"); @@ -98,15 +104,16 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp int sumNum = Integer.parseInt(sumNumElement.getText()); if (sumNum == 0) { - logger.info("[收到通道]设备:{}的: 0个", take.getDevice().getDeviceId()); + log.info("[收到通道]设备:{}的: 0个", take.getDevice().getDeviceId()); // 数据已经完整接收 - storager.cleanChannelsForDevice(take.getDevice().getDeviceId()); + deviceChannelService.cleanChannelsForDevice(take.getDevice().getId()); catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null); } else { Iterator deviceListIterator = deviceListElement.elementIterator(); if (deviceListIterator != null) { List channelList = new ArrayList<>(); - List parentChannelIds = new ArrayList<>(); + List regionList = new ArrayList<>(); + List groupList = new ArrayList<>(); // 遍历DeviceList while (deviceListIterator.hasNext()) { Element itemDevice = deviceListIterator.next(); @@ -114,28 +121,41 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp if (channelDeviceElement == null) { continue; } - DeviceChannel channel = XmlUtil.channelContentHandler(itemDevice, device, null); - if (channel == null) { - logger.info("[收到目录订阅]:但是解析失败 {}", new String(evt.getRequest().getRawContent())); + DeviceChannel channel = DeviceChannel.decode(itemDevice); + if (channel.getDeviceId() == null) { + log.info("[收到目录订阅]:但是解析失败 {}", new String(evt.getRequest().getRawContent())); continue; } + channel.setDeviceDbId(device.getId()); if (channel.getParentId() != null && channel.getParentId().equals(sipConfig.getId())) { channel.setParentId(null); } - SipUtils.updateGps(channel, device.getGeoCoordSys()); - channel.setDeviceId(take.getDevice().getDeviceId()); - + // 解析通道类型 + if (channel.getDeviceId().length() <= 8) { + // 行政区划 + Region region = Region.getInstance(channel); + regionList.add(region); + channel.setChannelType(1); + }else if (channel.getDeviceId().length() == 20){ + // 业务分组/虚拟组织 + Group group = Group.getInstance(channel); + if (group != null) { + channel.setChannelType(2); + groupList.add(group); + } + } channelList.add(channel); } int sn = Integer.parseInt(snElement.getText()); - catalogDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, take.getDevice(), channelList); - logger.info("[收到通道]设备: {} -> {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.get(take.getDevice().getDeviceId()) == null ? 0 : catalogDataCatch.get(take.getDevice().getDeviceId()).size(), sumNum); - if (catalogDataCatch.get(take.getDevice().getDeviceId()).size() == sumNum) { + catalogDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, take.getDevice(), + channelList, regionList, groupList); + log.info("[收到通道]设备: {} -> {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.getDeviceChannelList(take.getDevice().getDeviceId()) == null ? 0 : catalogDataCatch.getDeviceChannelList(take.getDevice().getDeviceId()).size(), sumNum); + if (catalogDataCatch.getDeviceChannelList(take.getDevice().getDeviceId()).size() == sumNum) { // 数据已经完整接收, 此时可能存在某个设备离线变上线的情况,但是考虑到性能,此处不做处理, // 目前支持设备通道上线通知时和设备上线时向上级通知 - boolean resetChannelsResult = storager.resetChannels(take.getDevice().getDeviceId(), catalogDataCatch.get(take.getDevice().getDeviceId())); + boolean resetChannelsResult = saveData(device); if (!resetChannelsResult) { - String errorMsg = "接收成功,写入失败,共" + sumNum + "条,已接收" + catalogDataCatch.get(take.getDevice().getDeviceId()).size() + "条"; + String errorMsg = "接收成功,写入失败,共" + sumNum + "条,已接收" + catalogDataCatch.getDeviceChannelList(take.getDevice().getDeviceId()).size() + "条"; catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), errorMsg); } else { catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null); @@ -145,8 +165,8 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp } } catch (Exception e) { - logger.warn("[收到通道] 发现未处理的异常, \r\n{}", evt.getRequest()); - logger.error("[收到通道] 异常内容: ", e); + log.warn("[收到通道] 发现未处理的异常, \r\n{}", evt.getRequest()); + log.error("[收到通道] 异常内容: ", e); } } processing.set(false); @@ -155,13 +175,34 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp } + @Transactional + public boolean saveData(Device device) { + + boolean result = true; + List deviceChannelList = catalogDataCatch.getDeviceChannelList(device.getDeviceId()); + if (deviceChannelList != null && !deviceChannelList.isEmpty()) { + result &= deviceChannelService.resetChannels(device.getId(), deviceChannelList); + } + + List regionList = catalogDataCatch.getRegionList(device.getDeviceId()); + if ( regionList!= null && !regionList.isEmpty()) { + result &= regionService.batchAdd(regionList); + } + + List groupList = catalogDataCatch.getGroupList(device.getDeviceId()); + if (groupList != null && !groupList.isEmpty()) { + result &= groupService.batchAdd(groupList); + } + return result; + } + @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element rootElement) { } public SyncStatus getChannelSyncProgress(String deviceId) { - if (catalogDataCatch.get(deviceId) == null) { + if (catalogDataCatch.getDeviceChannelList(deviceId) == null) { return null; } else { return catalogDataCatch.getSyncStatus(deviceId); @@ -169,7 +210,7 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp } public boolean isSyncRunning(String deviceId) { - if (catalogDataCatch.get(deviceId) == null) { + if (catalogDataCatch.getDeviceChannelList(deviceId) == null) { return false; } else { return catalogDataCatch.isSyncRunning(deviceId); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java index 47ae1a9c1..ed2c8b0ab 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java @@ -2,8 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.respon import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; @@ -11,9 +10,8 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessag import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -26,18 +24,15 @@ import java.text.ParseException; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; +@Slf4j @Component public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(ConfigDownloadResponseMessageHandler.class); private final String cmdType = "ConfigDownload"; @Autowired private ResponseMessageHandler responseMessageHandler; - @Autowired - private EventPublisher publisher; - @Autowired private DeferredResultHolder deferredResultHolder; @@ -55,13 +50,13 @@ public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorPar // 回复200 OK responseAck((SIPRequest) evt.getRequest(), Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 设备配置查询: {}", e.getMessage()); + log.error("[命令发送失败] 设备配置查询: {}", e.getMessage()); } // 此处是对本平台发出DeviceControl指令的应答 JSONObject json = new JSONObject(); XmlUtil.node2Json(element, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); + if (log.isDebugEnabled()) { + log.debug(json.toJSONString()); } RequestMessage msg = new RequestMessage(); msg.setKey(key); @@ -72,7 +67,7 @@ public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorPar } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element element) { // 不会收到上级平台的心跳信息 } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceConfigResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceConfigResponseMessageHandler.java index b18a87b29..453d8066f 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceConfigResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceConfigResponseMessageHandler.java @@ -2,16 +2,15 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.respon import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -20,10 +19,10 @@ import javax.sip.RequestEvent; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; +@Slf4j @Component public class DeviceConfigResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(DeviceConfigResponseMessageHandler.class); private final String cmdType = "DeviceConfig"; @Autowired @@ -42,8 +41,8 @@ public class DeviceConfigResponseMessageHandler extends SIPRequestProcessorParen JSONObject json = new JSONObject(); XmlUtil.node2Json(element, json); String channelId = getText(element, "DeviceID"); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); + if (log.isDebugEnabled()) { + log.debug(json.toJSONString()); } String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + device.getDeviceId() + channelId; RequestMessage msg = new RequestMessage(); @@ -53,6 +52,6 @@ public class DeviceConfigResponseMessageHandler extends SIPRequestProcessorParen } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element rootElement) { } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java index af08e13ba..ef3531a85 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java @@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.respon import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; @@ -10,9 +10,8 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessag import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -21,15 +20,14 @@ import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; import javax.sip.SipException; import javax.sip.message.Response; - import java.text.ParseException; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; +@Slf4j @Component public class DeviceControlResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(DeviceControlResponseMessageHandler.class); private final String cmdType = "DeviceControl"; @Autowired @@ -49,13 +47,13 @@ public class DeviceControlResponseMessageHandler extends SIPRequestProcessorPare try { responseAck((SIPRequest) evt.getRequest(), Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 设备控制: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 设备控制: {}", e.getMessage()); } JSONObject json = new JSONObject(); String channelId = getText(element, "DeviceID"); XmlUtil.node2Json(element, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); + if (log.isDebugEnabled()) { + log.debug(json.toJSONString()); } RequestMessage msg = new RequestMessage(); String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + device.getDeviceId() + channelId; @@ -66,6 +64,6 @@ public class DeviceControlResponseMessageHandler extends SIPRequestProcessorPare } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element rootElement) { } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java index 849be81f0..9db37b41f 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java @@ -1,18 +1,17 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; -import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.DocumentException; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -29,10 +28,10 @@ import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; /** * @author lin */ +@Slf4j @Component public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(DeviceInfoResponseMessageHandler.class); private final String cmdType = "DeviceInfo"; @Autowired @@ -52,10 +51,10 @@ public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent @Override public void handForDevice(RequestEvent evt, Device device, Element rootElement) { - logger.debug("接收到DeviceInfo应答消息"); + log.debug("接收到DeviceInfo应答消息"); // 检查设备是否存在, 不存在则不回复 if (device == null || !device.isOnLine()) { - logger.warn("[接收到DeviceInfo应答消息,但是设备已经离线]:" + (device != null ? device.getDeviceId():"" )); + log.warn("[接收到DeviceInfo应答消息,但是设备已经离线]:" + (device != null ? device.getDeviceId():"" )); return; } SIPRequest request = (SIPRequest) evt.getRequest(); @@ -63,11 +62,11 @@ public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent rootElement = getRootElement(evt, device.getCharset()); if (rootElement == null) { - logger.warn("[ 接收到DeviceInfo应答消息 ] content cannot be null, {}", evt.getRequest()); + log.warn("[ 接收到DeviceInfo应答消息 ] content cannot be null, {}", evt.getRequest()); try { responseAck((SIPRequest) evt.getRequest(), Response.BAD_REQUEST); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] DeviceInfo应答消息 BAD_REQUEST: {}", e.getMessage()); + log.error("[命令发送失败] DeviceInfo应答消息 BAD_REQUEST: {}", e.getMessage()); } return; } @@ -95,13 +94,13 @@ public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent // 回复200 OK responseAck(request, Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] DeviceInfo应答消息 200: {}", e.getMessage()); + log.error("[命令发送失败] DeviceInfo应答消息 200: {}", e.getMessage()); } } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element rootElement) { } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java index da4743f68..c6e366445 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java @@ -2,19 +2,18 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.respon import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; -import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -25,10 +24,10 @@ import javax.sip.SipException; import javax.sip.message.Response; import java.text.ParseException; +@Slf4j @Component public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(DeviceStatusResponseMessageHandler.class); private final String cmdType = "DeviceStatus"; @Autowired @@ -50,7 +49,7 @@ public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParen @Override public void handForDevice(RequestEvent evt, Device device, Element element) { - logger.info("接收到DeviceStatus应答消息"); + log.info("接收到DeviceStatus应答消息"); // 检查设备是否存在, 不存在则不回复 if (device == null) { return; @@ -59,15 +58,15 @@ public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParen try { responseAck((SIPRequest) evt.getRequest(), Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 设备状态应答回复200OK: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 设备状态应答回复200OK: {}", e.getMessage()); } Element deviceIdElement = element.element("DeviceID"); Element onlineElement = element.element("Online"); String channelId = deviceIdElement.getText(); JSONObject json = new JSONObject(); XmlUtil.node2Json(element, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); + if (log.isDebugEnabled()) { + log.debug(json.toJSONString()); } String text = onlineElement.getText(); if ("ONLINE".equalsIgnoreCase(text.trim())) { @@ -82,7 +81,7 @@ public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParen } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element rootElement) { } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java index 5c3d6d66c..15461ec25 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java @@ -1,10 +1,9 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; -import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; @@ -12,15 +11,12 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessag import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; -import com.genersoft.iot.vmp.service.IDeviceChannelService; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import com.genersoft.iot.vmp.utils.DateUtil; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.DocumentException; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -38,24 +34,15 @@ import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; * 移动设备位置数据查询回复 * @author lin */ +@Slf4j @Component public class MobilePositionResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(MobilePositionResponseMessageHandler.class); private final String cmdType = "MobilePosition"; @Autowired private ResponseMessageHandler responseMessageHandler; - @Autowired - private UserSetting userSetting; - - @Autowired - private IVideoManagerStorage storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - @Autowired private IDeviceChannelService deviceChannelService; @@ -74,84 +61,80 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar try { rootElement = getRootElement(evt, device.getCharset()); if (rootElement == null) { - logger.warn("[ 移动设备位置数据查询回复 ] content cannot be null, {}", evt.getRequest()); + log.warn("[ 移动设备位置数据查询回复 ] content cannot be null, {}", evt.getRequest()); try { responseAck(request, Response.BAD_REQUEST); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 移动设备位置数据查询 BAD_REQUEST: {}", e.getMessage()); + log.error("[命令发送失败] 移动设备位置数据查询 BAD_REQUEST: {}", e.getMessage()); } return; } - MobilePosition mobilePosition = new MobilePosition(); - mobilePosition.setCreateTime(DateUtil.getNow()); - if (!ObjectUtils.isEmpty(device.getName())) { - mobilePosition.setDeviceName(device.getName()); - } - mobilePosition.setDeviceId(device.getDeviceId()); - mobilePosition.setChannelId(getText(rootElement, "DeviceID")); - //兼容ISO 8601格式时间 - String time = getText(rootElement, "Time"); - if (ObjectUtils.isEmpty(time)){ - mobilePosition.setTime(DateUtil.getNow()); + String channelId = getText(rootElement, "DeviceID"); + DeviceChannel deviceChannel = deviceChannelService.getOne(device.getDeviceId(), channelId); + if (deviceChannel == null) { + log.warn("[解析报警消息] 未找到通道:{}/{}", device.getDeviceId(), channelId); }else { - mobilePosition.setTime(SipUtils.parseTime(time)); + MobilePosition mobilePosition = new MobilePosition(); + mobilePosition.setCreateTime(DateUtil.getNow()); + if (!ObjectUtils.isEmpty(device.getName())) { + mobilePosition.setDeviceName(device.getName()); + } + mobilePosition.setDeviceId(device.getDeviceId()); + mobilePosition.setChannelId(deviceChannel.getId()); + //兼容ISO 8601格式时间 + String time = getText(rootElement, "Time"); + if (ObjectUtils.isEmpty(time)){ + mobilePosition.setTime(DateUtil.getNow()); + }else { + mobilePosition.setTime(SipUtils.parseTime(time)); + } + mobilePosition.setLongitude(Double.parseDouble(getText(rootElement, "Longitude"))); + mobilePosition.setLatitude(Double.parseDouble(getText(rootElement, "Latitude"))); + if (NumericUtil.isDouble(getText(rootElement, "Speed"))) { + mobilePosition.setSpeed(Double.parseDouble(getText(rootElement, "Speed"))); + } else { + mobilePosition.setSpeed(0.0); + } + if (NumericUtil.isDouble(getText(rootElement, "Direction"))) { + mobilePosition.setDirection(Double.parseDouble(getText(rootElement, "Direction"))); + } else { + mobilePosition.setDirection(0.0); + } + if (NumericUtil.isDouble(getText(rootElement, "Altitude"))) { + mobilePosition.setAltitude(Double.parseDouble(getText(rootElement, "Altitude"))); + } else { + mobilePosition.setAltitude(0.0); + } + mobilePosition.setReportSource("Mobile Position"); + + // 更新device channel 的经纬度 + deviceChannel.setLongitude(mobilePosition.getLongitude()); + deviceChannel.setLatitude(mobilePosition.getLatitude()); + deviceChannel.setGpsTime(mobilePosition.getTime()); + + deviceChannelService.updateChannelGPS(device, deviceChannel, mobilePosition); + + String key = DeferredResultHolder.CALLBACK_CMD_MOBILE_POSITION + device.getDeviceId(); + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(mobilePosition); + resultHolder.invokeAllResult(msg); } - mobilePosition.setLongitude(Double.parseDouble(getText(rootElement, "Longitude"))); - mobilePosition.setLatitude(Double.parseDouble(getText(rootElement, "Latitude"))); - if (NumericUtil.isDouble(getText(rootElement, "Speed"))) { - mobilePosition.setSpeed(Double.parseDouble(getText(rootElement, "Speed"))); - } else { - mobilePosition.setSpeed(0.0); - } - if (NumericUtil.isDouble(getText(rootElement, "Direction"))) { - mobilePosition.setDirection(Double.parseDouble(getText(rootElement, "Direction"))); - } else { - mobilePosition.setDirection(0.0); - } - if (NumericUtil.isDouble(getText(rootElement, "Altitude"))) { - mobilePosition.setAltitude(Double.parseDouble(getText(rootElement, "Altitude"))); - } else { - mobilePosition.setAltitude(0.0); - } - mobilePosition.setReportSource("Mobile Position"); - - // 更新device channel 的经纬度 - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setDeviceId(device.getDeviceId()); - deviceChannel.setChannelId(mobilePosition.getChannelId()); - deviceChannel.setLongitude(mobilePosition.getLongitude()); - deviceChannel.setLatitude(mobilePosition.getLatitude()); - deviceChannel.setGpsTime(mobilePosition.getTime()); - - deviceChannel = deviceChannelService.updateGps(deviceChannel, device); - - mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); - mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); - mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); - mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); - - deviceChannelService.updateChannelGPS(device, deviceChannel, mobilePosition); - - String key = DeferredResultHolder.CALLBACK_CMD_MOBILE_POSITION + device.getDeviceId(); - RequestMessage msg = new RequestMessage(); - msg.setKey(key); - msg.setData(mobilePosition); - resultHolder.invokeAllResult(msg); //回复 200 OK try { responseAck(request, Response.OK); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 移动设备位置数据查询 200: {}", e.getMessage()); + log.error("[命令发送失败] 移动设备位置数据查询 200: {}", e.getMessage()); } } catch (DocumentException e) { - logger.error("未处理的异常 ", e); + log.error("未处理的异常 ", e); } } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element element) { } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/PresetQueryResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/PresetQueryResponseMessageHandler.java index 862a8eea2..119b0c7f0 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/PresetQueryResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/PresetQueryResponseMessageHandler.java @@ -1,17 +1,17 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.bean.PresetQuerySipReq; -import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.DocumentException; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -30,10 +30,10 @@ import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; /** * 设备预置位查询应答 */ +@Slf4j @Component public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(PresetQueryResponseMessageHandler.class); private final String cmdType = "PresetQuery"; @Autowired @@ -57,11 +57,11 @@ public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent Element rootElement = getRootElement(evt, device.getCharset()); if (rootElement == null) { - logger.warn("[ 设备预置位查询应答 ] content cannot be null, {}", evt.getRequest()); + log.warn("[ 设备预置位查询应答 ] content cannot be null, {}", evt.getRequest()); try { responseAck(request, Response.BAD_REQUEST); } catch (InvalidArgumentException | ParseException | SipException e) { - logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage()); + log.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage()); } return; } @@ -74,7 +74,7 @@ public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent try { responseAck(request, Response.BAD_REQUEST, "xml error"); } catch (InvalidArgumentException | ParseException | SipException e) { - logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage()); + log.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage()); } return; } @@ -105,15 +105,15 @@ public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent try { responseAck(request, Response.OK); } catch (InvalidArgumentException | ParseException | SipException e) { - logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage()); + log.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage()); } } catch (DocumentException e) { - logger.error("[解析xml]失败: ", e); + log.error("[解析xml]失败: ", e); } } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element rootElement) { } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java index fc0fda8d6..655c05511 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java @@ -1,7 +1,10 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; +import com.genersoft.iot.vmp.gb28181.bean.RecordItem; import com.genersoft.iot.vmp.gb28181.event.EventPublisher; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; @@ -11,9 +14,8 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.respons import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.UJson; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -28,7 +30,6 @@ import javax.sip.SipException; import javax.sip.message.Response; import java.text.ParseException; import java.util.*; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -37,14 +38,12 @@ import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; /** * @author lin */ +@Slf4j @Component public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private final Logger logger = LoggerFactory.getLogger(RecordInfoResponseMessageHandler.class); private final String cmdType = "RecordInfo"; - private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); - @Autowired private ResponseMessageHandler responseMessageHandler; @@ -74,7 +73,7 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent // 回复200 OK responseAck((SIPRequest) evt.getRequest(), Response.OK); }catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 国标录像: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 国标录像: {}", e.getMessage()); } taskExecutor.execute(()->{ try { @@ -93,7 +92,7 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent recordInfo.setSumNum(sumNum); Element recordListElement = rootElement.element("RecordList"); if (recordListElement == null || sumNum == 0) { - logger.info("无录像数据"); + log.info("无录像数据"); recordInfo.setCount(sumNum); eventPublisher.recordEndEventPush(recordInfo); releaseRequest(device.getDeviceId(), sn,recordInfo); @@ -106,7 +105,7 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent Element itemRecord = recordListIterator.next(); Element recordElement = itemRecord.element("DeviceID"); if (recordElement == null) { - logger.info("记录为空,下一个..."); + log.info("记录为空,下一个..."); continue; } RecordItem record = new RecordItem(); @@ -154,14 +153,14 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent } } } catch (Exception e) { - logger.error("[国标录像] 发现未处理的异常, \r\n{}", evt.getRequest()); - logger.error("[国标录像] 异常内容: ", e); + log.error("[国标录像] 发现未处理的异常, \r\n{}", evt.getRequest()); + log.error("[国标录像] 异常内容: ", e); } }); } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element element) { } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/ISIPResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/ISIPResponseProcessor.java index 50fb20211..ffa93f218 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/ISIPResponseProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/ISIPResponseProcessor.java @@ -1,5 +1,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.response; +import org.springframework.scheduling.annotation.Async; + import javax.sip.ResponseEvent; /** @@ -9,6 +11,7 @@ import javax.sip.ResponseEvent; */ public interface ISIPResponseProcessor { + void process(ResponseEvent evt); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/ByeResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/ByeResponseProcessor.java index ff63fad04..17c928df7 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/ByeResponseProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/ByeResponseProcessor.java @@ -1,7 +1,5 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl; -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.SipLayer; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract; import org.springframework.beans.factory.annotation.Autowired; @@ -19,13 +17,6 @@ public class ByeResponseProcessor extends SIPResponseProcessorAbstract { private final String method = "BYE"; - @Autowired - private SipLayer sipLayer; - - @Autowired - private SipConfig config; - - @Autowired private SIPProcessorObserver sipProcessorObserver; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/CancelResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/CancelResponseProcessor.java index 775beeb6d..a5f80d875 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/CancelResponseProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/CancelResponseProcessor.java @@ -1,7 +1,5 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl; -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.SipLayer; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract; import org.springframework.beans.factory.annotation.Autowired; @@ -19,12 +17,6 @@ public class CancelResponseProcessor extends SIPResponseProcessorAbstract { private final String method = "CANCEL"; - @Autowired - private SipLayer sipLayer; - - @Autowired - private SipConfig config; - @Autowired private SIPProcessorObserver sipProcessorObserver; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java index d29affcdc..13f97a4ee 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java @@ -1,6 +1,5 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl; -import com.genersoft.iot.vmp.gb28181.SipLayer; import com.genersoft.iot.vmp.gb28181.bean.Gb28181Sdp; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; @@ -9,8 +8,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcesso import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import gov.nist.javax.sip.ResponseEventExt; import gov.nist.javax.sip.message.SIPResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -19,9 +17,6 @@ import javax.sdp.SessionDescription; import javax.sip.InvalidArgumentException; import javax.sip.ResponseEvent; import javax.sip.SipException; -import javax.sip.InvalidArgumentException; -import javax.sip.ResponseEvent; -import javax.sip.SipException; import javax.sip.SipFactory; import javax.sip.address.SipURI; import javax.sip.message.Request; @@ -34,26 +29,21 @@ import java.text.ParseException; * @author: panlinlin * @date: 2021年11月5日 16:40 */ +@Slf4j @Component public class InviteResponseProcessor extends SIPResponseProcessorAbstract { - private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class); private final String method = "INVITE"; @Autowired private SIPProcessorObserver sipProcessorObserver; - - @Autowired - private SipLayer sipLayer; - @Autowired private SIPSender sipSender; @Autowired private SIPRequestHeaderProvider headerProvider; - @Override public void afterPropertiesSet() throws Exception { // 添加消息处理的订阅 @@ -70,7 +60,7 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract { */ @Override public void process(ResponseEvent evt ){ - logger.debug("接收到消息:" + evt.getResponse()); + log.debug("接收到消息:" + evt.getResponse()); try { SIPResponse response = (SIPResponse)evt.getResponse(); int statusCode = response.getStatusCode(); @@ -88,11 +78,11 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract { SipURI requestUri = SipFactory.getInstance().createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort()); Request reqAck = headerProvider.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response); - logger.info("[回复ack] {}-> {}:{} ", sdp.getOrigin().getUsername(), event.getRemoteIpAddress(), event.getRemotePort()); + log.info("[回复ack] {}-> {}:{} ", sdp.getOrigin().getUsername(), event.getRemoteIpAddress(), event.getRemotePort()); sipSender.transmitRequest( response.getLocalAddress().getHostAddress(), reqAck); } } catch (InvalidArgumentException | ParseException | SipException | SdpParseException e) { - logger.info("[点播回复ACK],异常:", e ); + log.info("[点播回复ACK],异常:", e ); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java index edf2c6807..3564e86c5 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java @@ -1,19 +1,16 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.bean.PlatformCatch; import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; -import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract; -import com.genersoft.iot.vmp.service.IPlatformService; +import com.genersoft.iot.vmp.gb28181.service.IPlatformService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo; import gov.nist.javax.sip.message.SIPResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -29,27 +26,21 @@ import java.text.ParseException; * @author: swwheihei * @date: 2020年5月3日 下午5:32:23 */ +@Slf4j @Component public class RegisterResponseProcessor extends SIPResponseProcessorAbstract { - private final Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class); private final String method = "REGISTER"; @Autowired private ISIPCommanderForPlatform sipCommanderForPlatform; - @Autowired - private IVideoManagerStorage storager; - @Autowired private IRedisCatchStorage redisCatchStorage; @Autowired private SIPProcessorObserver sipProcessorObserver; - @Autowired - private SubscribeHolder subscribeHolder; - @Autowired private IPlatformService platformService; @@ -70,21 +61,21 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract { String callId = response.getCallIdHeader().getCallId(); PlatformRegisterInfo platformRegisterInfo = redisCatchStorage.queryPlatformRegisterInfo(callId); if (platformRegisterInfo == null) { - logger.info(String.format("[国标级联]未找到callId: %s 的注册/注销平台id", callId )); + log.info(String.format("[国标级联]未找到callId: %s 的注册/注销平台id", callId )); return; } - ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(platformRegisterInfo.getPlatformId()); + PlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(platformRegisterInfo.getPlatformId()); if (parentPlatformCatch == null) { - logger.warn(String.format("[国标级联]收到注册/注销%S请求,平台:%s,但是平台缓存信息未查询到!!!", response.getStatusCode(),platformRegisterInfo.getPlatformId())); + log.warn(String.format("[国标级联]收到注册/注销%S请求,平台:%s,但是平台缓存信息未查询到!!!", response.getStatusCode(),platformRegisterInfo.getPlatformId())); return; } String action = platformRegisterInfo.isRegister() ? "注册" : "注销"; - logger.info(String.format("[国标级联]%s %S响应,%s ", action, response.getStatusCode(), platformRegisterInfo.getPlatformId() )); - ParentPlatform parentPlatform = parentPlatformCatch.getParentPlatform(); + log.info(String.format("[国标级联]%s %S响应,%s ", action, response.getStatusCode(), platformRegisterInfo.getPlatformId() )); + Platform parentPlatform = parentPlatformCatch.getPlatform(); if (parentPlatform == null) { - logger.warn(String.format("[国标级联]收到 %s %s的%S请求, 但是平台信息未查询到!!!", platformRegisterInfo.getPlatformId(), action, response.getStatusCode())); + log.warn(String.format("[国标级联]收到 %s %s的%S请求, 但是平台信息未查询到!!!", platformRegisterInfo.getPlatformId(), action, response.getStatusCode())); return; } @@ -94,7 +85,7 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract { try { sipCommanderForPlatform.register(parentPlatform, sipTransactionInfo, www, null, null, platformRegisterInfo.isRegister()); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 再次注册: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 再次注册: {}", e.getMessage()); } }else if (response.getStatusCode() == Response.OK){ diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/timeout/impl/TimeoutProcessorImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/timeout/impl/TimeoutProcessorImpl.java index 531505d28..c36a2e54b 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/timeout/impl/TimeoutProcessorImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/timeout/impl/TimeoutProcessorImpl.java @@ -1,11 +1,10 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.timeout.impl; -import com.genersoft.iot.vmp.conf.SystemInfoTimerTask; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.gb28181.event.sip.SipEvent; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.event.timeout.ITimeoutProcessor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -13,11 +12,10 @@ import org.springframework.stereotype.Component; import javax.sip.TimeoutEvent; import javax.sip.header.CallIdHeader; +@Slf4j @Component public class TimeoutProcessorImpl implements InitializingBean, ITimeoutProcessor { - private Logger logger = LoggerFactory.getLogger(TimeoutProcessorImpl.class); - @Autowired private SIPProcessorObserver processorObserver; @@ -35,13 +33,14 @@ public class TimeoutProcessorImpl implements InitializingBean, ITimeoutProcessor // TODO Auto-generated method stub CallIdHeader callIdHeader = event.getClientTransaction().getDialog().getCallId(); String callId = callIdHeader.getCallId(); - SipSubscribe.Event errorSubscribe = sipSubscribe.getErrorSubscribe(callId); - SipSubscribe.EventResult timeoutEventEventResult = new SipSubscribe.EventResult<>(event); - errorSubscribe.response(timeoutEventEventResult); - sipSubscribe.removeErrorSubscribe(callId); - sipSubscribe.removeOkSubscribe(callId); + SipEvent sipEvent = sipSubscribe.getSubscribe(callId); + if (sipEvent != null && sipEvent.getErrorEvent() != null) { + SipSubscribe.EventResult timeoutEventEventResult = new SipSubscribe.EventResult<>(event); + sipEvent.getErrorEvent().response(timeoutEventEventResult); + sipSubscribe.removeSubscribe(callId); + } } catch (Exception e) { - logger.error("[超时事件失败]: {}", e.getMessage()); + log.error("[超时事件失败]: {}", e.getMessage()); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/MessageElementForCatalog.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/MessageElementForCatalog.java new file mode 100644 index 000000000..7abd7b338 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/MessageElementForCatalog.java @@ -0,0 +1,17 @@ +package com.genersoft.iot.vmp.gb28181.utils; + +import java.lang.annotation.*; + +/** + * @author gaofuwang + * @version 1.0 + * @date 2022/6/28 14:58 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface MessageElementForCatalog { + String[] value(); + + String subVal() default ""; +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/NumericUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/NumericUtil.java index d990db93e..f2b84b2c5 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/NumericUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/NumericUtil.java @@ -1,17 +1,11 @@ package com.genersoft.iot.vmp.gb28181.utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * 数值格式判断和处理 * @author lawrencehj * @date 2021年1月27日 */ public class NumericUtil { - - private static Logger logger = LoggerFactory.getLogger(NumericUtil.class); - /** * 判断是否Double格式 * @param str diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java index c64605883..0bce5f872 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java @@ -1,6 +1,5 @@ package com.genersoft.iot.vmp.gb28181.utils; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.Gb28181Sdp; import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; import com.genersoft.iot.vmp.utils.DateUtil; @@ -9,9 +8,8 @@ import gov.nist.javax.sip.address.AddressImpl; import gov.nist.javax.sip.address.SipUri; import gov.nist.javax.sip.header.Subject; import gov.nist.javax.sip.message.SIPRequest; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.util.ObjectUtils; import javax.sdp.SdpFactory; @@ -20,7 +18,7 @@ import javax.sdp.SessionDescription; import javax.sip.PeerUnavailableException; import javax.sip.SipFactory; import javax.sip.header.FromHeader; -import javax.sip.header.Header; +import javax.sip.header.SubjectHeader; import javax.sip.header.UserAgentHeader; import javax.sip.message.Request; import java.text.ParseException; @@ -36,10 +34,9 @@ import java.util.UUID; * @description JAIN SIP的工具类 * @createTime 2021年09月27日 15:12:00 */ +@Slf4j public class SipUtils { - private final static Logger logger = LoggerFactory.getLogger(SipUtils.class); - public static String getUserIdFromFromHeader(Request request) { FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME); return getUserIdFromFromHeader(fromHeader); @@ -47,13 +44,22 @@ public class SipUtils { /** * 从subject读取channelId * */ - public static String getChannelIdFromRequest(Request request) { - Header subject = request.getHeader("subject"); + public static String[] getChannelIdFromRequest(Request request) { + SubjectHeader subject = (Subject)request.getHeader("subject"); if (subject == null) { // 如果缺失subject return null; } - return ((Subject) subject).getSubject().split(":")[0]; + String[] result = new String[2]; + String subjectStr = subject.getSubject(); + if (subjectStr.indexOf(",") > 0) { + String[] subjectSplit = subjectStr.split(","); + result[0] = subjectSplit[0].split(":")[0]; + result[1] = subjectSplit[1].split(":")[0]; + }else { + result[0] = subjectStr.split(":")[0]; + } + return result; } public static String getUserIdFromFromHeader(FromHeader fromHeader) { @@ -189,39 +195,6 @@ public class SipUtils { return new RemoteAddressInfo(remoteAddress, remotePort); } - public static DeviceChannel updateGps(DeviceChannel deviceChannel, String geoCoordSys) { - if (deviceChannel.getLongitude()*deviceChannel.getLatitude() > 0) { - - if (geoCoordSys == null) { - geoCoordSys = "WGS84"; - } - if ("WGS84".equals(geoCoordSys)) { - deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); - deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); - Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude()); - deviceChannel.setLongitudeGcj02(position[0]); - deviceChannel.setLatitudeGcj02(position[1]); - }else if ("GCJ02".equals(geoCoordSys)) { - deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); - deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); - Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude()); - deviceChannel.setLongitudeWgs84(position[0]); - deviceChannel.setLatitudeWgs84(position[1]); - }else { - deviceChannel.setLongitudeGcj02(0.00); - deviceChannel.setLatitudeGcj02(0.00); - deviceChannel.setLongitudeWgs84(0.00); - deviceChannel.setLatitudeWgs84(0.00); - } - }else { - deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); - deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); - deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); - deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); - } - return deviceChannel; - } - public static Gb28181Sdp parseSDP(String sdpStr) throws SdpParseException { // jainSip不支持y= f=字段, 移除以解析。 @@ -277,7 +250,7 @@ public class SipUtils { try { localDateTime = LocalDateTime.parse(timeStr, DateUtil.formatterISO8601); }catch (DateTimeParseException e2) { - logger.error("[格式化时间] 无法格式化时间: {}", timeStr); + log.error("[格式化时间] 无法格式化时间: {}", timeStr); return null; } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java index 6596f53a2..98c8dadb3 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java @@ -2,21 +2,13 @@ package com.genersoft.iot.vmp.gb28181.utils; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; -import com.genersoft.iot.vmp.common.CivilCodePo; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; -import com.genersoft.iot.vmp.utils.CivilCodeUtil; -import com.genersoft.iot.vmp.utils.DateUtil; -import org.apache.commons.lang3.StringUtils; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.math.NumberUtils; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; @@ -32,21 +24,12 @@ import java.util.*; /** * 基于dom4j的工具包 - * - * */ +@Slf4j public class XmlUtil { - /** - * 日志服务 - */ - private static Logger logger = LoggerFactory.getLogger(XmlUtil.class); /** * 解析XML为Document对象 - * - * @param xml 被解析的XMl - * - * @return Document */ public static Element parseXml(String xml) { Document document = null; @@ -56,7 +39,7 @@ public class XmlUtil { try { document = saxReader.read(sr); } catch (DocumentException e) { - logger.error("解析失败", e); + log.error("解析失败", e); } return null == document ? null : document.getRootElement(); } @@ -240,387 +223,387 @@ public class XmlUtil { CivilCode, BusinessGroup,VirtualOrganization,Other } - public static DeviceChannel channelContentHandler(Element itemDevice, Device device, String event){ - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setDeviceId(device.getDeviceId()); - Element channdelIdElement = itemDevice.element("DeviceID"); - if (channdelIdElement == null) { - logger.warn("解析Catalog消息时发现缺少 DeviceID"); - return null; - } - String channelId = channdelIdElement.getTextTrim(); - if (ObjectUtils.isEmpty(channelId)) { - logger.warn("解析Catalog消息时发现缺少 DeviceID"); - return null; - } - deviceChannel.setChannelId(channelId); - if (event != null && !event.equals(CatalogEvent.ADD) && !event.equals(CatalogEvent.UPDATE)) { - // 除了ADD和update情况下需要识别全部内容, - return deviceChannel; - } - Element nameElement = itemDevice.element("Name"); - // 当通道名称为空时,设置通道名称为通道编码,避免级联时因通道名称为空导致上级接收通道失败 - if (nameElement != null && StringUtils.isNotBlank(nameElement.getText())) { - deviceChannel.setName(nameElement.getText()); - } else { - deviceChannel.setName(channelId); - } - if(channelId.length() <= 8) { - deviceChannel.setHasAudio(false); - CivilCodePo parentCode = CivilCodeUtil.INSTANCE.getParentCode(channelId); - if (parentCode != null) { - deviceChannel.setParentId(parentCode.getCode()); - deviceChannel.setCivilCode(parentCode.getCode()); - }else { - logger.warn("[xml解析] 无法确定行政区划{}的上级行政区划", channelId); - } - deviceChannel.setStatus(true); - return deviceChannel; - }else { - if(channelId.length() != 20) { - logger.warn("[xml解析] 失败,编号不符合国标28181定义: {}", channelId); - return null; - } - - int code = Integer.parseInt(channelId.substring(10, 13)); - if (code == 136 || code == 137 || code == 138) { - deviceChannel.setHasAudio(true); - }else { - deviceChannel.setHasAudio(false); - } - // 设备厂商 - String manufacturer = getText(itemDevice, "Manufacturer"); - // 设备型号 - String model = getText(itemDevice, "Model"); - // 设备归属 - String owner = getText(itemDevice, "Owner"); - // 行政区域 - String civilCode = getText(itemDevice, "CivilCode"); - // 虚拟组织所属的业务分组ID,业务分组根据特定的业务需求制定,一个业务分组包含一组特定的虚拟组织 - String businessGroupID = getText(itemDevice, "BusinessGroupID"); - // 父设备/区域/系统ID - String parentID = getText(itemDevice, "ParentID"); - if (parentID != null && parentID.equalsIgnoreCase("null")) { - parentID = null; - } - // 注册方式(必选)缺省为1;1:符合IETFRFC3261标准的认证注册模式;2:基于口令的双向认证注册模式;3:基于数字证书的双向认证注册模式 - String registerWay = getText(itemDevice, "RegisterWay"); - // 保密属性(必选)缺省为0;0:不涉密,1:涉密 - String secrecy = getText(itemDevice, "Secrecy"); - // 安装地址 - String address = getText(itemDevice, "Address"); - - switch (code){ - case 200: - // 系统目录 - if (!ObjectUtils.isEmpty(manufacturer)) { - deviceChannel.setManufacture(manufacturer); - } - if (!ObjectUtils.isEmpty(model)) { - deviceChannel.setModel(model); - } - if (!ObjectUtils.isEmpty(owner)) { - deviceChannel.setOwner(owner); - } - if (!ObjectUtils.isEmpty(civilCode)) { - deviceChannel.setCivilCode(civilCode); - deviceChannel.setParentId(civilCode); - }else { - if (!ObjectUtils.isEmpty(parentID)) { - deviceChannel.setParentId(parentID); - } - } - if (!ObjectUtils.isEmpty(address)) { - deviceChannel.setAddress(address); - } - deviceChannel.setStatus(true); - if (!ObjectUtils.isEmpty(registerWay)) { - try { - deviceChannel.setRegisterWay(Integer.parseInt(registerWay)); - }catch (NumberFormatException exception) { - logger.warn("[xml解析] 从通道数据获取registerWay失败: {}", registerWay); - } - } - if (!ObjectUtils.isEmpty(secrecy)) { - deviceChannel.setSecrecy(secrecy); - } - return deviceChannel; - case 215: - // 业务分组 - deviceChannel.setStatus(true); - if (!ObjectUtils.isEmpty(parentID)) { - if (!parentID.trim().equalsIgnoreCase(device.getDeviceId())) { - deviceChannel.setParentId(parentID); - } - }else { - logger.warn("[xml解析] 业务分组数据中缺少关键信息->ParentId"); - if (!ObjectUtils.isEmpty(civilCode)) { - deviceChannel.setCivilCode(civilCode); - } - } - break; - case 216: - // 虚拟组织 - deviceChannel.setStatus(true); - if (!ObjectUtils.isEmpty(businessGroupID)) { - deviceChannel.setBusinessGroupId(businessGroupID); - } - - if (!ObjectUtils.isEmpty(parentID)) { - if (parentID.contains("/")) { - String[] parentIdArray = parentID.split("/"); - parentID = parentIdArray[parentIdArray.length - 1]; - } - deviceChannel.setParentId(parentID); - }else { - if (!ObjectUtils.isEmpty(businessGroupID)) { - deviceChannel.setParentId(businessGroupID); - } - } - break; - default: - // 设备目录 - if (!ObjectUtils.isEmpty(manufacturer)) { - deviceChannel.setManufacture(manufacturer); - } - if (!ObjectUtils.isEmpty(model)) { - deviceChannel.setModel(model); - } - if (!ObjectUtils.isEmpty(owner)) { - deviceChannel.setOwner(owner); - } - if (!ObjectUtils.isEmpty(civilCode) - && civilCode.length() <= 8 - && NumberUtils.isParsable(civilCode) - && civilCode.length()%2 == 0 - ) { - deviceChannel.setCivilCode(civilCode); - } - if (!ObjectUtils.isEmpty(businessGroupID)) { - deviceChannel.setBusinessGroupId(businessGroupID); - } - - // 警区 - String block = getText(itemDevice, "Block"); - if (!ObjectUtils.isEmpty(block)) { - deviceChannel.setBlock(block); - } - if (!ObjectUtils.isEmpty(address)) { - deviceChannel.setAddress(address); - } - - if (!ObjectUtils.isEmpty(secrecy)) { - deviceChannel.setSecrecy(secrecy); - } - - // 当为设备时,是否有子设备(必选)1有,0没有 - String parental = getText(itemDevice, "Parental"); - if (!ObjectUtils.isEmpty(parental)) { - try { - // 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1 - if (!ObjectUtils.isEmpty(parental) && parental.length() == 1 && Integer.parseInt(parental) == 0) { - deviceChannel.setParental(0); - }else { - deviceChannel.setParental(1); - } - }catch (NumberFormatException e) { - logger.warn("[xml解析] 从通道数据获取 parental失败: {}", parental); - } - } - // 父设备/区域/系统ID - - if (!ObjectUtils.isEmpty(parentID) ) { - if (parentID.contains("/")) { - String[] parentIdArray = parentID.split("/"); - deviceChannel.setParentId(parentIdArray[parentIdArray.length - 1]); - }else { - if (parentID.length()%2 == 0) { - deviceChannel.setParentId(parentID); - }else { - logger.warn("[xml解析] 不规范的parentID:{}, 已舍弃", parentID); - } - } - }else { - if (!ObjectUtils.isEmpty(businessGroupID)) { - deviceChannel.setParentId(businessGroupID); - }else { - if (!ObjectUtils.isEmpty(deviceChannel.getCivilCode())) { - deviceChannel.setParentId(deviceChannel.getCivilCode()); - } - } - } - // 注册方式 - if (!ObjectUtils.isEmpty(registerWay)) { - try { - int registerWayInt = Integer.parseInt(registerWay); - deviceChannel.setRegisterWay(registerWayInt); - }catch (NumberFormatException exception) { - logger.warn("[xml解析] 从通道数据获取registerWay失败: {}", registerWay); - deviceChannel.setRegisterWay(1); - } - }else { - deviceChannel.setRegisterWay(1); - } - - // 信令安全模式(可选)缺省为0; 0:不采用;2:S/MIME 签名方式;3:S/MIME加密签名同时采用方式;4:数字摘要方式 - String safetyWay = getText(itemDevice, "SafetyWay"); - if (!ObjectUtils.isEmpty(safetyWay)) { - try { - deviceChannel.setSafetyWay(Integer.parseInt(safetyWay)); - }catch (NumberFormatException e) { - logger.warn("[xml解析] 从通道数据获取 safetyWay失败: {}", safetyWay); - } - } - - // 证书序列号(有证书的设备必选) - String certNum = getText(itemDevice, "CertNum"); - if (!ObjectUtils.isEmpty(certNum)) { - deviceChannel.setCertNum(certNum); - } - - // 证书有效标识(有证书的设备必选)缺省为0;证书有效标识:0:无效 1:有效 - String certifiable = getText(itemDevice, "Certifiable"); - if (!ObjectUtils.isEmpty(certifiable)) { - try { - deviceChannel.setCertifiable(Integer.parseInt(certifiable)); - }catch (NumberFormatException e) { - logger.warn("[xml解析] 从通道数据获取 Certifiable失败: {}", certifiable); - } - } - - // 无效原因码(有证书且证书无效的设备必选) - String errCode = getText(itemDevice, "ErrCode"); - if (!ObjectUtils.isEmpty(errCode)) { - try { - deviceChannel.setErrCode(Integer.parseInt(errCode)); - }catch (NumberFormatException e) { - logger.warn("[xml解析] 从通道数据获取 ErrCode失败: {}", errCode); - } - } - - // 证书终止有效期(有证书的设备必选) - String endTime = getText(itemDevice, "EndTime"); - if (!ObjectUtils.isEmpty(endTime)) { - deviceChannel.setEndTime(endTime); - } - - - // 设备/区域/系统IP地址 - String ipAddress = getText(itemDevice, "IPAddress"); - if (!ObjectUtils.isEmpty(ipAddress)) { - deviceChannel.setIpAddress(ipAddress); - } - - // 设备/区域/系统端口 - String port = getText(itemDevice, "Port"); - if (!ObjectUtils.isEmpty(port)) { - try { - deviceChannel.setPort(Integer.parseInt(port)); - }catch (NumberFormatException e) { - logger.warn("[xml解析] 从通道数据获取 Port失败: {}", port); - } - } - - // 设备口令 - String password = getText(itemDevice, "Password"); - if (!ObjectUtils.isEmpty(password)) { - deviceChannel.setPassword(password); - } - - - // 设备状态 - String status = getText(itemDevice, "Status"); - if (status != null) { - // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理 - if (status.equalsIgnoreCase("ON") || status.equalsIgnoreCase("On") || status.equalsIgnoreCase("ONLINE") || status.equalsIgnoreCase("OK")) { - deviceChannel.setStatus(true); - } - if (status.equalsIgnoreCase("OFF") || status.equalsIgnoreCase("Off") || status.equalsIgnoreCase("OFFLINE")) { - deviceChannel.setStatus(false); - } - }else { - deviceChannel.setStatus(true); - } -// logger.info("状态字符串: {}", status); -// logger.info("状态结果: {}", deviceChannel.isStatus()); - // 经度 - String longitude = getText(itemDevice, "Longitude"); - if (NumericUtil.isDouble(longitude)) { - deviceChannel.setLongitude(Double.parseDouble(longitude)); - } else { - deviceChannel.setLongitude(0.00); - } - - // 纬度 - String latitude = getText(itemDevice, "Latitude"); - if (NumericUtil.isDouble(latitude)) { - deviceChannel.setLatitude(Double.parseDouble(latitude)); - } else { - deviceChannel.setLatitude(0.00); - } - - deviceChannel.setGpsTime(DateUtil.getNow()); - - // -摄像机类型扩展,标识摄像机类型:1-球机;2-半球;3-固定枪机;4-遥控枪机。当目录项为摄像机时可选 - String ptzType = getText(itemDevice, "PTZType"); - if (ObjectUtils.isEmpty(ptzType)) { - //兼容INFO中的信息 - Element info = itemDevice.element("Info"); - String ptzTypeFromInfo = XmlUtil.getText(info, "PTZType"); - if(!ObjectUtils.isEmpty(ptzTypeFromInfo)){ - try { - deviceChannel.setPtzType(Integer.parseInt(ptzTypeFromInfo)); - }catch (NumberFormatException e){ - logger.warn("[xml解析] 从通道数据info中获取PTZType失败: {}", ptzTypeFromInfo); - } - } - } else { - try { - deviceChannel.setPtzType(Integer.parseInt(ptzType)); - }catch (NumberFormatException e){ - logger.warn("[xml解析] 从通道数据中获取PTZType失败: {}", ptzType); - } - } - - // TODO 摄像机位置类型扩展。 - // 1-省际检查站、 - // 2-党政机关、 - // 3-车站码头、 - // 4-中心广场、 - // 5-体育场馆、 - // 6-商业中心、 - // 7-宗教场所、 - // 8-校园周边、 - // 9-治安复杂区域、 - // 10-交通干线。 - // String positionType = getText(itemDevice, "PositionType"); - - // TODO 摄像机安装位置室外、室内属性。1-室外、2-室内。 - // String roomType = getText(itemDevice, "RoomType"); - // TODO 摄像机用途属性 - // String useType = getText(itemDevice, "UseType"); - // TODO 摄像机补光属性。1-无补光、2-红外补光、3-白光补光 - // String supplyLightType = getText(itemDevice, "SupplyLightType"); - // TODO 摄像机监视方位属性。1-东、2-西、3-南、4-北、5-东南、6-东北、7-西南、8-西北。 - // String directionType = getText(itemDevice, "DirectionType"); - // TODO 摄像机支持的分辨率,可有多个分辨率值,各个取值间以“/”分隔。分辨率取值参见附录 F中SDPf字段规定 - // String resolution = getText(itemDevice, "Resolution"); - - // TODO 下载倍速范围(可选),各可选参数以“/”分隔,如设备支持1,2,4倍速下载则应写为“1/2/4 - // String downloadSpeed = getText(itemDevice, "DownloadSpeed"); - // TODO 空域编码能力,取值0:不支持;1:1级增强(1个增强层);2:2级增强(2个增强层);3:3级增强(3个增强层) - // String svcSpaceSupportMode = getText(itemDevice, "SVCSpaceSupportMode"); - // TODO 时域编码能力,取值0:不支持;1:1级增强;2:2级增强;3:3级增强 - // String svcTimeSupportMode = getText(itemDevice, "SVCTimeSupportMode"); - - - deviceChannel.setSecrecy(secrecy); - break; - } - } - - return deviceChannel; - } +// public static DeviceChannel channelContentHandler(Element itemDevice, Device device, String event){ +// DeviceChannel deviceChannel = new DeviceChannel(); +// deviceChannel.setDeviceId(device.getDeviceId()); +// Element channdelIdElement = itemDevice.element("DeviceID"); +// if (channdelIdElement == null) { +// logger.warn("解析Catalog消息时发现缺少 DeviceID"); +// return null; +// } +// String channelId = channdelIdElement.getTextTrim(); +// if (ObjectUtils.isEmpty(channelId)) { +// logger.warn("解析Catalog消息时发现缺少 DeviceID"); +// return null; +// } +// deviceChannel.setDeviceId(channelId); +// if (event != null && !event.equals(CatalogEvent.ADD) && !event.equals(CatalogEvent.UPDATE)) { +// // 除了ADD和update情况下需要识别全部内容, +// return deviceChannel; +// } +// Element nameElement = itemDevice.element("Name"); +// // 当通道名称为空时,设置通道名称为通道编码,避免级联时因通道名称为空导致上级接收通道失败 +// if (nameElement != null && StringUtils.isNotBlank(nameElement.getText())) { +// deviceChannel.setName(nameElement.getText()); +// } else { +// deviceChannel.setName(channelId); +// } +// if(channelId.length() <= 8) { +// deviceChannel.setHasAudio(false); +// CivilCodePo parentCode = CivilCodeUtil.INSTANCE.getParentCode(channelId); +// if (parentCode != null) { +// deviceChannel.setParentId(parentCode.getCode()); +// deviceChannel.setCivilCode(parentCode.getCode()); +// }else { +// logger.warn("[xml解析] 无法确定行政区划{}的上级行政区划", channelId); +// } +// deviceChannel.setStatus("ON"); +// return deviceChannel; +// }else { +// if(channelId.length() != 20) { +// logger.warn("[xml解析] 失败,编号不符合国标28181定义: {}", channelId); +// return null; +// } +// +// int code = Integer.parseInt(channelId.substring(10, 13)); +// if (code == 136 || code == 137 || code == 138) { +// deviceChannel.setHasAudio(true); +// }else { +// deviceChannel.setHasAudio(false); +// } +// // 设备厂商 +// String manufacturer = getText(itemDevice, "Manufacturer"); +// // 设备型号 +// String model = getText(itemDevice, "Model"); +// // 设备归属 +// String owner = getText(itemDevice, "Owner"); +// // 行政区域 +// String civilCode = getText(itemDevice, "CivilCode"); +// // 虚拟组织所属的业务分组ID,业务分组根据特定的业务需求制定,一个业务分组包含一组特定的虚拟组织 +// String businessGroupID = getText(itemDevice, "BusinessGroupID"); +// // 父设备/区域/系统ID +// String parentID = getText(itemDevice, "ParentID"); +// if (parentID != null && parentID.equalsIgnoreCase("null")) { +// parentID = null; +// } +// // 注册方式(必选)缺省为1;1:符合IETFRFC3261标准的认证注册模式;2:基于口令的双向认证注册模式;3:基于数字证书的双向认证注册模式 +// String registerWay = getText(itemDevice, "RegisterWay"); +// // 保密属性(必选)缺省为0;0:不涉密,1:涉密 +// String secrecy = getText(itemDevice, "Secrecy"); +// // 安装地址 +// String address = getText(itemDevice, "Address"); +// +// switch (code){ +// case 200: +// // 系统目录 +// if (!ObjectUtils.isEmpty(manufacturer)) { +// deviceChannel.setManufacture(manufacturer); +// } +// if (!ObjectUtils.isEmpty(model)) { +// deviceChannel.setModel(model); +// } +// if (!ObjectUtils.isEmpty(owner)) { +// deviceChannel.setOwner(owner); +// } +// if (!ObjectUtils.isEmpty(civilCode)) { +// deviceChannel.setCivilCode(civilCode); +// deviceChannel.setParentId(civilCode); +// }else { +// if (!ObjectUtils.isEmpty(parentID)) { +// deviceChannel.setParentId(parentID); +// } +// } +// if (!ObjectUtils.isEmpty(address)) { +// deviceChannel.setAddress(address); +// } +// deviceChannel.setStatus(true); +// if (!ObjectUtils.isEmpty(registerWay)) { +// try { +// deviceChannel.setRegisterWay(Integer.parseInt(registerWay)); +// }catch (NumberFormatException exception) { +// logger.warn("[xml解析] 从通道数据获取registerWay失败: {}", registerWay); +// } +// } +// if (!ObjectUtils.isEmpty(secrecy)) { +// deviceChannel.setSecrecy(secrecy); +// } +// return deviceChannel; +// case 215: +// // 业务分组 +// deviceChannel.setStatus(true); +// if (!ObjectUtils.isEmpty(parentID)) { +// if (!parentID.trim().equalsIgnoreCase(device.getDeviceId())) { +// deviceChannel.setParentId(parentID); +// } +// }else { +// logger.warn("[xml解析] 业务分组数据中缺少关键信息->ParentId"); +// if (!ObjectUtils.isEmpty(civilCode)) { +// deviceChannel.setCivilCode(civilCode); +// } +// } +// break; +// case 216: +// // 虚拟组织 +// deviceChannel.setStatus(true); +// if (!ObjectUtils.isEmpty(businessGroupID)) { +// deviceChannel.setBusinessGroupId(businessGroupID); +// } +// +// if (!ObjectUtils.isEmpty(parentID)) { +// if (parentID.contains("/")) { +// String[] parentIdArray = parentID.split("/"); +// parentID = parentIdArray[parentIdArray.length - 1]; +// } +// deviceChannel.setParentId(parentID); +// }else { +// if (!ObjectUtils.isEmpty(businessGroupID)) { +// deviceChannel.setParentId(businessGroupID); +// } +// } +// break; +// default: +// // 设备目录 +// if (!ObjectUtils.isEmpty(manufacturer)) { +// deviceChannel.setManufacture(manufacturer); +// } +// if (!ObjectUtils.isEmpty(model)) { +// deviceChannel.setModel(model); +// } +// if (!ObjectUtils.isEmpty(owner)) { +// deviceChannel.setOwner(owner); +// } +// if (!ObjectUtils.isEmpty(civilCode) +// && civilCode.length() <= 8 +// && NumberUtils.isParsable(civilCode) +// && civilCode.length()%2 == 0 +// ) { +// deviceChannel.setCivilCode(civilCode); +// } +// if (!ObjectUtils.isEmpty(businessGroupID)) { +// deviceChannel.setBusinessGroupId(businessGroupID); +// } +// +// // 警区 +// String block = getText(itemDevice, "Block"); +// if (!ObjectUtils.isEmpty(block)) { +// deviceChannel.setBlock(block); +// } +// if (!ObjectUtils.isEmpty(address)) { +// deviceChannel.setAddress(address); +// } +// +// if (!ObjectUtils.isEmpty(secrecy)) { +// deviceChannel.setSecrecy(secrecy); +// } +// +// // 当为设备时,是否有子设备(必选)1有,0没有 +// String parental = getText(itemDevice, "Parental"); +// if (!ObjectUtils.isEmpty(parental)) { +// try { +// // 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1 +// if (!ObjectUtils.isEmpty(parental) && parental.length() == 1 && Integer.parseInt(parental) == 0) { +// deviceChannel.setParental(0); +// }else { +// deviceChannel.setParental(1); +// } +// }catch (NumberFormatException e) { +// logger.warn("[xml解析] 从通道数据获取 parental失败: {}", parental); +// } +// } +// // 父设备/区域/系统ID +// +// if (!ObjectUtils.isEmpty(parentID) ) { +// if (parentID.contains("/")) { +// String[] parentIdArray = parentID.split("/"); +// deviceChannel.setParentId(parentIdArray[parentIdArray.length - 1]); +// }else { +// if (parentID.length()%2 == 0) { +// deviceChannel.setParentId(parentID); +// }else { +// logger.warn("[xml解析] 不规范的parentID:{}, 已舍弃", parentID); +// } +// } +// }else { +// if (!ObjectUtils.isEmpty(businessGroupID)) { +// deviceChannel.setParentId(businessGroupID); +// }else { +// if (!ObjectUtils.isEmpty(deviceChannel.getCivilCode())) { +// deviceChannel.setParentId(deviceChannel.getCivilCode()); +// } +// } +// } +// // 注册方式 +// if (!ObjectUtils.isEmpty(registerWay)) { +// try { +// int registerWayInt = Integer.parseInt(registerWay); +// deviceChannel.setRegisterWay(registerWayInt); +// }catch (NumberFormatException exception) { +// logger.warn("[xml解析] 从通道数据获取registerWay失败: {}", registerWay); +// deviceChannel.setRegisterWay(1); +// } +// }else { +// deviceChannel.setRegisterWay(1); +// } +// +// // 信令安全模式(可选)缺省为0; 0:不采用;2:S/MIME 签名方式;3:S/MIME加密签名同时采用方式;4:数字摘要方式 +// String safetyWay = getText(itemDevice, "SafetyWay"); +// if (!ObjectUtils.isEmpty(safetyWay)) { +// try { +// deviceChannel.setSafetyWay(Integer.parseInt(safetyWay)); +// }catch (NumberFormatException e) { +// logger.warn("[xml解析] 从通道数据获取 safetyWay失败: {}", safetyWay); +// } +// } +// +// // 证书序列号(有证书的设备必选) +// String certNum = getText(itemDevice, "CertNum"); +// if (!ObjectUtils.isEmpty(certNum)) { +// deviceChannel.setCertNum(certNum); +// } +// +// // 证书有效标识(有证书的设备必选)缺省为0;证书有效标识:0:无效 1:有效 +// String certifiable = getText(itemDevice, "Certifiable"); +// if (!ObjectUtils.isEmpty(certifiable)) { +// try { +// deviceChannel.setCertifiable(Integer.parseInt(certifiable)); +// }catch (NumberFormatException e) { +// logger.warn("[xml解析] 从通道数据获取 Certifiable失败: {}", certifiable); +// } +// } +// +// // 无效原因码(有证书且证书无效的设备必选) +// String errCode = getText(itemDevice, "ErrCode"); +// if (!ObjectUtils.isEmpty(errCode)) { +// try { +// deviceChannel.setErrCode(Integer.parseInt(errCode)); +// }catch (NumberFormatException e) { +// logger.warn("[xml解析] 从通道数据获取 ErrCode失败: {}", errCode); +// } +// } +// +// // 证书终止有效期(有证书的设备必选) +// String endTime = getText(itemDevice, "EndTime"); +// if (!ObjectUtils.isEmpty(endTime)) { +// deviceChannel.setEndTime(endTime); +// } +// +// +// // 设备/区域/系统IP地址 +// String ipAddress = getText(itemDevice, "IPAddress"); +// if (!ObjectUtils.isEmpty(ipAddress)) { +// deviceChannel.setIpAddress(ipAddress); +// } +// +// // 设备/区域/系统端口 +// String port = getText(itemDevice, "Port"); +// if (!ObjectUtils.isEmpty(port)) { +// try { +// deviceChannel.setPort(Integer.parseInt(port)); +// }catch (NumberFormatException e) { +// logger.warn("[xml解析] 从通道数据获取 Port失败: {}", port); +// } +// } +// +// // 设备口令 +// String password = getText(itemDevice, "Password"); +// if (!ObjectUtils.isEmpty(password)) { +// deviceChannel.setPassword(password); +// } +// +// +// // 设备状态 +// String status = getText(itemDevice, "Status"); +// if (status != null) { +// // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理 +// if (status.equalsIgnoreCase("ON") || status.equalsIgnoreCase("On") || status.equalsIgnoreCase("ONLINE") || status.equalsIgnoreCase("OK")) { +// deviceChannel.setStatus(true); +// } +// if (status.equalsIgnoreCase("OFF") || status.equalsIgnoreCase("Off") || status.equalsIgnoreCase("OFFLINE")) { +// deviceChannel.setStatus(false); +// } +// }else { +// deviceChannel.setStatus(true); +// } +//// logger.info("状态字符串: {}", status); +//// logger.info("状态结果: {}", deviceChannel.isStatus()); +// // 经度 +// String longitude = getText(itemDevice, "Longitude"); +// if (NumericUtil.isDouble(longitude)) { +// deviceChannel.setLongitude(Double.parseDouble(longitude)); +// } else { +// deviceChannel.setLongitude(0.00); +// } +// +// // 纬度 +// String latitude = getText(itemDevice, "Latitude"); +// if (NumericUtil.isDouble(latitude)) { +// deviceChannel.setLatitude(Double.parseDouble(latitude)); +// } else { +// deviceChannel.setLatitude(0.00); +// } +// +// deviceChannel.setGpsTime(DateUtil.getNow()); +// +// // -摄像机类型扩展,标识摄像机类型:1-球机;2-半球;3-固定枪机;4-遥控枪机。当目录项为摄像机时可选 +// String ptzType = getText(itemDevice, "PTZType"); +// if (ObjectUtils.isEmpty(ptzType)) { +// //兼容INFO中的信息 +// Element info = itemDevice.element("Info"); +// String ptzTypeFromInfo = XmlUtil.getText(info, "PTZType"); +// if(!ObjectUtils.isEmpty(ptzTypeFromInfo)){ +// try { +// deviceChannel.setPtzType(Integer.parseInt(ptzTypeFromInfo)); +// }catch (NumberFormatException e){ +// logger.warn("[xml解析] 从通道数据info中获取PTZType失败: {}", ptzTypeFromInfo); +// } +// } +// } else { +// try { +// deviceChannel.setPtzType(Integer.parseInt(ptzType)); +// }catch (NumberFormatException e){ +// logger.warn("[xml解析] 从通道数据中获取PTZType失败: {}", ptzType); +// } +// } +// +// // TODO 摄像机位置类型扩展。 +// // 1-省际检查站、 +// // 2-党政机关、 +// // 3-车站码头、 +// // 4-中心广场、 +// // 5-体育场馆、 +// // 6-商业中心、 +// // 7-宗教场所、 +// // 8-校园周边、 +// // 9-治安复杂区域、 +// // 10-交通干线。 +// // String positionType = getText(itemDevice, "PositionType"); +// +// // TODO 摄像机安装位置室外、室内属性。1-室外、2-室内。 +// // String roomType = getText(itemDevice, "RoomType"); +// // TODO 摄像机用途属性 +// // String useType = getText(itemDevice, "UseType"); +// // TODO 摄像机补光属性。1-无补光、2-红外补光、3-白光补光 +// // String supplyLightType = getText(itemDevice, "SupplyLightType"); +// // TODO 摄像机监视方位属性。1-东、2-西、3-南、4-北、5-东南、6-东北、7-西南、8-西北。 +// // String directionType = getText(itemDevice, "DirectionType"); +// // TODO 摄像机支持的分辨率,可有多个分辨率值,各个取值间以“/”分隔。分辨率取值参见附录 F中SDPf字段规定 +// // String resolution = getText(itemDevice, "Resolution"); +// +// // TODO 下载倍速范围(可选),各可选参数以“/”分隔,如设备支持1,2,4倍速下载则应写为“1/2/4 +// // String downloadSpeed = getText(itemDevice, "DownloadSpeed"); +// // TODO 空域编码能力,取值0:不支持;1:1级增强(1个增强层);2:2级增强(2个增强层);3:3级增强(3个增强层) +// // String svcSpaceSupportMode = getText(itemDevice, "SVCSpaceSupportMode"); +// // TODO 时域编码能力,取值0:不支持;1:1级增强;2:2级增强;3:3级增强 +// // String svcTimeSupportMode = getText(itemDevice, "SVCTimeSupportMode"); +// +// +// deviceChannel.setSecrecy(secrecy); +// break; +// } +// } +// +// return deviceChannel; +// } /** * 新增方法支持内部嵌套 @@ -671,6 +654,50 @@ public class XmlUtil { return t; } + public static T elementDecode(Element element, Class clazz) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + Field[] fields = clazz.getDeclaredFields(); + T t = clazz.getDeclaredConstructor().newInstance(); + for (Field field : fields) { + ReflectionUtils.makeAccessible(field); + MessageElementForCatalog annotation = field.getAnnotation(MessageElementForCatalog.class); + if (annotation == null) { + continue; + } + String[] values = annotation.value(); + for (String value : values) { + boolean subVal = value.contains("."); + if (!subVal) { + Element element1 = element.element(value); + if (element1 == null) { + continue; + } + // 无下级数据 + Object fieldVal = element1.isTextOnly() ? element1.getText() : loadElement(element1, field.getType()); + Object o = simpleTypeDeal(field.getType(), fieldVal); + ReflectionUtils.setField(field, t, o); + break; + } else { + String[] pathArray = value.split("\\."); + Element subElement = element; + for (String path : pathArray) { + subElement = subElement.element(path); + if (subElement == null) { + break; + } + } + if (subElement == null) { + continue; + } + Object fieldVal = subElement.isTextOnly() ? subElement.getText() : loadElement(subElement, field.getType()); + Object o = simpleTypeDeal(field.getType(), fieldVal); + ReflectionUtils.setField(field, t, o); + } + } + + } + return t; + } + /** * 简单类型处理 * @@ -679,6 +706,9 @@ public class XmlUtil { * @return */ private static Object simpleTypeDeal(Class tClass, Object val) { + if (val == null || val.toString().equalsIgnoreCase("null")) { + return null; + } if (tClass.equals(String.class)) { return val.toString(); } diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/decode/Jt808Decoder.java b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/decode/Jt808Decoder.java index a72fea578..7368c9fbe 100644 --- a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/decode/Jt808Decoder.java +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/decode/Jt808Decoder.java @@ -16,6 +16,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; +import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; import java.util.List; @@ -25,8 +26,8 @@ import java.util.List; * @date 2023/4/27 18:10 * @email qingtaij@163.com */ +@Slf4j public class Jt808Decoder extends ByteToMessageDecoder { - private final static Logger log = LoggerFactory.getLogger(Jt808Decoder.class); private ApplicationEventPublisher applicationEventPublisher = null; private Ijt1078Service service = null; diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808Encoder.java b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808Encoder.java index 387884155..949177db6 100644 --- a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808Encoder.java +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808Encoder.java @@ -7,8 +7,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import java.util.List; @@ -17,8 +16,8 @@ import java.util.List; * @date 2023/4/27 18:10 * @email qingtaij@163.com */ +@Slf4j public class Jt808Encoder extends MessageToByteEncoder { - private final static Logger log = LoggerFactory.getLogger(Jt808Encoder.class); @Override protected void encode(ChannelHandlerContext ctx, Rs msg, ByteBuf out) throws Exception { diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808EncoderCmd.java b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808EncoderCmd.java index f4c760e3a..095306a56 100644 --- a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808EncoderCmd.java +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808EncoderCmd.java @@ -13,8 +13,7 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; import io.netty.util.ByteProcessor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import java.util.LinkedList; @@ -25,8 +24,8 @@ import java.util.List; * @date 2023/4/27 18:25 * @email qingtaij@163.com */ +@Slf4j public class Jt808EncoderCmd extends MessageToByteEncoder { - private final static Logger log = LoggerFactory.getLogger(Jt808EncoderCmd.class); @Override protected void encode(ChannelHandlerContext ctx, Cmd cmd, ByteBuf out) throws Exception { diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/Jt808Handler.java b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/Jt808Handler.java index 61403670d..802bbf69e 100644 --- a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/Jt808Handler.java +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/Jt808Handler.java @@ -12,19 +12,18 @@ import io.netty.handler.timeout.IdleStateEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEventPublisher; +import lombok.extern.slf4j.Slf4j; /** * @author QingtaiJiang * @date 2023/4/27 18:14 * @email qingtaij@163.com */ +@Slf4j public class Jt808Handler extends ChannelInboundHandlerAdapter { - private final static Logger log = LoggerFactory.getLogger(Jt808Handler.class); - private ApplicationEventPublisher applicationEventPublisher = null; - public Jt808Handler(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/TcpServer.java b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/TcpServer.java index 44b734c78..3996c3fa1 100644 --- a/src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/TcpServer.java +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/TcpServer.java @@ -25,6 +25,7 @@ import org.springframework.boot.CommandLineRunner; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; +import lombok.extern.slf4j.Slf4j; import java.util.concurrent.TimeUnit; @@ -34,9 +35,8 @@ import java.util.concurrent.TimeUnit; * @email qingtaij@163.com */ - -public class TcpServer{ - private final static Logger log = LoggerFactory.getLogger(TcpServer.class); +@Slf4j +public class TcpServer { private final Integer port; private boolean isRunning = false; diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/factory/CodecFactory.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/factory/CodecFactory.java index 45d5fc71f..8551e39aa 100644 --- a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/factory/CodecFactory.java +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/factory/CodecFactory.java @@ -3,8 +3,7 @@ package com.genersoft.iot.vmp.jt1078.proc.factory; import com.genersoft.iot.vmp.jt1078.annotation.MsgId; import com.genersoft.iot.vmp.jt1078.proc.request.Re; import com.genersoft.iot.vmp.jt1078.util.ClassUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import java.util.HashMap; import java.util.List; @@ -15,9 +14,8 @@ import java.util.Map; * @date 2023/4/27 18:29 * @email qingtaij@163.com */ - +@Slf4j public class CodecFactory { - private final static Logger log = LoggerFactory.getLogger(CodecFactory.class); private static Map> protocolHash; diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/Re.java b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/Re.java index f4b11e73c..734f2d25b 100644 --- a/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/Re.java +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/Re.java @@ -8,6 +8,7 @@ import io.netty.buffer.ByteBuf; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEvent; +import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; /** @@ -15,8 +16,8 @@ import org.springframework.util.StringUtils; * @date 2023/4/27 18:50 * @email qingtaij@163.com */ +@Slf4j public abstract class Re { - private final static Logger log = LoggerFactory.getLogger(Re.class); protected abstract Rs decode0(ByteBuf buf, Header header, Session session); diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/session/Session.java b/src/main/java/com/genersoft/iot/vmp/jt1078/session/Session.java index e8a96815d..392f911d5 100644 --- a/src/main/java/com/genersoft/iot/vmp/jt1078/session/Session.java +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/session/Session.java @@ -3,8 +3,7 @@ package com.genersoft.iot.vmp.jt1078.session; import com.genersoft.iot.vmp.jt1078.proc.Header; import io.netty.channel.Channel; import io.netty.util.AttributeKey; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import java.util.concurrent.atomic.AtomicInteger; @@ -13,8 +12,8 @@ import java.util.concurrent.atomic.AtomicInteger; * @date 2023/4/27 18:54 * @email qingtaij@163.com */ +@Slf4j public class Session { - private final static Logger log = LoggerFactory.getLogger(Session.class); public static final AttributeKey KEY = AttributeKey.newInstance(Session.class.getName()); diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/session/SessionManager.java b/src/main/java/com/genersoft/iot/vmp/jt1078/session/SessionManager.java index 4d20e9c2b..3c1bd7390 100644 --- a/src/main/java/com/genersoft/iot/vmp/jt1078/session/SessionManager.java +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/session/SessionManager.java @@ -2,8 +2,7 @@ package com.genersoft.iot.vmp.jt1078.session; import com.genersoft.iot.vmp.jt1078.proc.entity.Cmd; import io.netty.channel.Channel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -16,9 +15,9 @@ import java.util.concurrent.TimeUnit; * @date 2023/4/27 19:54 * @email qingtaij@163.com */ +@Slf4j public enum SessionManager { INSTANCE; - private final static Logger log = LoggerFactory.getLogger(SessionManager.class); // 用与消息的缓存 private final Map> topicSubscribers = new ConcurrentHashMap<>(); diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/util/ClassUtil.java b/src/main/java/com/genersoft/iot/vmp/jt1078/util/ClassUtil.java index 3dcb1b860..9c3fd2868 100644 --- a/src/main/java/com/genersoft/iot/vmp/jt1078/util/ClassUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/util/ClassUtil.java @@ -1,7 +1,6 @@ package com.genersoft.iot.vmp.jt1078.util; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; @@ -10,17 +9,15 @@ import java.lang.annotation.Annotation; import java.util.LinkedList; import java.util.List; +@Slf4j public class ClassUtil { - private static final Logger logger = LoggerFactory.getLogger(ClassUtil.class); - - public static Object getBean(Class clazz) { if (clazz != null) { try { return clazz.getDeclaredConstructor().newInstance(); } catch (Exception ex) { - logger.error("ClassUtil:找不到指定的类", ex); + log.error("ClassUtil:找不到指定的类", ex); } } return null; @@ -32,14 +29,14 @@ public class ClassUtil { try { clazz = Class.forName(className); } catch (Exception ex) { - logger.error("ClassUtil:找不到指定的类"); + log.error("ClassUtil:找不到指定的类"); } if (clazz != null) { try { //获取声明的构造器--》创建实例 return clazz.getDeclaredConstructor().newInstance(); } catch (Exception ex) { - logger.error("ClassUtil:找不到指定的类", ex); + log.error("ClassUtil:找不到指定的类", ex); } } return null; diff --git a/src/main/java/com/genersoft/iot/vmp/media/MediaServerConfig.java b/src/main/java/com/genersoft/iot/vmp/media/MediaServerConfig.java index ccd2828e1..1c6bda3d5 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/MediaServerConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/media/MediaServerConfig.java @@ -4,8 +4,7 @@ import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.context.ApplicationEventPublisher; @@ -17,12 +16,11 @@ import java.util.List; /** * 启动是从配置文件加载节点信息,以及发送个节点状态管理去控制节点状态 */ +@Slf4j @Component @Order(value=12) public class MediaServerConfig implements CommandLineRunner { - private final static Logger logger = LoggerFactory.getLogger(MediaServerConfig.class); - @Autowired private ApplicationEventPublisher applicationEventPublisher; @@ -56,7 +54,7 @@ public class MediaServerConfig implements CommandLineRunner { mediaServerService.syncCatchFromDatabase(); // 获取所有的zlm, 并开启主动连接 List all = mediaServerService.getAllFromDatabase(); - logger.info("[媒体节点] 加载节点列表, 共{}个节点", all.size()); + log.info("[媒体节点] 加载节点列表, 共{}个节点", all.size()); MediaServerChangeEvent event = new MediaServerChangeEvent(this); event.setMediaServerItemList(all); applicationEventPublisher.publishEvent(event); diff --git a/src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java b/src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java index 2dd92eb56..ace469866 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java @@ -3,13 +3,17 @@ package com.genersoft.iot.vmp.media.bean; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; +import com.genersoft.iot.vmp.utils.MediaServerUtils; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; import java.util.List; +import java.util.Map; /** * 视频信息 */ +@Data @Schema(description = "视频信息") public class MediaInfo { @Schema(description = "应用名") @@ -41,16 +45,23 @@ public class MediaInfo { private Boolean online; @Schema(description = "unknown = 0,rtmp_push=1,rtsp_push=2,rtp_push=3,pull=4,ffmpeg_pull=5,mp4_vod=6,device_chn=7") private Integer originType; + @Schema(description = "产生流的源流地址") + private String originUrl; @Schema(description = "存活时间,单位秒") private Long aliveSecond; @Schema(description = "数据产生速度,单位byte/s") private Long bytesSpeed; @Schema(description = "鉴权参数") private String callId; + @Schema(description = "额外参数") + private Map paramMap; + @Schema(description = "服务ID") + private String serverId; - public static MediaInfo getInstance(JSONObject jsonObject, MediaServer mediaServer) { + public static MediaInfo getInstance(JSONObject jsonObject, MediaServer mediaServer, String serverId) { MediaInfo mediaInfo = new MediaInfo(); mediaInfo.setMediaServer(mediaServer); + mediaInfo.setServerId(serverId); String app = jsonObject.getString("app"); mediaInfo.setApp(app); String stream = jsonObject.getString("stream"); @@ -60,7 +71,9 @@ public class MediaInfo { Integer totalReaderCount = jsonObject.getInteger("totalReaderCount"); Boolean online = jsonObject.getBoolean("online"); Integer originType = jsonObject.getInteger("originType"); + String originUrl = jsonObject.getString("originUrl"); Long aliveSecond = jsonObject.getLong("aliveSecond"); + String params = jsonObject.getString("params"); Long bytesSpeed = jsonObject.getLong("bytesSpeed"); if (totalReaderCount != null) { mediaInfo.setReaderCount(totalReaderCount); @@ -71,12 +84,22 @@ public class MediaInfo { if (originType != null) { mediaInfo.setOriginType(originType); } + if (originUrl != null) { + mediaInfo.setOriginUrl(originUrl); + } + if (aliveSecond != null) { mediaInfo.setAliveSecond(aliveSecond); } if (bytesSpeed != null) { mediaInfo.setBytesSpeed(bytesSpeed); } + if (params != null) { + mediaInfo.setParamMap(MediaServerUtils.urlParamToMap(params)); + if(mediaInfo.getCallId() == null) { + mediaInfo.setCallId(mediaInfo.getParamMap().get("callId")); + } + } JSONArray jsonArray = jsonObject.getJSONArray("tracks"); if (jsonArray.isEmpty()) { return null; @@ -128,7 +151,7 @@ public class MediaInfo { return mediaInfo; } - public static MediaInfo getInstance(OnStreamChangedHookParam param, MediaServer mediaServer) { + public static MediaInfo getInstance(OnStreamChangedHookParam param, MediaServer mediaServer, String serverId) { MediaInfo mediaInfo = new MediaInfo(); mediaInfo.setApp(param.getApp()); @@ -138,8 +161,14 @@ public class MediaInfo { mediaInfo.setReaderCount(param.getTotalReaderCount()); mediaInfo.setOnline(param.isRegist()); mediaInfo.setOriginType(param.getOriginType()); + mediaInfo.setOriginUrl(param.getOriginUrl()); mediaInfo.setAliveSecond(param.getAliveSecond()); mediaInfo.setBytesSpeed(param.getBytesSpeed()); + mediaInfo.setParamMap(param.getParamMap()); + if(mediaInfo.getCallId() == null) { + mediaInfo.setCallId(param.getParamMap().get("callId")); + } + mediaInfo.setServerId(serverId); List tracks = param.getTracks(); if (tracks == null || tracks.isEmpty()) { return mediaInfo; @@ -177,140 +206,4 @@ public class MediaInfo { } return mediaInfo; } - - public Integer getReaderCount() { - return readerCount; - } - - public void setReaderCount(Integer readerCount) { - this.readerCount = readerCount; - } - - public String getVideoCodec() { - return videoCodec; - } - - public void setVideoCodec(String videoCodec) { - this.videoCodec = videoCodec; - } - - public Integer getWidth() { - return width; - } - - public void setWidth(Integer width) { - this.width = width; - } - - public Integer getHeight() { - return height; - } - - public void setHeight(Integer height) { - this.height = height; - } - - public String getAudioCodec() { - return audioCodec; - } - - public void setAudioCodec(String audioCodec) { - this.audioCodec = audioCodec; - } - - public Integer getAudioChannels() { - return audioChannels; - } - - public void setAudioChannels(Integer audioChannels) { - this.audioChannels = audioChannels; - } - - public Integer getAudioSampleRate() { - return audioSampleRate; - } - - public void setAudioSampleRate(Integer audioSampleRate) { - this.audioSampleRate = audioSampleRate; - } - - public Long getDuration() { - return duration; - } - - public void setDuration(Long duration) { - this.duration = duration; - } - - public Boolean getOnline() { - return online; - } - - public void setOnline(Boolean online) { - this.online = online; - } - - public Integer getOriginType() { - return originType; - } - - public void setOriginType(Integer originType) { - this.originType = originType; - } - - public Long getAliveSecond() { - return aliveSecond; - } - - public void setAliveSecond(Long aliveSecond) { - this.aliveSecond = aliveSecond; - } - - public Long getBytesSpeed() { - return bytesSpeed; - } - - public void setBytesSpeed(Long bytesSpeed) { - this.bytesSpeed = bytesSpeed; - } - - 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 MediaServer getMediaServer() { - return mediaServer; - } - - public void setMediaServer(MediaServer mediaServer) { - this.mediaServer = mediaServer; - } - - public String getSchema() { - return schema; - } - - public void setSchema(String schema) { - this.schema = schema; - } - - public String getCallId() { - return callId; - } - - public void setCallId(String callId) { - this.callId = callId; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/event/hook/Hook.java b/src/main/java/com/genersoft/iot/vmp/media/event/hook/Hook.java index 341e3c361..0d1c55a97 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/event/hook/Hook.java +++ b/src/main/java/com/genersoft/iot/vmp/media/event/hook/Hook.java @@ -1,9 +1,12 @@ package com.genersoft.iot.vmp.media.event.hook; +import lombok.Data; + /** * zlm hook事件的参数 * @author lin */ +@Data public class Hook { private HookType hookType; @@ -12,60 +15,21 @@ public class Hook { private String stream; - private String mediaServerId; - private Long expireTime; - public static Hook getInstance(HookType hookType, String app, String stream, String mediaServerId) { + public static Hook getInstance(HookType hookType, String app, String stream) { Hook hookSubscribe = new Hook(); hookSubscribe.setApp(app); hookSubscribe.setStream(stream); hookSubscribe.setHookType(hookType); - hookSubscribe.setMediaServerId(mediaServerId); hookSubscribe.setExpireTime(System.currentTimeMillis() + 5 * 60 * 1000); return hookSubscribe; } - public HookType getHookType() { - return hookType; - } - - public void setHookType(HookType hookType) { - this.hookType = hookType; - } - - 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 Long getExpireTime() { - return expireTime; - } - - public void setExpireTime(Long expireTime) { - this.expireTime = expireTime; - } - - public String getMediaServerId() { - return mediaServerId; - } - - public void setMediaServerId(String mediaServerId) { - this.mediaServerId = mediaServerId; + public static Hook getInstance(HookType hookType, String app, String stream, String mediaServer) { + // TODO 后续修改所有方法 + return Hook.getInstance(hookType, app, stream); } @Override @@ -74,8 +38,7 @@ public class Hook { Hook param = (Hook) obj; return param.getHookType().equals(this.hookType) && param.getApp().equals(this.app) - && param.getStream().equals(this.stream) - && param.getMediaServerId().equals(this.mediaServerId); + && param.getStream().equals(this.stream); }else { return false; } @@ -83,6 +46,6 @@ public class Hook { @Override public String toString() { - return this.getHookType() + this.getApp() + this.getStream() + this.getMediaServerId(); + return this.getHookType() + this.getApp() + this.getStream(); } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/event/hook/HookData.java b/src/main/java/com/genersoft/iot/vmp/media/event/hook/HookData.java index d4a82a7e2..6aa3a675d 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/event/hook/HookData.java +++ b/src/main/java/com/genersoft/iot/vmp/media/event/hook/HookData.java @@ -8,10 +8,12 @@ import com.genersoft.iot.vmp.media.event.media.MediaPublishEvent; import com.genersoft.iot.vmp.media.event.media.MediaRecordMp4Event; import com.genersoft.iot.vmp.media.bean.MediaServer; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; /** * Hook返回的内容 */ +@Data public class HookData { /** * 应用名 @@ -73,60 +75,4 @@ public class HookData { } return hookData; } - - 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 MediaServer getMediaServer() { - return mediaServer; - } - - public void setMediaServer(MediaServer mediaServer) { - this.mediaServer = mediaServer; - } - - public String getSchema() { - return schema; - } - - public void setSchema(String schema) { - this.schema = schema; - } - - public MediaInfo getMediaInfo() { - return mediaInfo; - } - - public void setMediaInfo(MediaInfo mediaInfo) { - this.mediaInfo = mediaInfo; - } - - public String getParams() { - return params; - } - - public void setParams(String params) { - this.params = params; - } - - public RecordInfo getRecordInfo() { - return recordInfo; - } - - public void setRecordInfo(RecordInfo recordInfo) { - this.recordInfo = recordInfo; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/event/hook/HookSubscribe.java b/src/main/java/com/genersoft/iot/vmp/media/event/hook/HookSubscribe.java index 58e376195..b88fdd443 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/event/hook/HookSubscribe.java +++ b/src/main/java/com/genersoft/iot/vmp/media/event/hook/HookSubscribe.java @@ -6,6 +6,8 @@ import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -21,6 +23,7 @@ public class HookSubscribe { */ private final long subscribeExpire = 5 * 60 * 1000; + @FunctionalInterface public interface Event{ void response(HookData data); @@ -70,7 +73,7 @@ public class HookSubscribe { private final Map allHook = new ConcurrentHashMap<>(); private void sendNotify(HookType hookType, MediaEvent event) { - Hook paramHook = Hook.getInstance(hookType, event.getApp(), event.getStream(), event.getMediaServer().getId()); + Hook paramHook = Hook.getInstance(hookType, event.getApp(), event.getStream()); Event hookSubscribeEvent = allSubscribes.get(paramHook.toString()); if (hookSubscribeEvent != null) { HookData data = HookData.getInstance(event); @@ -79,6 +82,7 @@ public class HookSubscribe { } public void addSubscribe(Hook hook, HookSubscribe.Event event) { + System.out.println("add==" + hook.toString()); if (hook.getExpireTime() == null) { hook.setExpireTime(System.currentTimeMillis() + subscribeExpire); } @@ -104,4 +108,8 @@ public class HookSubscribe { } } } + + public List getAll() { + return new ArrayList<>(allHook.values()); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/event/media/MediaArrivalEvent.java b/src/main/java/com/genersoft/iot/vmp/media/event/media/MediaArrivalEvent.java index 2f51a3e21..b91b77b0a 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/event/media/MediaArrivalEvent.java +++ b/src/main/java/com/genersoft/iot/vmp/media/event/media/MediaArrivalEvent.java @@ -4,65 +4,51 @@ import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; import com.genersoft.iot.vmp.vmanager.bean.StreamContent; +import lombok.Getter; +import lombok.Setter; + +import java.util.Map; /** * 流到来事件 */ + public class MediaArrivalEvent extends MediaEvent { public MediaArrivalEvent(Object source) { super(source); } - public static MediaArrivalEvent getInstance(Object source, OnStreamChangedHookParam hookParam, MediaServer mediaServer){ + public static MediaArrivalEvent getInstance(Object source, OnStreamChangedHookParam hookParam, MediaServer mediaServer, String serverId){ MediaArrivalEvent mediaArrivalEvent = new MediaArrivalEvent(source); - mediaArrivalEvent.setMediaInfo(MediaInfo.getInstance(hookParam, mediaServer)); + mediaArrivalEvent.setMediaInfo(MediaInfo.getInstance(hookParam, mediaServer, serverId)); mediaArrivalEvent.setApp(hookParam.getApp()); mediaArrivalEvent.setStream(hookParam.getStream()); mediaArrivalEvent.setMediaServer(mediaServer); mediaArrivalEvent.setSchema(hookParam.getSchema()); mediaArrivalEvent.setSchema(hookParam.getSchema()); - mediaArrivalEvent.setHookParam(hookParam); + mediaArrivalEvent.setParamMap(hookParam.getParamMap()); return mediaArrivalEvent; } + @Getter + @Setter private MediaInfo mediaInfo; + @Getter + @Setter private String callId; - private OnStreamChangedHookParam hookParam; - + @Getter + @Setter private StreamContent streamInfo; - public MediaInfo getMediaInfo() { - return mediaInfo; - } + @Getter + @Setter + private Map paramMap; - public void setMediaInfo(MediaInfo mediaInfo) { - this.mediaInfo = mediaInfo; - } + @Getter + @Setter + private String serverId; - public String getCallId() { - return callId; - } - - public void setCallId(String callId) { - this.callId = callId; - } - - public OnStreamChangedHookParam getHookParam() { - return hookParam; - } - - public void setHookParam(OnStreamChangedHookParam hookParam) { - this.hookParam = hookParam; - } - - public StreamContent getStreamInfo() { - return streamInfo; - } - - public void setStreamInfo(StreamContent streamInfo) { - this.streamInfo = streamInfo; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerStatusEventListener.java b/src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerStatusEventListener.java index 2413f5676..e30ffea40 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerStatusEventListener.java +++ b/src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerStatusEventListener.java @@ -1,10 +1,7 @@ package com.genersoft.iot.vmp.media.event.mediaServer; -import com.genersoft.iot.vmp.service.IPlayService; -import com.genersoft.iot.vmp.service.IStreamProxyService; -import com.genersoft.iot.vmp.service.IStreamPushService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.genersoft.iot.vmp.gb28181.service.IPlayService; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; @@ -18,26 +15,17 @@ import org.springframework.stereotype.Component; * @author: swwheihei * @date: 2020年5月6日 下午1:51:23 */ +@Slf4j @Component public class MediaServerStatusEventListener { - private final static Logger logger = LoggerFactory.getLogger(MediaServerStatusEventListener.class); - - @Autowired - private IStreamPushService streamPushService; - - @Autowired - private IStreamProxyService streamProxyService; - @Autowired private IPlayService playService; @Async("taskExecutor") @EventListener public void onApplicationEvent(MediaServerOnlineEvent event) { - logger.info("[媒体节点] 上线 ID:" + event.getMediaServerId()); - streamPushService.zlmServerOnline(event.getMediaServerId()); - streamProxyService.zlmServerOnline(event.getMediaServerId()); + log.info("[媒体节点] 上线 ID:" + event.getMediaServerId()); playService.zlmServerOnline(event.getMediaServerId()); } @@ -45,10 +33,8 @@ public class MediaServerStatusEventListener { @EventListener public void onApplicationEvent(MediaServerOfflineEvent event) { - logger.info("[媒体节点] 离线,ID:" + event.getMediaServerId()); + log.info("[媒体节点] 离线,ID:" + event.getMediaServerId()); // 处理ZLM离线 - streamProxyService.zlmServerOffline(event.getMediaServerId()); - streamPushService.zlmServerOffline(event.getMediaServerId()); playService.zlmServerOffline(event.getMediaServerId()); } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java b/src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java index 0efc0b926..eb3a2cb1e 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java @@ -2,9 +2,10 @@ package com.genersoft.iot.vmp.media.service; import com.genersoft.iot.vmp.common.CommonCallback; import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import java.util.List; @@ -49,7 +50,7 @@ public interface IMediaNodeServerService { WVPResult addFFmpegSource(MediaServer mediaServer, String srcUrl, String dstUrl, int timeoutMs, boolean enableAudio, boolean enableMp4, String ffmpegCmdKey); - WVPResult addStreamProxy(MediaServer mediaServer, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType); + WVPResult addStreamProxy(MediaServer mediaServer, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType, Integer timeout); Boolean delFFmpegSource(MediaServer mediaServer, String streamKey); @@ -57,9 +58,13 @@ public interface IMediaNodeServerService { Map getFFmpegCMDs(MediaServer mediaServer); - Integer startSendRtpPassive(MediaServer mediaServer, SendRtpItem sendRtpItem, Integer timeout); + void startSendRtpPassive(MediaServer mediaServer, SendRtpInfo sendRtpItem, Integer timeout); - void startSendRtpStream(MediaServer mediaServer, SendRtpItem sendRtpItem); + void startSendRtpStream(MediaServer mediaServer, SendRtpInfo sendRtpItem); Long updateDownloadProcess(MediaServer mediaServer, String app, String stream); + + StreamInfo startProxy(MediaServer mediaServer, StreamProxy streamProxy); + + void stopProxy(MediaServer mediaServer, String streamKey); } diff --git a/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java b/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java index 943ea3df2..3d1f7880c 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java @@ -2,11 +2,12 @@ package com.genersoft.iot.vmp.media.service; import com.genersoft.iot.vmp.common.CommonCallback; import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.service.bean.MediaServerLoad; import com.genersoft.iot.vmp.service.bean.SSRCInfo; +import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import java.util.List; @@ -39,6 +40,7 @@ public interface IMediaServerService { void closeRTPServer(MediaServer mediaServerItem, String streamId); void closeRTPServer(MediaServer mediaServerItem, String streamId, CommonCallback callback); + Boolean updateRtpServerSSRC(MediaServer mediaServerItem, String streamId, String ssrc); void closeRTPServer(String mediaServerId, String streamId); @@ -97,7 +99,7 @@ public interface IMediaServerService { WVPResult addFFmpegSource(MediaServer mediaServerItem, String srcUrl, String dstUrl, int timeoutMs, boolean enableAudio, boolean enableMp4, String ffmpegCmdKey); - WVPResult addStreamProxy(MediaServer mediaServerItem, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType); + WVPResult addStreamProxy(MediaServer mediaServerItem, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType, Integer timeout); Boolean delFFmpegSource(MediaServer mediaServerItem, String streamKey); @@ -140,16 +142,20 @@ public interface IMediaServerService { Boolean isStreamReady(MediaServer mediaServer, String rtp, String streamId); - Integer startSendRtpPassive(MediaServer mediaServer, SendRtpItem sendRtpItem, Integer timeout); + void startSendRtpPassive(MediaServer mediaServer, SendRtpInfo sendRtpItem, Integer timeout); - void startSendRtp(MediaServer mediaServer, SendRtpItem sendRtpItem); - - SendRtpItem createSendRtpItem(MediaServer mediaServerItem, String addressStr, int port, String ssrc, String requesterId, String deviceId, String channelId, boolean mediaTransmissionTCP, boolean rtcp); - - SendRtpItem createSendRtpItem(MediaServer serverItem, String ip, int port, String ssrc, String platformId, - String app, String stream, String channelId, boolean tcp, boolean rtcp); + void startSendRtp(MediaServer mediaServer, SendRtpInfo sendRtpItem); MediaServer getMediaServerByAppAndStream(String app, String stream); Long updateDownloadProcess(MediaServer mediaServerItem, String app, String stream); + + StreamInfo startProxy(MediaServer mediaServer, StreamProxy streamProxy); + + void stopProxy(MediaServer mediaServer, String streamKey); + + StreamInfo getMediaByAppAndStream(String app, String stream); + + int createRTPServer(MediaServer mediaServerItem, String streamId, long ssrc, Integer port, boolean onlyAuto, boolean disableAudio, boolean reUsePort, Integer tcpMode); + } diff --git a/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java index 2e69f2334..d58161ed1 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java @@ -7,7 +7,8 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.ControllerException; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; +import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; @@ -17,24 +18,22 @@ import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerDeleteEvent; import com.genersoft.iot.vmp.media.service.IMediaNodeServerService; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; import com.genersoft.iot.vmp.media.zlm.dto.hook.OriginType; -import com.genersoft.iot.vmp.service.IInviteStreamService; import com.genersoft.iot.vmp.service.bean.MediaServerLoad; import com.genersoft.iot.vmp.service.bean.SSRCInfo; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.dao.MediaServerMapper; +import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy; import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.JsonUtil; import com.genersoft.iot.vmp.utils.redis.RedisUtil; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.event.EventListener; @@ -49,12 +48,11 @@ import java.util.*; /** * 媒体服务器节点管理 */ +@Slf4j @Service @DS("master") public class MediaServerServiceImpl implements IMediaServerService { - private final static Logger logger = LoggerFactory.getLogger(MediaServerServiceImpl.class); - @Autowired private SSRCFactory ssrcFactory; @@ -82,11 +80,6 @@ public class MediaServerServiceImpl implements IMediaServerService { @Autowired private MediaConfig mediaConfig; - @Autowired - private SendRtpPortManager sendRtpPortManager; - - - /** * 流到来的处理 */ @@ -94,7 +87,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @org.springframework.context.event.EventListener public void onApplicationEvent(MediaArrivalEvent event) { if ("rtsp".equals(event.getSchema())) { - logger.info("流变化:注册 app->{}, stream->{}", event.getApp(), event.getStream()); + log.info("流变化:注册 app->{}, stream->{}", event.getApp(), event.getStream()); addCount(event.getMediaServer().getId()); String type = OriginType.values()[event.getMediaInfo().getOriginType()].getType(); redisCatchStorage.addStream(event.getMediaServer(), type, event.getApp(), event.getStream(), event.getMediaInfo()); @@ -108,7 +101,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @EventListener public void onApplicationEvent(MediaDepartureEvent event) { if ("rtsp".equals(event.getSchema())) { - logger.info("流变化:注销, app->{}, stream->{}", event.getApp(), event.getStream()); + log.info("流变化:注销, app->{}, stream->{}", event.getApp(), event.getStream()); removeCount(event.getMediaServer().getId()); MediaInfo mediaInfo = redisCatchStorage.getStreamInfo( event.getApp(), event.getStream(), event.getMediaServer().getId()); @@ -127,7 +120,7 @@ public class MediaServerServiceImpl implements IMediaServerService { */ @Override public void updateVmServer(List mediaServerList) { - logger.info("[媒体服务节点] 缓存初始化 "); + log.info("[媒体服务节点] 缓存初始化 "); for (MediaServer mediaServer : mediaServerList) { if (ObjectUtils.isEmpty(mediaServer.getId())) { continue; @@ -150,7 +143,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public SSRCInfo openRTPServer(MediaServer mediaServer, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback, Integer port, Boolean onlyAuto, Boolean disableAudio, Boolean reUsePort, Integer tcpMode) { if (mediaServer == null || mediaServer.getId() == null) { - logger.info("[openRTPServer] 失败, mediaServer == null || mediaServer.getId() == null"); + log.info("[openRTPServer] 失败, mediaServer == null || mediaServer.getId() == null"); return null; } // 获取mediaServer可用的ssrc @@ -170,20 +163,36 @@ public class MediaServerServiceImpl implements IMediaServerService { } if (ssrcCheck && tcpMode > 0) { // 目前zlm不支持 tcp模式更新ssrc,暂时关闭ssrc校验 - logger.warn("[openRTPServer] 平台对接时下级可能自定义ssrc,但是tcp模式zlm收流目前无法更新ssrc,可能收流超时,此时请使用udp收流或者关闭ssrc校验"); + log.warn("[openRTPServer] 平台对接时下级可能自定义ssrc,但是tcp模式zlm收流目前无法更新ssrc,可能收流超时,此时请使用udp收流或者关闭ssrc校验"); } int rtpServerPort; if (mediaServer.isRtpEnable()) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[openRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[openRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return null; } rtpServerPort = mediaNodeServerService.createRTPServer(mediaServer, streamId, ssrcCheck ? Long.parseLong(ssrc) : 0, port, onlyAuto, disableAudio, reUsePort, tcpMode); } else { rtpServerPort = mediaServer.getRtpProxyPort(); } - return new SSRCInfo(rtpServerPort, ssrc, streamId); + return new SSRCInfo(rtpServerPort, ssrc, streamId, null); + } + + @Override + public int createRTPServer(MediaServer mediaServer, String streamId, long ssrc, Integer port, boolean onlyAuto, boolean disableAudio, boolean reUsePort, Integer tcpMode) { + int rtpServerPort; + if (mediaServer.isRtpEnable()) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + log.info("[openRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return 0; + } + rtpServerPort = mediaNodeServerService.createRTPServer(mediaServer, streamId, ssrc, port, onlyAuto, disableAudio, reUsePort, tcpMode); + } else { + rtpServerPort = mediaServer.getRtpProxyPort(); + } + return rtpServerPort; } @Override @@ -193,7 +202,7 @@ public class MediaServerServiceImpl implements IMediaServerService { } IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return; } mediaNodeServerService.closeRtpServer(mediaServer, streamId); @@ -207,7 +216,7 @@ public class MediaServerServiceImpl implements IMediaServerService { } IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return; } mediaNodeServerService.closeRtpServer(mediaServer, streamId, callback); @@ -224,7 +233,7 @@ public class MediaServerServiceImpl implements IMediaServerService { } IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return; } mediaNodeServerService.closeStreams(mediaServer, "rtp", streamId); @@ -237,7 +246,7 @@ public class MediaServerServiceImpl implements IMediaServerService { } IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[updateRtpServerSSRC] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[updateRtpServerSSRC] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return false; } return mediaNodeServerService.updateRtpServerSSRC(mediaServer, streamId, ssrc); @@ -260,7 +269,6 @@ public class MediaServerServiceImpl implements IMediaServerService { ssrcFactory.reset(mediaServer.getId()); } - @Override public void update(MediaServer mediaSerItem) { mediaServerMapper.update(mediaSerItem); @@ -387,16 +395,16 @@ public class MediaServerServiceImpl implements IMediaServerService { mediaServer.setHookAliveInterval(10F); } if (mediaServer.getType() == null) { - logger.info("[添加媒体节点] 失败, mediaServer的类型:为空"); + log.info("[添加媒体节点] 失败, mediaServer的类型:为空"); return; } if (mediaServerMapper.queryOne(mediaServer.getId()) != null) { - logger.info("[添加媒体节点] 失败, 媒体服务ID已存在,请修改媒体服务器配置, {}", mediaServer.getId()); + log.info("[添加媒体节点] 失败, 媒体服务ID已存在,请修改媒体服务器配置, {}", mediaServer.getId()); throw new ControllerException(ErrorCode.ERROR100.getCode(),"保存失败,媒体服务ID [ " + mediaServer.getId() + " ] 已存在,请修改媒体服务器配置"); } IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[添加媒体节点] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[添加媒体节点] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return; } @@ -457,7 +465,7 @@ public class MediaServerServiceImpl implements IMediaServerService { String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId(); Long size = redisTemplate.opsForZSet().zCard(key); if (size == null || size == 0) { - logger.info("获取负载最低的节点时无在线节点"); + log.info("获取负载最低的节点时无在线节点"); return null; } @@ -499,7 +507,7 @@ public class MediaServerServiceImpl implements IMediaServerService { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(type); if (mediaNodeServerService == null) { - logger.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", type); + log.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", type); return null; } MediaServer mediaServer = mediaNodeServerService.checkMediaServer(ip, port, secret); @@ -586,7 +594,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public boolean stopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaInfo.getType()); if (mediaNodeServerService == null) { - logger.info("[stopSendRtp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaInfo.getType()); + log.info("[stopSendRtp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaInfo.getType()); return false; } return mediaNodeServerService.stopSendRtp(mediaInfo, app, stream, ssrc); @@ -596,7 +604,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public boolean initStopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaInfo.getType()); if (mediaNodeServerService == null) { - logger.info("[stopSendRtp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaInfo.getType()); + log.info("[stopSendRtp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaInfo.getType()); return false; } return mediaNodeServerService.initStopSendRtp(mediaInfo, app, stream, ssrc); @@ -606,7 +614,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public boolean deleteRecordDirectory(MediaServer mediaServer, String app, String stream, String date, String fileName) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[stopSendRtp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[stopSendRtp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return false; } return mediaNodeServerService.deleteRecordDirectory(mediaServer, app, stream, date, fileName); @@ -616,7 +624,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public List getMediaList(MediaServer mediaServer, String app, String stream, String callId) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[getMediaList] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[getMediaList] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return new ArrayList<>(); } return mediaNodeServerService.getMediaList(mediaServer, app, stream, callId); @@ -626,7 +634,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public Boolean connectRtpServer(MediaServer mediaServer, String address, int port, String stream) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[connectRtpServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[connectRtpServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return false; } return mediaNodeServerService.connectRtpServer(mediaServer, address, port, stream); @@ -636,7 +644,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public void getSnap(MediaServer mediaServer, String streamUrl, int timeoutSec, int expireSec, String path, String fileName) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[getSnap] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[getSnap] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return; } mediaNodeServerService.getSnap(mediaServer, streamUrl, timeoutSec, expireSec, path, fileName); @@ -646,7 +654,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public MediaInfo getMediaInfo(MediaServer mediaServer, String app, String stream) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[getMediaInfo] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[getMediaInfo] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return null; } return mediaNodeServerService.getMediaInfo(mediaServer, app, stream); @@ -656,7 +664,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public Boolean pauseRtpCheck(MediaServer mediaServer, String streamKey) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[pauseRtpCheck] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[pauseRtpCheck] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return false; } return mediaNodeServerService.pauseRtpCheck(mediaServer, streamKey); @@ -666,7 +674,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public boolean resumeRtpCheck(MediaServer mediaServer, String streamKey) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[pauseRtpCheck] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[pauseRtpCheck] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return false; } return mediaNodeServerService.resumeRtpCheck(mediaServer, streamKey); @@ -676,7 +684,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public String getFfmpegCmd(MediaServer mediaServer, String cmdKey) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[getFfmpegCmd] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[getFfmpegCmd] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return null; } return mediaNodeServerService.getFfmpegCmd(mediaServer, cmdKey); @@ -686,7 +694,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public void closeStreams(MediaServer mediaServer, String app, String stream) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[closeStreams] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[closeStreams] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return; } mediaNodeServerService.closeStreams(mediaServer, app, stream); @@ -696,27 +704,28 @@ public class MediaServerServiceImpl implements IMediaServerService { public WVPResult addFFmpegSource(MediaServer mediaServer, String srcUrl, String dstUrl, int timeoutMs, boolean enableAudio, boolean enableMp4, String ffmpegCmdKey) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[addFFmpegSource] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[addFFmpegSource] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return WVPResult.fail(ErrorCode.ERROR400); } return mediaNodeServerService.addFFmpegSource(mediaServer, srcUrl, dstUrl, timeoutMs, enableAudio, enableMp4, ffmpegCmdKey); } @Override - public WVPResult addStreamProxy(MediaServer mediaServer, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType) { + public WVPResult addStreamProxy(MediaServer mediaServer, String app, String stream, String url, + boolean enableAudio, boolean enableMp4, String rtpType, Integer timeout) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[addStreamProxy] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[addStreamProxy] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return WVPResult.fail(ErrorCode.ERROR400); } - return mediaNodeServerService.addStreamProxy(mediaServer, app, stream, url, enableAudio, enableMp4, rtpType); + return mediaNodeServerService.addStreamProxy(mediaServer, app, stream, url, enableAudio, enableMp4, rtpType, timeout); } @Override public Boolean delFFmpegSource(MediaServer mediaServer, String streamKey) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[delFFmpegSource] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[delFFmpegSource] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return false; } return mediaNodeServerService.delFFmpegSource(mediaServer, streamKey); @@ -726,7 +735,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public Boolean delStreamProxy(MediaServer mediaServerItem, String streamKey) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServerItem.getType()); if (mediaNodeServerService == null) { - logger.info("[delStreamProxy] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServerItem.getType()); + log.info("[delStreamProxy] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServerItem.getType()); return false; } return mediaNodeServerService.delStreamProxy(mediaServerItem, streamKey); @@ -736,7 +745,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public Map getFFmpegCMDs(MediaServer mediaServer) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[getFFmpegCMDs] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[getFFmpegCMDs] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return new HashMap<>(); } return mediaNodeServerService.getFFmpegCMDs(mediaServer); @@ -749,13 +758,12 @@ public class MediaServerServiceImpl implements IMediaServerService { @Override public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr, boolean authority) { - StreamInfo streamInfo = null; if (mediaServerId == null) { mediaServerId = mediaConfig.getId(); } MediaServer mediaInfo = getOne(mediaServerId); if (mediaInfo == null) { - return null; + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到使用的媒体节点"); } String calld = null; StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream); @@ -763,7 +771,7 @@ public class MediaServerServiceImpl implements IMediaServerService { calld = streamAuthorityInfo.getCallId(); } List streamInfoList = getMediaList(mediaInfo, app, stream, calld); - if (streamInfoList.isEmpty()) { + if (streamInfoList == null || streamInfoList.isEmpty()) { return null; }else { return streamInfoList.get(0); @@ -787,7 +795,13 @@ public class MediaServerServiceImpl implements IMediaServerService { } streamInfoResult.setIp(addr); - streamInfoResult.setMediaServerId(mediaServer.getId()); + if (mediaInfo != null) { + streamInfoResult.setServerId(mediaInfo.getServerId()); + }else { + streamInfoResult.setServerId(userSetting.getServerId()); + } + + streamInfoResult.setMediaServer(mediaServer); String callIdParam = ObjectUtils.isEmpty(callId)?"":"?callId=" + callId; streamInfoResult.setRtmp(addr, mediaServer.getRtmpPort(),mediaServer.getRtmpSSlPort(), app, stream, callIdParam); streamInfoResult.setRtsp(addr, mediaServer.getRtspPort(),mediaServer.getRtspSSLPort(), app, stream, callIdParam); @@ -820,13 +834,13 @@ public class MediaServerServiceImpl implements IMediaServerService { } @Override - public Boolean isStreamReady(MediaServer mediaServer, String rtp, String streamId) { + public Boolean isStreamReady(MediaServer mediaServer, String app, String streamId) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[isStreamReady] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[isStreamReady] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); return false; } - MediaInfo mediaInfo = mediaNodeServerService.getMediaInfo(mediaServer, rtp, streamId); + MediaInfo mediaInfo = mediaNodeServerService.getMediaInfo(mediaServer, app, streamId); return mediaInfo != null; } @@ -834,69 +848,27 @@ public class MediaServerServiceImpl implements IMediaServerService { public Integer startSendRtpPassive(MediaServer mediaServer, SendRtpItem sendRtpItem, Integer timeout) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[startSendRtpPassive] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[startSendRtpPassive] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类"); } return mediaNodeServerService.startSendRtpPassive(mediaServer, sendRtpItem, timeout); } @Override - public void startSendRtp(MediaServer mediaServer, SendRtpItem sendRtpItem) { + public void startSendRtp(MediaServer mediaServer, SendRtpInfo sendRtpItem) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[startSendRtpStream] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[startSendRtpStream] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类"); } - logger.info("[开始推流] rtp/{}, 目标={}:{},SSRC={}, RTCP={}", sendRtpItem.getStream(), + sendRtpItem.setRtcp(true); + + log.info("[开始推流] {}/{}, 目标={}:{},SSRC={}, RTCP={}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isRtcp()); mediaNodeServerService.startSendRtpStream(mediaServer, sendRtpItem); } - @Override - public SendRtpItem createSendRtpItem(MediaServer mediaServer, String ip, int port, String ssrc, String requesterId, String deviceId, String channelId, boolean isTcp, boolean rtcp) { - int localPort = sendRtpPortManager.getNextPort(mediaServer); - if (localPort == 0) { - return null; - } - SendRtpItem sendRtpItem = new SendRtpItem(); - sendRtpItem.setIp(ip); - sendRtpItem.setPort(port); - sendRtpItem.setSsrc(ssrc); - sendRtpItem.setPlatformId(deviceId); - sendRtpItem.setDeviceId(deviceId); - sendRtpItem.setChannelId(channelId); - sendRtpItem.setTcp(isTcp); - sendRtpItem.setRtcp(rtcp); - sendRtpItem.setApp("rtp"); - sendRtpItem.setLocalPort(localPort); - sendRtpItem.setServerId(userSetting.getServerId()); - sendRtpItem.setMediaServerId(mediaServer.getId()); - return sendRtpItem; - } - @Override - public SendRtpItem createSendRtpItem(MediaServer serverItem, String ip, int port, String ssrc, String platformId, - String app, String stream, String channelId, boolean tcp, boolean rtcp){ - - int localPort = sendRtpPortManager.getNextPort(serverItem); - if (localPort == 0) { - return null; - } - SendRtpItem sendRtpItem = new SendRtpItem(); - sendRtpItem.setIp(ip); - sendRtpItem.setPort(port); - sendRtpItem.setSsrc(ssrc); - sendRtpItem.setApp(app); - sendRtpItem.setStream(stream); - sendRtpItem.setPlatformId(platformId); - sendRtpItem.setChannelId(channelId); - sendRtpItem.setTcp(tcp); - sendRtpItem.setLocalPort(localPort); - sendRtpItem.setServerId(userSetting.getServerId()); - sendRtpItem.setMediaServerId(serverItem.getId()); - sendRtpItem.setRtcp(rtcp); - return sendRtpItem; - } @Override public MediaServer getMediaServerByAppAndStream(String app, String stream) { @@ -910,13 +882,46 @@ public class MediaServerServiceImpl implements IMediaServerService { return null; } + @Override + public StreamInfo getMediaByAppAndStream(String app, String stream) { + + List mediaServerList = getAll(); + for (MediaServer mediaServer : mediaServerList) { + MediaInfo mediaInfo = getMediaInfo(mediaServer, app, stream); + if (mediaInfo != null) { + return getStreamInfoByAppAndStream(mediaServer, app, stream, mediaInfo, mediaInfo.getCallId()); + } + } + return null; + } + @Override public Long updateDownloadProcess(MediaServer mediaServer, String app, String stream) { IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); if (mediaNodeServerService == null) { - logger.info("[updateDownloadProcess] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + log.info("[updateDownloadProcess] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类"); } return mediaNodeServerService.updateDownloadProcess(mediaServer, app, stream); } + + @Override + public StreamInfo startProxy(MediaServer mediaServer, StreamProxy streamProxy) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + log.info("[startProxy] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类"); + } + return mediaNodeServerService.startProxy(mediaServer, streamProxy); + } + + @Override + public void stopProxy(MediaServer mediaServer, String streamKey) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + log.info("[stopProxy] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类"); + } + mediaNodeServerService.stopProxy(mediaServer, streamKey); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java index 6f6d5ea8d..6a2b7d247 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java @@ -4,11 +4,10 @@ import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.utils.SSLSocketClientUtil; +import lombok.extern.slf4j.Slf4j; 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; @@ -22,12 +21,10 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; +@Slf4j @Component public class AssistRESTfulUtils { - private final static Logger logger = LoggerFactory.getLogger(AssistRESTfulUtils.class); - - private OkHttpClient client; @@ -51,9 +48,9 @@ public class AssistRESTfulUtils { httpClientBuilder.readTimeout(readTimeOut,TimeUnit.SECONDS); // 设置连接池 httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES)); - if (logger.isDebugEnabled()) { + if (log.isDebugEnabled()) { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> { - logger.debug("http请求参数:" + message); + log.debug("http请求参数:" + message); }); logging.setLevel(HttpLoggingInterceptor.Level.BASIC); // OkHttp進行添加攔截器loggingInterceptor @@ -77,7 +74,7 @@ public class AssistRESTfulUtils { return null; } if (mediaServerItem.getRecordAssistPort() <= 0) { - logger.warn("未启用Assist服务"); + log.warn("未启用Assist服务"); return null; } StringBuilder stringBuffer = new StringBuilder(); @@ -99,7 +96,7 @@ public class AssistRESTfulUtils { } String url = stringBuffer.toString(); - logger.info("[访问assist]: {}", url); + log.info("[访问assist]: {}", url); Request request = new Request.Builder() .get() .url(url) @@ -118,10 +115,10 @@ public class AssistRESTfulUtils { Objects.requireNonNull(response.body()).close(); } } catch (ConnectException e) { - logger.error(String.format("连接Assist失败: %s, %s", e.getCause().getMessage(), e.getMessage())); - logger.info("请检查media配置并确认Assist已启动..."); + log.error(String.format("连接Assist失败: %s, %s", e.getCause().getMessage(), e.getMessage())); + log.info("请检查media配置并确认Assist已启动..."); }catch (IOException e) { - logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + log.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); } }else { client.newCall(request).enqueue(new Callback(){ @@ -133,7 +130,7 @@ public class AssistRESTfulUtils { String responseStr = Objects.requireNonNull(response.body()).string(); callback.run(JSON.parseObject(responseStr)); } catch (IOException e) { - logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + log.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); } }else { @@ -144,8 +141,8 @@ public class AssistRESTfulUtils { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { - logger.error(String.format("连接Assist失败: %s, %s", e.getCause().getMessage(), e.getMessage())); - logger.info("请检查media配置并确认Assist已启动..."); + log.error(String.format("连接Assist失败: %s, %s", e.getCause().getMessage(), e.getMessage())); + log.info("请检查media配置并确认Assist已启动..."); } }); } @@ -163,7 +160,7 @@ public class AssistRESTfulUtils { if (mediaServerItem == null) { return null; } - logger.info("[访问assist]: {}, 参数: {}", url, param); + log.info("[访问assist]: {}, 参数: {}", url, param); JSONObject responseJSON = new JSONObject(); //-2自定义流媒体 调用错误码 responseJSON.put("code",-2); @@ -190,19 +187,19 @@ public class AssistRESTfulUtils { Objects.requireNonNull(response.body()).close(); } }catch (IOException e) { - logger.error(String.format("[ %s ]ASSIST请求失败: %s", url, e.getMessage())); + log.error(String.format("[ %s ]ASSIST请求失败: %s", url, e.getMessage())); if(e instanceof SocketTimeoutException){ //读取超时超时异常 - logger.error(String.format("读取ASSIST数据失败: %s, %s", url, e.getMessage())); + log.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())); + log.error(String.format("连接ASSIST失败: %s, %s", url, e.getMessage())); } }catch (Exception e){ - logger.error(String.format("访问ASSIST失败: %s, %s", url, e.getMessage())); + log.error(String.format("访问ASSIST失败: %s, %s", url, e.getMessage())); } }else { client.newCall(request).enqueue(new Callback(){ @@ -214,7 +211,7 @@ public class AssistRESTfulUtils { String responseStr = Objects.requireNonNull(response.body()).string(); callback.run(JSON.parseObject(responseStr)); } catch (IOException e) { - logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + log.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); } }else { @@ -225,15 +222,15 @@ public class AssistRESTfulUtils { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { - logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage())); + log.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())); + log.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())); + log.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage())); } } }); diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/SendRtpPortManager.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/SendRtpPortManager.java index f799a9301..b756b9ece 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/SendRtpPortManager.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/SendRtpPortManager.java @@ -2,12 +2,11 @@ package com.genersoft.iot.vmp.media.zlm; import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.utils.redis.RedisUtil; +import lombok.extern.slf4j.Slf4j; 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.data.redis.core.RedisTemplate; import org.springframework.data.redis.support.atomic.RedisAtomicInteger; @@ -17,11 +16,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +@Slf4j @Component public class SendRtpPortManager { - private final static Logger logger = LoggerFactory.getLogger(SendRtpPortManager.class); - @Autowired private UserSetting userSetting; @@ -32,17 +30,17 @@ public class SendRtpPortManager { public synchronized int getNextPort(MediaServer mediaServer) { if (mediaServer == null) { - logger.warn("[发送端口管理] 参数错误,mediaServer为NULL"); + log.warn("[发送端口管理] 参数错误,mediaServer为NULL"); return -1; } String sendIndexKey = KEY + userSetting.getServerId() + "_" + mediaServer.getId(); - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + String key = VideoManagerConstants.SEND_RTP_INFO + userSetting.getServerId() + "_*"; List queryResult = RedisUtil.scan(redisTemplate, key); - Map sendRtpItemMap = new HashMap<>(); + Map sendRtpItemMap = new HashMap<>(); for (Object o : queryResult) { - SendRtpItem sendRtpItem = (SendRtpItem) redisTemplate.opsForValue().get(o); + SendRtpInfo sendRtpItem = (SendRtpInfo) redisTemplate.opsForValue().get(o); if (sendRtpItem != null) { sendRtpItemMap.put(sendRtpItem.getLocalPort(), sendRtpItem); } @@ -53,12 +51,12 @@ public class SendRtpPortManager { if (sendRtpPortRange != null) { String[] portArray = sendRtpPortRange.split(","); if (portArray.length != 2 || !NumberUtils.isParsable(portArray[0]) || !NumberUtils.isParsable(portArray[1])) { - logger.warn("{}发送端口配置格式错误,自动使用50000-60000作为端口范围", mediaServer.getId()); + log.warn("{}发送端口配置格式错误,自动使用50000-60000作为端口范围", mediaServer.getId()); startPort = 50000; endPort = 60000; }else { if ( Integer.parseInt(portArray[1]) - Integer.parseInt(portArray[0]) < 1) { - logger.warn("{}发送端口配置错误,结束端口至少比开始端口大一,自动使用50000-60000作为端口范围", mediaServer.getId()); + log.warn("{}发送端口配置错误,结束端口至少比开始端口大一,自动使用50000-60000作为端口范围", mediaServer.getId()); startPort = 50000; endPort = 60000; }else { @@ -67,12 +65,12 @@ public class SendRtpPortManager { } } }else { - logger.warn("{}未设置发送端口默认值,自动使用50000-60000作为端口范围", mediaServer.getId()); + log.warn("{}未设置发送端口默认值,自动使用50000-60000作为端口范围", mediaServer.getId()); startPort = 50000; endPort = 60000; } if (redisTemplate == null || redisTemplate.getConnectionFactory() == null) { - logger.warn("{}获取redis连接信息失败", mediaServer.getId()); + log.warn("{}获取redis连接信息失败", mediaServer.getId()); return -1; } // RedisAtomicInteger redisAtomicInteger = new RedisAtomicInteger(sendIndexKey , redisTemplate.getConnectionFactory()); @@ -82,7 +80,7 @@ public class SendRtpPortManager { return getSendPort(startPort, endPort, sendIndexKey, sendRtpItemMap); } - private synchronized int getSendPort(int startPort, int endPort, String sendIndexKey, Map sendRtpItemMap){ + private synchronized int getSendPort(int startPort, int endPort, String sendIndexKey, Map sendRtpItemMap){ // TODO 这里改为只取偶数端口 RedisAtomicInteger redisAtomicInteger = new RedisAtomicInteger(sendIndexKey , redisTemplate.getConnectionFactory()); if (redisAtomicInteger.get() < startPort) { diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java index 636b4bf1c..fc0b60ec6 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java @@ -3,16 +3,8 @@ package com.genersoft.iot.vmp.media.zlm; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; -import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; -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.ISIPCommanderForPlatform; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.bean.ResultForOnPublish; -import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; import com.genersoft.iot.vmp.media.event.media.*; import com.genersoft.iot.vmp.media.event.mediaServer.MediaSendRtpStoppedEvent; import com.genersoft.iot.vmp.media.service.IMediaServerService; @@ -20,22 +12,18 @@ import com.genersoft.iot.vmp.media.zlm.dto.ZLMServerConfig; import com.genersoft.iot.vmp.media.zlm.dto.hook.*; import com.genersoft.iot.vmp.media.zlm.event.HookZlmServerKeepaliveEvent; import com.genersoft.iot.vmp.media.zlm.event.HookZlmServerStartEvent; -import com.genersoft.iot.vmp.service.*; -import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.service.IMediaService; import com.genersoft.iot.vmp.utils.MediaServerUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; import java.util.Map; /** @@ -43,85 +31,23 @@ import java.util.Map; * @author: swwheihei * @date: 2020年5月8日 上午10:46:48 */ +@Slf4j @RestController @RequestMapping("/index/hook") public class ZLMHttpHookListener { - private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class); - - @Autowired - private SIPCommander cmder; - - @Autowired - private ISIPCommanderForPlatform commanderFroPlatform; - - @Autowired - private AudioBroadcastManager audioBroadcastManager; - - @Autowired - private IPlayService playService; - - @Autowired - private IVideoManagerStorage storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - - @Autowired - private IRedisRpcService redisRpcService; - - @Autowired - private IInviteStreamService inviteStreamService; - - @Autowired - private IDeviceService deviceService; - @Autowired private IMediaServerService mediaServerService; - @Autowired - private IStreamProxyService streamProxyService; - - @Autowired - private DeferredResultHolder resultHolder; - @Autowired private IMediaService mediaService; - @Autowired - private EventPublisher eventPublisher; - - @Autowired - private HookSubscribe subscribe; - @Autowired private UserSetting userSetting; - @Autowired - private IUserService userService; - - @Autowired - private ICloudRecordService cloudRecordService; - - @Autowired - private VideoStreamSessionManager sessionManager; - - @Autowired - private SSRCFactory ssrcFactory; - - @Qualifier("taskExecutor") - @Autowired - private ThreadPoolTaskExecutor taskExecutor; - - @Autowired - private RedisTemplate redisTemplate; - @Autowired private ApplicationEventPublisher applicationEventPublisher; - @Autowired - private IStreamPushService streamPushService; /** * 服务器定时上报时间,上报间隔可配置,默认10s上报一次 @@ -137,7 +63,7 @@ public class ZLMHttpHookListener { applicationEventPublisher.publishEvent(event); } }catch (Exception e) { - logger.info("[ZLM-HOOK-心跳] 发送通知失败 ", e); + log.info("[ZLM-HOOK-心跳] 发送通知失败 ", e); } return HookResult.SUCCESS(); } @@ -153,10 +79,12 @@ public class ZLMHttpHookListener { // 对于播放流进行鉴权 boolean authenticateResult = mediaService.authenticatePlay(param.getApp(), param.getStream(), paramMap.get("callId")); if (!authenticateResult) { - logger.info("[ZLM HOOK] 播放鉴权 失败:{}->{}", param.getMediaServerId(), param); + log.info("[ZLM HOOK] 播放鉴权 失败:{}->{}", param.getMediaServerId(), param); return new HookResult(401, "Unauthorized"); } - logger.info("[ZLM HOOK] 播放鉴权成功:{}->{}", param.getMediaServerId(), param); + if (log.isDebugEnabled()){ + log.debug("[ZLM HOOK] 播放鉴权成功:{}->{}", param.getMediaServerId(), param); + } return HookResult.SUCCESS(); } @@ -169,7 +97,7 @@ public class ZLMHttpHookListener { JSONObject json = (JSONObject) JSON.toJSON(param); - logger.info("[ZLM HOOK]推流鉴权:{}->{}", param.getMediaServerId(), param); + log.info("[ZLM HOOK]推流鉴权:{}->{}", param.getMediaServerId(), param); // TODO 加快处理速度 String mediaServerId = json.getString("mediaServerId"); @@ -181,11 +109,11 @@ public class ZLMHttpHookListener { ResultForOnPublish resultForOnPublish = mediaService.authenticatePublish(mediaServer, param.getApp(), param.getStream(), param.getParams()); if (resultForOnPublish != null) { HookResultForOnPublish successResult = HookResultForOnPublish.getInstance(resultForOnPublish); - logger.info("[ZLM HOOK]推流鉴权 响应:{}->{}->>>>{}", param.getMediaServerId(), param, successResult); + log.info("[ZLM HOOK]推流鉴权 响应:{}->{}->>>>{}", param.getMediaServerId(), param, successResult); return successResult; }else { HookResultForOnPublish fail = HookResultForOnPublish.Fail(); - logger.info("[ZLM HOOK]推流鉴权 响应:{}->{}->>>>{}", param.getMediaServerId(), param, fail); + log.info("[ZLM HOOK]推流鉴权 响应:{}->{}->>>>{}", param.getMediaServerId(), param, fail); return fail; } } @@ -217,11 +145,23 @@ public class ZLMHttpHookListener { } if (param.getSchema().equalsIgnoreCase("rtsp")) { if (param.isRegist()) { - logger.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); - MediaArrivalEvent mediaArrivalEvent = MediaArrivalEvent.getInstance(this, param, mediaServer); + log.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); + String queryParams = param.getParams(); + if (queryParams == null) { + try { + URL url = new URL("http" + param.getOriginUrl().substring(4)); + queryParams = url.getQuery(); + }catch (MalformedURLException ignored) {} + } + if (queryParams != null) { + param.setParamMap(MediaServerUtils.urlParamToMap(queryParams)); + }else { + param.setParamMap(new HashMap<>()); + } + MediaArrivalEvent mediaArrivalEvent = MediaArrivalEvent.getInstance(this, param, mediaServer, userSetting.getServerId()); applicationEventPublisher.publishEvent(mediaArrivalEvent); } else { - logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); + log.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); MediaDepartureEvent mediaDepartureEvent = MediaDepartureEvent.getInstance(this, param, mediaServer); applicationEventPublisher.publishEvent(mediaDepartureEvent); } @@ -237,7 +177,7 @@ public class ZLMHttpHookListener { @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8") public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param) { - logger.info("[ZLM HOOK]流无人观看:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), + log.info("[ZLM HOOK]流无人观看:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); MediaServer mediaInfo = mediaServerService.getOne(param.getMediaServerId()); @@ -265,7 +205,7 @@ public class ZLMHttpHookListener { @ResponseBody @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8") public HookResult onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param) { - logger.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); + log.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); MediaServer mediaServer = mediaServerService.getOne(param.getMediaServerId()); @@ -287,7 +227,7 @@ public class ZLMHttpHookListener { jsonObject.put("ip", request.getRemoteAddr()); ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject); zlmServerConfig.setIp(request.getRemoteAddr()); - logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId()); + log.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId()); try { HookZlmServerStartEvent event = new HookZlmServerStartEvent(this); MediaServer mediaServerItem = mediaServerService.getOne(zlmServerConfig.getMediaServerId()); @@ -296,7 +236,7 @@ public class ZLMHttpHookListener { applicationEventPublisher.publishEvent(event); } }catch (Exception e) { - logger.info("[ZLM-HOOK-ZLM启动] 发送通知失败 ", e); + log.info("[ZLM-HOOK-ZLM启动] 发送通知失败 ", e); } return HookResult.SUCCESS(); @@ -309,7 +249,7 @@ public class ZLMHttpHookListener { @PostMapping(value = "/on_send_rtp_stopped", produces = "application/json;charset=UTF-8") public HookResult onSendRtpStopped(HttpServletRequest request, @RequestBody OnSendRtpStoppedHookParam param) { - logger.info("[ZLM HOOK] rtp发送关闭:{}->{}/{}", param.getMediaServerId(), param.getApp(), param.getStream()); + log.info("[ZLM HOOK] rtp发送关闭:{}->{}/{}", param.getMediaServerId(), param.getApp(), param.getStream()); // 查找对应的上级推流,发送停止 if (!"rtp".equals(param.getApp())) { @@ -323,7 +263,7 @@ public class ZLMHttpHookListener { applicationEventPublisher.publishEvent(event); } }catch (Exception e) { - logger.info("[ZLM-HOOK-rtp发送关闭] 发送通知失败 ", e); + log.info("[ZLM-HOOK-rtp发送关闭] 发送通知失败 ", e); } return HookResult.SUCCESS(); @@ -336,7 +276,7 @@ public class ZLMHttpHookListener { @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8") public HookResult onRtpServerTimeout(@RequestBody OnRtpServerTimeoutHookParam param) { - logger.info("[ZLM HOOK] rtpServer收流超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc()); + log.info("[ZLM HOOK] rtpServer收流超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc()); try { MediaRtpServerTimeoutEvent event = new MediaRtpServerTimeoutEvent(this); @@ -347,7 +287,7 @@ public class ZLMHttpHookListener { applicationEventPublisher.publishEvent(event); } }catch (Exception e) { - logger.info("[ZLM-HOOK-rtpServer收流超时] 发送通知失败 ", e); + log.info("[ZLM-HOOK-rtpServer收流超时] 发送通知失败 ", e); } return HookResult.SUCCESS(); @@ -359,7 +299,7 @@ public class ZLMHttpHookListener { @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()); + log.info("[ZLM HOOK] 录像完成事件:{}->{}", param.getMediaServerId(), param.getFile_path()); try { MediaServer mediaServerItem = mediaServerService.getOne(param.getMediaServerId()); @@ -369,7 +309,7 @@ public class ZLMHttpHookListener { applicationEventPublisher.publishEvent(event); } }catch (Exception e) { - logger.info("[ZLM-HOOK-rtpServer收流超时] 发送通知失败 ", e); + log.info("[ZLM-HOOK-rtpServer收流超时] 发送通知失败 ", e); } return HookResult.SUCCESS(); diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java index 26e27677b..4ae55881b 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java @@ -5,16 +5,17 @@ import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.CommonCallback; import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.ControllerException; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.service.IMediaNodeServerService; import com.genersoft.iot.vmp.media.zlm.dto.ZLMServerConfig; +import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; @@ -24,10 +25,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +@Slf4j @Service("zlm") public class ZLMMediaNodeServerService implements IMediaNodeServerService { - private final static Logger logger = LoggerFactory.getLogger(ZLMMediaNodeServerService.class); @Autowired private ZLMRESTfulUtils zlmresTfulUtils; @@ -35,6 +36,9 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { @Autowired private ZLMServerFactory zlmServerFactory; + @Autowired + private UserSetting userSetting; + @Override public int createRTPServer(MediaServer mediaServer, String streamId, long ssrc, Integer port, Boolean onlyAuto, Boolean disableAudio, Boolean reUsePort, Integer tcpMode) { return zlmServerFactory.createRTPServer(mediaServer, streamId, ssrc, port, onlyAuto, disableAudio, reUsePort, tcpMode); @@ -132,10 +136,7 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { param.put("ssrc", ssrc); } JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaInfo, param); - if (jsonObject == null || jsonObject.getInteger("code") != 0 ) { - logger.error("停止发流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param)); - throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg")); - } + log.info("停止发流结果: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param)); return true; } @@ -151,7 +152,7 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { } JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaInfo, param); if (jsonObject == null || jsonObject.getInteger("code") != 0 ) { - logger.error("停止发流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param)); + log.error("停止发流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param)); return false; } return true; @@ -159,13 +160,13 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { @Override public boolean deleteRecordDirectory(MediaServer mediaServer, String app, String stream, String date, String fileName) { - logger.info("[zlm-deleteRecordDirectory] 删除磁盘文件, server: {} {}:{}->{}/{}", mediaServer.getId(), app, stream, date, fileName); + log.info("[zlm-deleteRecordDirectory] 删除磁盘文件, server: {} {}:{}->{}/{}", mediaServer.getId(), app, stream, date, fileName); JSONObject jsonObject = zlmresTfulUtils.deleteRecordDirectory(mediaServer, app, stream, date, fileName); if (jsonObject.getInteger("code") == 0) { return true; }else { - logger.info("[zlm-deleteRecordDirectory] 删除磁盘文件错误, server: {} {}:{}->{}/{}, 结果: {}", mediaServer.getId(), app, stream, date, fileName, jsonObject); + log.info("[zlm-deleteRecordDirectory] 删除磁盘文件错误, server: {} {}:{}->{}/{}, 结果: {}", mediaServer.getId(), app, stream, date, fileName, jsonObject); return false; } } @@ -181,7 +182,7 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { return null; } JSONObject mediaJSON = data.getJSONObject(0); - MediaInfo mediaInfo = MediaInfo.getInstance(mediaJSON, mediaServer); + MediaInfo mediaInfo = MediaInfo.getInstance(mediaJSON, mediaServer, userSetting.getServerId()); StreamInfo streamInfo = getStreamInfoByAppAndStream(mediaServer, app, stream, mediaInfo, callId, true); if (streamInfo != null) { streamInfoList.add(streamInfo); @@ -193,11 +194,12 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { public StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServer, String app, String stream, MediaInfo mediaInfo, String callId, boolean isPlay) { StreamInfo streamInfoResult = new StreamInfo(); + streamInfoResult.setServerId(userSetting.getServerId()); streamInfoResult.setStream(stream); streamInfoResult.setApp(app); String addr = mediaServer.getStreamIp(); streamInfoResult.setIp(addr); - streamInfoResult.setMediaServerId(mediaServer.getId()); + streamInfoResult.setMediaServer(mediaServer); String callIdParam = ObjectUtils.isEmpty(callId)?"":"?callId=" + callId; streamInfoResult.setRtmp(addr, mediaServer.getRtmpPort(),mediaServer.getRtmpSSlPort(), app, stream, callIdParam); streamInfoResult.setRtsp(addr, mediaServer.getRtspPort(),mediaServer.getRtspSSLPort(), app, stream, callIdParam); @@ -210,14 +212,16 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { streamInfoResult.setRtc(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam, isPlay); streamInfoResult.setMediaInfo(mediaInfo); - streamInfoResult.setOriginType(mediaInfo.getOriginType()); + if (mediaInfo != null) { + streamInfoResult.setOriginType(mediaInfo.getOriginType()); + } return streamInfoResult; } @Override public Boolean connectRtpServer(MediaServer mediaServer, String address, int port, String stream) { JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServer, address, port, stream); - logger.info("[TCP主动连接对方] 结果: {}", jsonObject); + log.info("[TCP主动连接对方] 结果: {}", jsonObject); return jsonObject.getInteger("code") == 0; } @@ -232,7 +236,7 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { if (jsonObject.getInteger("code") != 0) { return null; } - return MediaInfo.getInstance(jsonObject, mediaServer); + return MediaInfo.getInstance(jsonObject, mediaServer, userSetting.getServerId()); } @Override @@ -251,7 +255,7 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { public String getFfmpegCmd(MediaServer mediaServer, String cmdKey) { JSONObject jsonObject = zlmresTfulUtils.getMediaServerConfig(mediaServer); if (jsonObject.getInteger("code") != 0) { - logger.warn("[getFfmpegCmd] 获取流媒体配置失败"); + log.warn("[getFfmpegCmd] 获取流媒体配置失败"); throw new ControllerException(ErrorCode.ERROR100.getCode(), "获取流媒体配置失败"); } JSONArray dataArray = jsonObject.getJSONArray("data"); @@ -266,7 +270,7 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { public WVPResult addFFmpegSource(MediaServer mediaServer, String srcUrl, String dstUrl, int timeoutMs, boolean enableAudio, boolean enableMp4, String ffmpegCmdKey) { JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(mediaServer, srcUrl, dstUrl, timeoutMs, enableAudio, enableMp4, ffmpegCmdKey); if (jsonObject.getInteger("code") != 0) { - logger.warn("[getFfmpegCmd] 添加FFMPEG代理失败"); + log.warn("[getFfmpegCmd] 添加FFMPEG代理失败"); return WVPResult.fail(ErrorCode.ERROR100.getCode(), "添加FFMPEG代理失败"); }else { JSONObject data = jsonObject.getJSONObject("data"); @@ -279,8 +283,9 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { } @Override - public WVPResult addStreamProxy(MediaServer mediaServer, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType) { - JSONObject jsonObject = zlmresTfulUtils.addStreamProxy(mediaServer, app, stream, url, enableAudio, enableMp4, rtpType); + public WVPResult addStreamProxy(MediaServer mediaServer, String app, String stream, String url, + boolean enableAudio, boolean enableMp4, String rtpType, Integer timeout) { + JSONObject jsonObject = zlmresTfulUtils.addStreamProxy(mediaServer, app, stream, url, enableAudio, enableMp4, rtpType, timeout); if (jsonObject.getInteger("code") != 0) { return WVPResult.fail(ErrorCode.ERROR100.getCode(), "添加代理失败"); }else { @@ -323,7 +328,7 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { } @Override - public Integer startSendRtpPassive(MediaServer mediaServer, SendRtpItem sendRtpItem, Integer timeout) { + public Integer startSendRtpPassive(MediaServer mediaServer, SendRtpInfo sendRtpItem, Integer timeout) { Map param = new HashMap<>(12); param.put("vhost","__defaultVhost__"); param.put("app", sendRtpItem.getApp()); @@ -349,17 +354,17 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { JSONObject jsonObject = zlmServerFactory.startSendRtpPassive(mediaServer, param, null); if (jsonObject == null || jsonObject.getInteger("code") != 0 ) { - logger.error("启动监听TCP被动推流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param)); + log.error("启动监听TCP被动推流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param)); throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg")); } - logger.info("调用ZLM-TCP被动推流接口, 结果: {}", jsonObject); - logger.info("启动监听TCP被动推流成功[ {}/{} ],{}->{}:{}, " , sendRtpItem.getApp(), sendRtpItem.getStream(), + log.info("调用ZLM-TCP被动推流接口, 结果: {}", jsonObject); + log.info("启动监听TCP被动推流成功[ {}/{} ],{}->{}:{}, " , sendRtpItem.getApp(), sendRtpItem.getStream(), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port")); return jsonObject.getInteger("local_port"); } @Override - public void startSendRtpStream(MediaServer mediaServer, SendRtpItem sendRtpItem) { + public void startSendRtpStream(MediaServer mediaServer, SendRtpInfo sendRtpItem) { Map param = new HashMap<>(12); param.put("vhost", "__defaultVhost__"); param.put("app", sendRtpItem.getApp()); @@ -372,7 +377,7 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { param.put("is_udp", sendRtpItem.isTcp() ? "0" : "1"); if (!sendRtpItem.isTcp()) { // udp模式下开启rtcp保活 - param.put("udp_rtcp_timeout", sendRtpItem.isRtcp() ? "1" : "0"); + param.put("udp_rtcp_timeout", sendRtpItem.isRtcp() ? "500" : "0"); } param.put("dst_url", sendRtpItem.getIp()); param.put("dst_port", sendRtpItem.getPort()); @@ -380,15 +385,114 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { if (jsonObject == null || jsonObject.getInteger("code") != 0 ) { throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg")); } + log.info("[推流结果]:{} ,参数: {}",jsonObject, JSONObject.toJSONString(param)); } @Override public Long updateDownloadProcess(MediaServer mediaServer, String app, String stream) { MediaInfo mediaInfo = getMediaInfo(mediaServer, app, stream); if (mediaInfo == null) { - logger.warn("[获取下载进度] 查询进度失败, 节点Id: {}, {}/{}", mediaServer.getId(), app, stream); + log.warn("[获取下载进度] 查询进度失败, 节点Id: {}, {}/{}", mediaServer.getId(), app, stream); return null; } return mediaInfo.getDuration(); } + + @Override + public StreamInfo startProxy(MediaServer mediaServer, StreamProxy streamProxy) { + String dstUrl; + if ("ffmpeg".equalsIgnoreCase(streamProxy.getType())) { + + String ffmpegCmd = getFfmpegCmd(mediaServer, streamProxy.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中获取到输出格式"); + } + int port; + String schemaForUri; + if (schema.equalsIgnoreCase("rtsp")) { + port = mediaServer.getRtspPort(); + schemaForUri = schema; + }else if (schema.equalsIgnoreCase("flv")) { + port = mediaServer.getRtmpPort(); + schemaForUri = schema; + }else { + port = mediaServer.getRtmpPort(); + schemaForUri = schema; + } + + dstUrl = String.format("%s://%s:%s/%s/%s", schemaForUri, "127.0.0.1", port, streamProxy.getApp(), + streamProxy.getStream()); + }else { + dstUrl = String.format("rtsp://%s:%s/%s/%s", "127.0.0.1", mediaServer.getRtspPort(), streamProxy.getApp(), + streamProxy.getStream()); + } + MediaInfo mediaInfo = getMediaInfo(mediaServer, streamProxy.getApp(), streamProxy.getStream()); + if (mediaInfo != null) { + if (mediaInfo.getOriginUrl().equals(streamProxy.getSrcUrl())) { + log.info("[启动拉流代理] 已存在, 直接返回, app: {}, stream: {}", mediaInfo.getApp(), streamProxy.getStream()); + return getStreamInfoByAppAndStream(mediaServer, streamProxy.getApp(), streamProxy.getStream(), null, null, true); + } + closeStreams(mediaServer, streamProxy.getApp(), streamProxy.getStream()); + } + + JSONObject jsonObject = null; + if ("ffmpeg".equalsIgnoreCase(streamProxy.getType())){ + if (streamProxy.getTimeout() == 0) { + streamProxy.setTimeout(15); + } + jsonObject = zlmresTfulUtils.addFFmpegSource(mediaServer, streamProxy.getSrcUrl().trim(), dstUrl, + streamProxy.getTimeout(), streamProxy.isEnableAudio(), streamProxy.isEnableMp4(), + streamProxy.getFfmpegCmdKey()); + }else { + jsonObject = zlmresTfulUtils.addStreamProxy(mediaServer, streamProxy.getApp(), streamProxy.getStream(), streamProxy.getSrcUrl().trim(), + streamProxy.isEnableAudio(), streamProxy.isEnableMp4(), streamProxy.getRtspType(), streamProxy.getTimeout()); + } + if (jsonObject == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "请求失败"); + }else if (jsonObject.getInteger("code") != 0) { + throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg")); + }else { + JSONObject data = jsonObject.getJSONObject("data"); + if (data == null) { + throw new ControllerException(jsonObject.getInteger("code"), "代理结果异常: " + jsonObject); + }else { + streamProxy.setStreamKey(jsonObject.getString("key")); + return getStreamInfoByAppAndStream(mediaServer, streamProxy.getApp(), streamProxy.getStream(), null, null, true); + } + } + } + + private String getSchemaFromFFmpegCmd(String ffmpegCmd) { + ffmpegCmd = ffmpegCmd.replaceAll(" + ", " "); + String[] paramArray = ffmpegCmd.split(" "); + if (paramArray.length == 0) { + return null; + } + for (int i = 0; i < paramArray.length; i++) { + if (paramArray[i].equalsIgnoreCase("-f")) { + if (i + 1 < paramArray.length - 1) { + return paramArray[i+1]; + }else { + return null; + } + + } + } + return null; + } + + @Override + public void stopProxy(MediaServer mediaServer, String streamKey) { + JSONObject jsonObject = zlmresTfulUtils.delStreamProxy(mediaServer, streamKey); + if (jsonObject == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "请求失败"); + }else if (jsonObject.getInteger("code") != 0) { + throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg")); + } + } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaServerStatusManger.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaServerStatusManger.java index cdf1e3f27..6a11455ce 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaServerStatusManger.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaServerStatusManger.java @@ -5,15 +5,14 @@ import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerDeleteEvent; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.zlm.dto.ZLMServerConfig; import com.genersoft.iot.vmp.media.zlm.event.HookZlmServerKeepaliveEvent; import com.genersoft.iot.vmp.media.zlm.event.HookZlmServerStartEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.event.EventListener; @@ -31,11 +30,10 @@ import java.util.concurrent.ConcurrentHashMap; /** * 管理zlm流媒体节点的状态 */ +@Slf4j @Component public class ZLMMediaServerStatusManger { - private final static Logger logger = LoggerFactory.getLogger(ZLMMediaServerStatusManger.class); - private final Map offlineZlmPrimaryMap = new ConcurrentHashMap<>(); private final Map offlineZlmsecondaryMap = new ConcurrentHashMap<>(); private final Map offlineZlmTimeMap = new ConcurrentHashMap<>(); @@ -74,7 +72,7 @@ public class ZLMMediaServerStatusManger { if (!type.equals(mediaServerItem.getType())) { continue; } - logger.info("[ZLM-添加待上线节点] ID:" + mediaServerItem.getId()); + log.info("[ZLM-添加待上线节点] ID:" + mediaServerItem.getId()); offlineZlmPrimaryMap.put(mediaServerItem.getId(), mediaServerItem); offlineZlmTimeMap.put(mediaServerItem.getId(), System.currentTimeMillis()); execute(); @@ -93,7 +91,7 @@ public class ZLMMediaServerStatusManger { if (serverItem == null) { return; } - logger.info("[ZLM-HOOK事件-服务启动] ID:" + event.getMediaServerItem().getId()); + log.info("[ZLM-HOOK事件-服务启动] ID:" + event.getMediaServerItem().getId()); online(serverItem, null); } @@ -107,7 +105,7 @@ public class ZLMMediaServerStatusManger { if (serverItem == null) { return; } - logger.info("[ZLM-HOOK事件-心跳] ID:" + event.getMediaServerItem().getId()); + log.debug("[ZLM-HOOK事件-心跳] ID:" + event.getMediaServerItem().getId()); online(serverItem, null); } @@ -117,7 +115,7 @@ public class ZLMMediaServerStatusManger { if (event.getMediaServerId() == null) { return; } - logger.info("[ZLM-节点被移除] ID:" + event.getMediaServerId()); + log.info("[ZLM-节点被移除] ID:" + event.getMediaServerId()); offlineZlmPrimaryMap.remove(event.getMediaServerId()); offlineZlmsecondaryMap.remove(event.getMediaServerId()); offlineZlmTimeMap.remove(event.getMediaServerId()); @@ -136,16 +134,16 @@ public class ZLMMediaServerStatusManger { offlineZlmPrimaryMap.remove(mediaServerItem.getId()); continue; } - logger.info("[ZLM-尝试连接] ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + log.info("[ZLM-尝试连接] ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); JSONObject responseJson = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); ZLMServerConfig zlmServerConfig = null; if (responseJson == null) { - logger.info("[ZLM-尝试连接]失败, ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + log.info("[ZLM-尝试连接]失败, ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); continue; } JSONArray data = responseJson.getJSONArray("data"); if (data == null || data.isEmpty()) { - logger.info("[ZLM-尝试连接]失败, ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + log.info("[ZLM-尝试连接]失败, ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); }else { zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); initPort(mediaServerItem, zlmServerConfig); @@ -158,17 +156,17 @@ public class ZLMMediaServerStatusManger { if (offlineZlmTimeMap.get(mediaServerItem.getId()) < System.currentTimeMillis() - 30*60*1000) { continue; } - logger.info("[ZLM-尝试连接] ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + log.info("[ZLM-尝试连接] ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); JSONObject responseJson = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); ZLMServerConfig zlmServerConfig = null; if (responseJson == null) { - logger.info("[ZLM-尝试连接]失败, ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + log.info("[ZLM-尝试连接]失败, ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); offlineZlmTimeMap.put(mediaServerItem.getId(), System.currentTimeMillis()); continue; } JSONArray data = responseJson.getJSONArray("data"); if (data == null || data.isEmpty()) { - logger.info("[ZLM-尝试连接]失败, ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + log.info("[ZLM-尝试连接]失败, ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); offlineZlmTimeMap.put(mediaServerItem.getId(), System.currentTimeMillis()); }else { zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); @@ -184,7 +182,7 @@ public class ZLMMediaServerStatusManger { offlineZlmsecondaryMap.remove(mediaServerItem.getId()); offlineZlmTimeMap.remove(mediaServerItem.getId()); if (!mediaServerItem.isStatus()) { - logger.info("[ZLM-连接成功] ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + log.info("[ZLM-连接成功] ID:{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); mediaServerItem.setStatus(true); mediaServerItem.setHookAliveInterval(10F); mediaServerService.update(mediaServerItem); @@ -207,7 +205,7 @@ public class ZLMMediaServerStatusManger { // 设置两次心跳未收到则认为zlm离线 String key = "zlm-keepalive-" + mediaServerItem.getId(); dynamicTask.startDelay(key, ()->{ - logger.warn("[ZLM-心跳超时] ID:{}", mediaServerItem.getId()); + log.warn("[ZLM-心跳超时] ID:{}", mediaServerItem.getId()); mediaServerItem.setStatus(false); offlineZlmPrimaryMap.put(mediaServerItem.getId(), mediaServerItem); offlineZlmTimeMap.put(mediaServerItem.getId(), System.currentTimeMillis()); @@ -235,11 +233,17 @@ public class ZLMMediaServerStatusManger { if (mediaServerItem.getRtpProxyPort() == 0) { mediaServerItem.setRtpProxyPort(zlmServerConfig.getRtpProxyPort()); } + if (mediaServerItem.getFlvSSLPort() == 0) { + mediaServerItem.setFlvSSLPort(zlmServerConfig.getHttpSSLport()); + } + if (mediaServerItem.getWsFlvSSLPort() == 0) { + mediaServerItem.setWsFlvSSLPort(zlmServerConfig.getHttpSSLport()); + } mediaServerItem.setHookAliveInterval(10F); } public void setZLMConfig(MediaServer mediaServerItem, boolean restart) { - logger.info("[媒体服务节点] 正在设置 :{} -> {}:{}", + log.info("[媒体服务节点] 正在设置 :{} -> {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); String protocol = sslEnabled ? "https" : "http"; String hookPrefix = String.format("%s://%s:%s%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort, (serverServletContextPath == null || "/".equals(serverServletContextPath)) ? "" : serverServletContextPath); @@ -290,15 +294,15 @@ public class ZLMMediaServerStatusManger { if (responseJSON != null && responseJSON.getInteger("code") == 0) { if (restart) { - logger.info("[媒体服务节点] 设置成功,开始重启以保证配置生效 {} -> {}:{}", + log.info("[媒体服务节点] 设置成功,开始重启以保证配置生效 {} -> {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); zlmresTfulUtils.restartServer(mediaServerItem); }else { - logger.info("[媒体服务节点] 设置成功 {} -> {}:{}", + log.info("[媒体服务节点] 设置成功 {} -> {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); } }else { - logger.info("[媒体服务节点] 设置媒体服务节点失败 {} -> {}:{}", + log.info("[媒体服务节点] 设置媒体服务节点失败 {} -> {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java index 5d5f1c317..9125858cd 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java @@ -3,14 +3,15 @@ package com.genersoft.iot.vmp.media.zlm; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.media.bean.MediaServer; +import lombok.extern.slf4j.Slf4j; 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 java.io.*; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; import java.net.ConnectException; import java.net.SocketTimeoutException; import java.util.HashMap; @@ -18,11 +19,10 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; +@Slf4j @Component public class ZLMRESTfulUtils { - private final static Logger logger = LoggerFactory.getLogger(ZLMRESTfulUtils.class); - private OkHttpClient client; public interface RequestCallback{ @@ -46,9 +46,9 @@ public class ZLMRESTfulUtils { httpClientBuilder.readTimeout(readTimeOut,TimeUnit.SECONDS); // 设置连接池 httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES)); - if (logger.isDebugEnabled()) { + if (log.isDebugEnabled()) { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> { - logger.debug("http请求参数:" + message); + log.debug("http请求参数:" + message); }); logging.setLevel(HttpLoggingInterceptor.Level.BASIC); // OkHttp進行添加攔截器loggingInterceptor @@ -107,19 +107,19 @@ public class ZLMRESTfulUtils { Objects.requireNonNull(response.body()).close(); } }catch (IOException e) { - logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + log.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); if(e instanceof SocketTimeoutException){ //读取超时超时异常 - logger.error(String.format("读取ZLM数据超时失败: %s, %s", url, e.getMessage())); + log.error(String.format("读取ZLM数据超时失败: %s, %s", url, e.getMessage())); } if(e instanceof ConnectException){ //判断连接异常,我这里是报Failed to connect to 10.7.5.144 - logger.error(String.format("连接ZLM连接失败: %s, %s", url, e.getMessage())); + log.error(String.format("连接ZLM连接失败: %s, %s", url, e.getMessage())); } }catch (Exception e){ - logger.error(String.format("访问ZLM失败: %s, %s", url, e.getMessage())); + log.error(String.format("访问ZLM失败: %s, %s", url, e.getMessage())); } }else { client.newCall(request).enqueue(new Callback(){ @@ -131,7 +131,7 @@ public class ZLMRESTfulUtils { String responseStr = Objects.requireNonNull(response.body()).string(); callback.run(JSON.parseObject(responseStr)); } catch (IOException e) { - logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + log.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); } }else { @@ -142,15 +142,15 @@ public class ZLMRESTfulUtils { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { - logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage())); + log.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())); + log.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())); + log.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage())); } } }); @@ -179,7 +179,9 @@ public class ZLMRESTfulUtils { Request request = new Request.Builder() .url(httpBuilder.build()) .build(); - logger.info(request.toString()); + if (log.isDebugEnabled()){ + log.debug(request.toString()); + } try { OkHttpClient client = getClient(); Response response = client.newCall(request).execute(); @@ -188,7 +190,7 @@ public class ZLMRESTfulUtils { File snapFolder = new File(targetPath); if (!snapFolder.exists()) { if (!snapFolder.mkdirs()) { - logger.warn("{}路径创建失败", snapFolder.getAbsolutePath()); + log.warn("{}路径创建失败", snapFolder.getAbsolutePath()); } } @@ -199,17 +201,17 @@ public class ZLMRESTfulUtils { outStream.flush(); outStream.close(); } else { - logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message())); + log.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message())); } } else { - logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message())); + log.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message())); } Objects.requireNonNull(response.body()).close(); } catch (ConnectException e) { - logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage())); - logger.info("请检查media配置并确认ZLM已启动..."); + log.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage())); + log.info("请检查media配置并确认ZLM已启动..."); } catch (IOException e) { - logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + log.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); } } @@ -266,14 +268,14 @@ public class ZLMRESTfulUtils { return sendPost(mediaServerItem, "getRtpInfo",param, null); } - public JSONObject addFFmpegSource(MediaServer mediaServerItem, String src_url, String dst_url, Integer timeout_ms, + public JSONObject addFFmpegSource(MediaServer mediaServerItem, String src_url, String dst_url, Integer timeout_sec, boolean enable_audio, boolean enable_mp4, String ffmpeg_cmd_key){ - logger.info(src_url); - logger.info(dst_url); + log.info(src_url); + log.info(dst_url); Map param = new HashMap<>(); param.put("src_url", src_url); param.put("dst_url", dst_url); - param.put("timeout_ms", timeout_ms); + param.put("timeout_ms", timeout_sec*1000); param.put("enable_mp4", enable_mp4); param.put("ffmpeg_cmd_key", ffmpeg_cmd_key); return sendPost(mediaServerItem, "addFFmpegSource",param, null); @@ -335,7 +337,7 @@ public class ZLMRESTfulUtils { return sendPost(mediaServerItem, "restartServer",null, null); } - public JSONObject addStreamProxy(MediaServer mediaServerItem, String app, String stream, String url, boolean enable_audio, boolean enable_mp4, String rtp_type) { + public JSONObject addStreamProxy(MediaServer mediaServerItem, String app, String stream, String url, boolean enable_audio, boolean enable_mp4, String rtp_type, Integer timeOut) { Map param = new HashMap<>(); param.put("vhost", "__defaultVhost__"); param.put("app", app); @@ -344,6 +346,9 @@ public class ZLMRESTfulUtils { param.put("enable_mp4", enable_mp4?1:0); param.put("enable_audio", enable_audio?1:0); param.put("rtp_type", rtp_type); + param.put("timeout_sec", timeOut); + // 拉流重试次数,默认为3 + param.put("retry_count", 3); return sendPost(mediaServerItem, "addStreamProxy",param, null, 20); } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java index 9d10528e3..997ffd838 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java @@ -1,24 +1,20 @@ package com.genersoft.iot.vmp.media.zlm; -import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.CommonCallback; import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; +@Slf4j @Component public class ZLMServerFactory { - - private Logger logger = LoggerFactory.getLogger("ZLMServerFactory"); - @Autowired private ZLMRESTfulUtils zlmresTfulUtils; @@ -43,7 +39,6 @@ public class ZLMServerFactory { int result = -1; // 查询此rtp server 是否已经存在 JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId); - logger.info(JSONObject.toJSONString(rtpInfo)); if(rtpInfo.getInteger("code") == 0){ if (rtpInfo.getBoolean("exist")) { result = rtpInfo.getInteger("local_port"); @@ -57,7 +52,7 @@ public class ZLMServerFactory { if (jsonObject.getInteger("code") == 0) { return createRTPServer(mediaServerItem, streamId, ssrc, port,onlyAuto, reUsePort,disableAudio, tcpMode); }else { - logger.warn("[开启rtpServer], 重启RtpServer错误"); + log.warn("[开启rtpServer], 重启RtpServer错误"); } } } @@ -95,16 +90,15 @@ public class ZLMServerFactory { } JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param); - logger.info(JSONObject.toJSONString(openRtpServerResultJson)); if (openRtpServerResultJson != null) { if (openRtpServerResultJson.getInteger("code") == 0) { result= openRtpServerResultJson.getInteger("port"); }else { - logger.error("创建RTP Server 失败 {}: ", openRtpServerResultJson.getString("msg")); + log.error("创建RTP Server 失败 {}: ", openRtpServerResultJson.getString("msg")); } }else { // 检查ZLM状态 - logger.error("创建RTP Server 失败 {}: 请检查ZLM服务", param.get("port")); + log.error("创建RTP Server 失败 {}: 请检查ZLM服务", param.get("port")); } return result; } @@ -115,16 +109,16 @@ public class ZLMServerFactory { Map param = new HashMap<>(); param.put("stream_id", streamId); JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(serverItem, param); - logger.info("关闭RTP Server " + jsonObject); + log.info("关闭RTP Server " + jsonObject); if (jsonObject != null ) { if (jsonObject.getInteger("code") == 0) { result = jsonObject.getInteger("hit") >= 1; }else { - logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); + log.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); } }else { // 检查ZLM状态 - logger.error("关闭RTP Server 失败: 请检查ZLM服务"); + log.error("关闭RTP Server 失败: 请检查ZLM服务"); } } return result; @@ -143,11 +137,11 @@ public class ZLMServerFactory { callback.run(jsonObject.getInteger("hit") == 1); return; }else { - logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); + log.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); } }else { // 检查ZLM状态 - logger.error("关闭RTP Server 失败: 请检查ZLM服务"); + log.error("关闭RTP Server 失败: 请检查ZLM服务"); } callback.run(false); }); @@ -199,19 +193,19 @@ public class ZLMServerFactory { } Integer code = mediaInfo.getInteger("code"); if (code < 0) { - logger.warn("查询流({}/{})是否有其它观看者时得到: {}", app, streamId, mediaInfo.getString("msg")); + log.warn("查询流({}/{})是否有其它观看者时得到: {}", app, streamId, mediaInfo.getString("msg")); return -1; } if ( code == 0 && mediaInfo.getBoolean("online") != null && ! mediaInfo.getBoolean("online")) { - logger.warn("查询流({}/{})是否有其它观看者时得到: {}", app, streamId, mediaInfo.getString("msg")); + log.warn("查询流({}/{})是否有其它观看者时得到: {}", app, streamId, mediaInfo.getString("msg")); return -1; } return mediaInfo.getInteger("totalReaderCount"); } - public JSONObject startSendRtp(MediaServer mediaInfo, SendRtpItem sendRtpItem) { + public JSONObject startSendRtp(MediaServer mediaInfo, SendRtpInfo sendRtpItem) { String is_Udp = sendRtpItem.isTcp() ? "0" : "1"; - logger.info("rtp/{}开始推流, 目标={}:{},SSRC={}", sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc()); + log.info("rtp/{}开始推流, 目标={}:{},SSRC={}", sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc()); Map param = new HashMap<>(12); param.put("vhost","__defaultVhost__"); param.put("app",sendRtpItem.getApp()); @@ -234,7 +228,6 @@ public class ZLMServerFactory { if (sendRtpItem.getLocalPort() != 0) { if (sendRtpItem.isTcpActive()) { startSendRtpStreamResult = startSendRtpPassive(mediaInfo, param); - System.out.println(JSON.toJSON(param)); }else { param.put("is_udp", is_Udp); param.put("dst_url", sendRtpItem.getIp()); @@ -258,18 +251,18 @@ public class ZLMServerFactory { boolean result = false; JSONObject jsonObject = zlmresTfulUtils.updateRtpServerSSRC(mediaServerItem, streamId, ssrc); if (jsonObject == null) { - logger.error("[更新RTPServer] 失败: 请检查ZLM服务"); + log.error("[更新RTPServer] 失败: 请检查ZLM服务"); } else if (jsonObject.getInteger("code") == 0) { result= true; - logger.info("[更新RTPServer] 成功"); + log.info("[更新RTPServer] 成功"); } else { - logger.error("[更新RTPServer] 失败: {}, streamId:{},ssrc:{}->\r\n{}",jsonObject.getString("msg"), + log.error("[更新RTPServer] 失败: {}, streamId:{},ssrc:{}->\r\n{}",jsonObject.getString("msg"), streamId, ssrc, jsonObject); } return result; } - public JSONObject stopSendRtpStream(MediaServer mediaServerItem, SendRtpItem sendRtpItem) { + public JSONObject stopSendRtpStream(MediaServer mediaServerItem, SendRtpInfo sendRtpItem) { Map param = new HashMap<>(); param.put("vhost", "__defaultVhost__"); param.put("app", sendRtpItem.getApp()); diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ChannelOnlineEvent.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ChannelOnlineEvent.java index 6b3c94f8c..207ad4b0b 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ChannelOnlineEvent.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ChannelOnlineEvent.java @@ -1,6 +1,6 @@ package com.genersoft.iot.vmp.media.zlm.dto; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import java.text.ParseException; @@ -9,5 +9,5 @@ import java.text.ParseException; */ public interface ChannelOnlineEvent { - void run(SendRtpItem sendRtpItem) throws ParseException; + void run(SendRtpInfo sendRtpItem) throws ParseException; } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyItem.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyItem.java deleted file mode 100755 index 0486d00d5..000000000 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyItem.java +++ /dev/null @@ -1,180 +0,0 @@ -package com.genersoft.iot.vmp.media.zlm.dto; - -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import io.swagger.v3.oas.annotations.media.Schema; - -/** - * @author lin - */ -@Schema(description = "拉流代理的信息") -public class StreamProxyItem extends GbStream { - - @Schema(description = "类型") - private String type; - @Schema(description = "应用名") - private String app; - @Schema(description = "流ID") - private String stream; - @Schema(description = "流媒体服务ID") - private String mediaServerId; - @Schema(description = "拉流地址") - private String url; - @Schema(description = "拉流地址") - private String srcUrl; - @Schema(description = "目标地址") - private String dstUrl; - @Schema(description = "超时时间") - private int timeoutMs; - @Schema(description = "ffmpeg模板KEY") - private String ffmpegCmdKey; - @Schema(description = "rtsp拉流时,拉流方式,0:tcp,1:udp,2:组播") - private String rtpType; - @Schema(description = "是否启用") - private boolean enable; - @Schema(description = "是否启用音频") - private boolean enableAudio; - @Schema(description = "是否启用MP4") - private boolean enableMp4; - @Schema(description = "是否 无人观看时删除") - private boolean enableRemoveNoneReader; - - @Schema(description = "是否 无人观看时自动停用") - private boolean enableDisableNoneReader; - - @Schema(description = "拉流代理时zlm返回的key,用于停止拉流代理") - private String streamKey; - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - @Override - public String getApp() { - return app; - } - - @Override - public void setApp(String app) { - this.app = app; - } - - @Override - public String getStream() { - return stream; - } - - @Override - public void setStream(String stream) { - this.stream = stream; - } - - @Override - public String getMediaServerId() { - return mediaServerId; - } - - @Override - public void setMediaServerId(String mediaServerId) { - this.mediaServerId = mediaServerId; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getSrcUrl() { - return srcUrl; - } - - public void setSrcUrl(String src_url) { - this.srcUrl = src_url; - } - - public String getDstUrl() { - return dstUrl; - } - - public void setDstUrl(String dst_url) { - this.dstUrl = dst_url; - } - - public int getTimeoutMs() { - return timeoutMs; - } - - public void setTimeoutMs(int timeout_ms) { - this.timeoutMs = timeout_ms; - } - - public String getFfmpegCmdKey() { - return ffmpegCmdKey; - } - - public void setFfmpegCmdKey(String ffmpeg_cmd_key) { - this.ffmpegCmdKey = ffmpeg_cmd_key; - } - - public String getRtpType() { - return rtpType; - } - - public void setRtpType(String rtp_type) { - this.rtpType = rtp_type; - } - - public boolean isEnable() { - return enable; - } - - public void setEnable(boolean enable) { - this.enable = enable; - } - - public boolean isEnableMp4() { - return enableMp4; - } - - public void setEnableMp4(boolean enable_mp4) { - this.enableMp4 = enable_mp4; - } - - public boolean isEnableRemoveNoneReader() { - return enableRemoveNoneReader; - } - - public void setEnableRemoveNoneReader(boolean enable_remove_none_reader) { - this.enableRemoveNoneReader = enable_remove_none_reader; - } - - public boolean isEnableDisableNoneReader() { - return enableDisableNoneReader; - } - - public void setEnableDisableNoneReader(boolean enable_disable_none_reader) { - this.enableDisableNoneReader = enable_disable_none_reader; - } - - public boolean isEnableAudio() { - return enableAudio; - } - - public void setEnableAudio(boolean enable_audio) { - this.enableAudio = enable_audio; - } - - public String getStreamKey() { - return streamKey; - } - - public void setStreamKey(String streamKey) { - this.streamKey = streamKey; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java deleted file mode 100755 index f498479d8..000000000 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java +++ /dev/null @@ -1,432 +0,0 @@ -package com.genersoft.iot.vmp.media.zlm.dto; - -import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent; -import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; -import com.genersoft.iot.vmp.utils.DateUtil; -import io.swagger.v3.oas.annotations.media.Schema; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -@Schema(description = "推流信息") -public class StreamPushItem extends GbStream implements Comparable{ - - /** - * id - */ - @Schema(description = "id") - private Integer id; - - /** - * 应用名 - */ - @Schema(description = "应用名") - private String app; - - /** - * 流id - */ - @Schema(description = "流id") - private String stream; - - /** - * 观看总人数,包括hls/rtsp/rtmp/http-flv/ws-flv - */ - @Schema(description = "观看总人数") - private Integer totalReaderCount; - - /** - * 协议 包括hls/rtsp/rtmp/http-flv/ws-flv - */ - @Schema(description = "协议 包括hls/rtsp/rtmp/http-flv/ws-flv") - private List schemas; - - /** - * 产生源类型, - * unknown = 0, - * rtmp_push=1, - * rtsp_push=2, - * rtp_push=3, - * pull=4, - * ffmpeg_pull=5, - * mp4_vod=6, - * device_chn=7 - */ - @Schema(description = "产生源类型") - private int originType; - - /** - * 客户端和服务器网络信息,可能为null类型 - */ - @Schema(description = "客户端和服务器网络信息,可能为null类型") - private OnStreamChangedHookParam.OriginSock originSock; - - /** - * 产生源类型的字符串描述 - */ - @Schema(description = "产生源类型的字符串描述") - private String originTypeStr; - - /** - * 产生源的url - */ - @Schema(description = "产生源的url") - private String originUrl; - - /** - * 存活时间,单位秒 - */ - @Schema(description = "存活时间,单位秒") - private Long aliveSecond; - - /** - * 音视频轨道 - */ - @Schema(description = "音视频轨道") - private List tracks; - - /** - * 音视频轨道 - */ - @Schema(description = "音视频轨道") - private String vhost; - - /** - * 使用的流媒体ID - */ - @Schema(description = "使用的流媒体ID") - private String mediaServerId; - - /** - * 使用的服务ID - */ - @Schema(description = "使用的服务ID") - private String serverId; - - /** - * 推流时间 - */ - @Schema(description = "推流时间") - private String pushTime; - - /** - * 更新时间 - */ - @Schema(description = "更新时间") - private String updateTime; - - /** - * 创建时间 - */ - @Schema(description = "创建时间") - private String createTime; - - /** - * 是否正在推流 - */ - @Schema(description = "是否正在推流") - private boolean pushIng; - - /** - * 是否自己平台的推流 - */ - @Schema(description = "是否自己平台的推流") - private boolean self; - - - - public String getVhost() { - return vhost; - } - - public void setVhost(String vhost) { - this.vhost = vhost; - } - - - @Override - public int compareTo(@NotNull StreamPushItem streamPushItem) { - return Long.valueOf(DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(super.createTime) - - DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(streamPushItem.getCreateTime())).intValue(); - } - - public StreamPushItem getInstance(StreamInfo streamInfo) { - StreamPushItem streamPushItem = new StreamPushItem(); - streamPushItem.setApp(streamInfo.getApp()); - streamPushItem.setMediaServerId(streamInfo.getMediaServerId()); - streamPushItem.setStream(streamInfo.getStream()); - streamPushItem.setAliveSecond(streamInfo.getMediaInfo().getAliveSecond()); -// streamPushItem.setOriginSock(streamInfo.getMediaInfo().getOriginSock()); - streamPushItem.setTotalReaderCount(streamInfo.getMediaInfo().getReaderCount()); - streamPushItem.setOriginType(streamInfo.getOriginType()); -// streamPushItem.setOriginTypeStr(streamInfo.getMediaInfo().getOriginTypeStr()); -// streamPushItem.setOriginUrl(streamInfo.getMediaInfo().getOriginUrl()); - streamPushItem.setCreateTime(DateUtil.getNow()); - streamPushItem.setAliveSecond(streamInfo.getMediaInfo().getAliveSecond()); - streamPushItem.setStatus(true); - streamPushItem.setStreamType("push"); -// streamPushItem.setVhost(streamInfo.getVhost()); - streamPushItem.setServerId(streamInfo.getMediaServerId()); - return streamPushItem; - - } - - public static StreamPushItem getInstance(MediaArrivalEvent event, String serverId){ - StreamPushItem streamPushItem = new StreamPushItem(); - streamPushItem.setApp(event.getApp()); - streamPushItem.setMediaServerId(event.getMediaServer().getId()); - streamPushItem.setStream(event.getStream()); - streamPushItem.setAliveSecond(event.getMediaInfo().getAliveSecond()); -// streamPushItem.setOriginSock(streamInfo.getMediaInfo().getOriginSock()); - streamPushItem.setTotalReaderCount(event.getMediaInfo().getReaderCount()); - streamPushItem.setOriginType(event.getMediaInfo().getOriginType()); -// streamPushItem.setOriginTypeStr(streamInfo.getMediaInfo().getOriginTypeStr()); -// streamPushItem.setOriginUrl(streamInfo.getMediaInfo().getOriginUrl()); - streamPushItem.setCreateTime(DateUtil.getNow()); - streamPushItem.setAliveSecond(event.getMediaInfo().getAliveSecond()); - streamPushItem.setStatus(true); - streamPushItem.setStreamType("push"); -// streamPushItem.setVhost(streamInfo.getVhost()); - streamPushItem.setServerId(serverId); - return streamPushItem; - } - - public static class MediaSchema { - private String schema; - private Long bytesSpeed; - - public String getSchema() { - return schema; - } - - public void setSchema(String schema) { - this.schema = schema; - } - - public Long getBytesSpeed() { - return bytesSpeed; - } - - public void setBytesSpeed(Long bytesSpeed) { - this.bytesSpeed = bytesSpeed; - } - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - @Override - public String getApp() { - return app; - } - - @Override - public void setApp(String app) { - this.app = app; - } - - @Override - public String getStream() { - return stream; - } - - @Override - public void setStream(String stream) { - this.stream = stream; - } - - public Integer getTotalReaderCount() { - return totalReaderCount; - } - - public void setTotalReaderCount(Integer totalReaderCount) { - this.totalReaderCount = totalReaderCount; - } - - public List getSchemas() { - return schemas; - } - - public void setSchemas(List schemas) { - this.schemas = schemas; - } - - public int getOriginType() { - return originType; - } - - public void setOriginType(int originType) { - this.originType = originType; - } - - public OnStreamChangedHookParam.OriginSock getOriginSock() { - return originSock; - } - - public void setOriginSock(OnStreamChangedHookParam.OriginSock originSock) { - this.originSock = originSock; - } - - - public String getOriginTypeStr() { - return originTypeStr; - } - - public void setOriginTypeStr(String originTypeStr) { - this.originTypeStr = originTypeStr; - } - - public String getOriginUrl() { - return originUrl; - } - - public void setOriginUrl(String originUrl) { - this.originUrl = originUrl; - } - - public Long getAliveSecond() { - return aliveSecond; - } - - public void setAliveSecond(Long aliveSecond) { - this.aliveSecond = aliveSecond; - } - - public List getTracks() { - return tracks; - } - - public void setTracks(List tracks) { - this.tracks = tracks; - } - - - @Override - public String getMediaServerId() { - return mediaServerId; - } - - @Override - public void setMediaServerId(String mediaServerId) { - this.mediaServerId = mediaServerId; - } - - public String getServerId() { - return serverId; - } - - public void setServerId(String serverId) { - this.serverId = serverId; - } - - - public String getPushTime() { - return pushTime; - } - - public void setPushTime(String pushTime) { - this.pushTime = pushTime; - } - - public String getUpdateTime() { - return updateTime; - } - - public void setUpdateTime(String updateTime) { - this.updateTime = updateTime; - } - - @Override - public String getCreateTime() { - return createTime; - } - - @Override - public void setCreateTime(String createTime) { - this.createTime = createTime; - } - - public boolean isPushIng() { - return pushIng; - } - - public void setPushIng(boolean pushIng) { - this.pushIng = pushIng; - } - - public boolean isSelf() { - return self; - } - - public void setSelf(boolean self) { - this.self = self; - } - -// @Override -// public Integer getGbStreamId() { -// return super.getGbStreamId(); -// } -// -// @Override -// public void setGbStreamId(Integer gbStreamId) { -// super.setGbStreamId(gbStreamId); -// } -// -// -// public String getGbId() { -// return super.getGbId(); -// } -// -// public void setGbId(String gbId) { -// super.setGbId(gbId); -// } -// -// public String getName() { -// return super.getName(); -// } -// -// public void setName(String name) { -// super.setName(name); -// } -// -// public double getLongitude() { -// return super.getLongitude(); -// } -// -// public void setLongitude(double longitude) { -// super.setLongitude(longitude); -// } -// -// public double getLatitude() { -// return super.getLatitude(); -// } -// -// public void setLatitude(double latitude) { -// super.setLatitude(latitude); -// } -// -// public String getStreamType() { -// return super.getStreamType(); -// } -// -// public void setStreamType(String streamType) { -// super.setStreamType(streamType); -// } -// -// public boolean isStatus() { -// return super.isStatus(); -// } -// -// public void setStatus(boolean status) { -// super.setStatus(status); -// } - -} - diff --git a/src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java b/src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java deleted file mode 100755 index 3ea8e5fc7..000000000 --- a/src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.genersoft.iot.vmp.service; - -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; -import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; - -import java.util.List; - -/** - * 国标通道业务类 - * @author lin - */ -public interface IDeviceChannelService { - - /** - * 更新gps信息 - */ - DeviceChannel updateGps(DeviceChannel deviceChannel, Device device); - - /** - * 添加设备通道 - * - * @param deviceId 设备id - * @param channel 通道 - */ - void updateChannel(String deviceId, DeviceChannel channel); - - /** - * 批量添加设备通道 - * - * @param deviceId 设备id - * @param channels 多个通道 - */ - int updateChannels(String deviceId, List channels); - - /** - * 获取统计信息 - * @return - */ - ResourceBaseInfo getOverview(); - - /** - * 查询所有未分配的通道 - * @param platformId - * @return - */ - List queryAllChannelList(String platformId); - - /** - * 数据位置信息格式处理 - */ - boolean updateAllGps(Device device); - - /** - * 查询通道所属的设备 - */ - List getDeviceByChannelId(String channelId); - - /** - * 批量删除通道 - * @param deleteChannelList 待删除的通道列表 - */ - int deleteChannels(List deleteChannelList); - - /** - * 批量上线 - */ - int channelsOnline(List channels); - - /** - * 批量下线 - */ - int channelsOffline(List channels); - - /** - * 获取一个通道 - */ - DeviceChannel getOne(String deviceId, String channelId); - - /** - * 直接批量更新通道 - */ - void batchUpdateChannel(List channels); - - /** - * 直接批量添加 - */ - void batchAddChannel(List deviceChannels); - - /** - * 修改通道的码流类型 - */ - void updateChannelStreamIdentification(DeviceChannel channel); - - List queryChaneListByDeviceId(String deviceId); - - void updateChannelGPS(Device device, DeviceChannel deviceChannel, MobilePosition mobilePosition); - - void stopPlay(String deviceId, String channelId); - void batchUpdateChannelGPS(List channelList); - - void batchAddMobilePosition(List addMobilePositionList); - - void online(DeviceChannel channel); - - void offline(DeviceChannel channel); - - void delete(DeviceChannel channel); -} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java b/src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java deleted file mode 100755 index e40909779..000000000 --- a/src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.genersoft.iot.vmp.service; - -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; -import com.github.pagehelper.PageInfo; - -import java.util.List; -import java.util.Map; - -/** - * 级联国标平台关联流业务接口 - */ -public interface IGbStreamService { - - /** - * 分页获取所有 - * @param page - * @param count - * @return - */ - PageInfo getAll(Integer page, Integer count, String platFormId, String catalogId,String query,String mediaServerId); - - - /** - * 移除 - * @param app - * @param stream - */ - void del(String app, String stream); - - /** - * 保存国标关联 - * @param gbStreams - */ - boolean addPlatformInfo(List gbStreams, String platformId, String catalogId); - - /** - * 移除国标关联 - * @param gbStreams - * @param platformId - */ - boolean delPlatformInfo(String platformId, List gbStreams); - - DeviceChannel getDeviceChannelListByStream(GbStream gbStream, String catalogId, ParentPlatform platform); - - void sendCatalogMsg(GbStream gbStream, String type); - void sendCatalogMsgs(List gbStreams, String type); - - /** - * 修改gbId或name - * @param streamPushItemForUpdate - * @return - */ - int updateGbIdOrName(List streamPushItemForUpdate); - - DeviceChannel getDeviceChannelListByStreamWithStatus(GbStream gbStream, String catalogId, ParentPlatform platform); - - /** - * 查询所有未分配的通道 - * @param platformId - * @return - */ - List getAllGBChannels(String platformId); - - /** - * 移除所有关联的通道 - * @param platformId - * @param catalogId - */ - void delAllPlatformInfo(String platformId, String catalogId); - - List getGbChannelWithGbid(String gbId); - - Map getAllGBId(); - -} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IMobilePositionService.java b/src/main/java/com/genersoft/iot/vmp/service/IMobilePositionService.java index 9af64150b..bbfe7a7e1 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IMobilePositionService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IMobilePositionService.java @@ -2,6 +2,8 @@ package com.genersoft.iot.vmp.service; import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import java.util.List; @@ -10,4 +12,13 @@ public interface IMobilePositionService { void add(List mobilePositionList); void add(MobilePosition mobilePosition); + + List queryMobilePositions(String deviceId, String channelId, String startTime, String endTime); + + List queryEnablePlatformListWithAsMessageChannel(); + + MobilePosition queryLatestPosition(String deviceId); + + void updateStreamGPS(List gpsMsgInfoList); + } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IPlatformChannelService.java b/src/main/java/com/genersoft/iot/vmp/service/IPlatformChannelService.java deleted file mode 100755 index 49d74282c..000000000 --- a/src/main/java/com/genersoft/iot/vmp/service/IPlatformChannelService.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.genersoft.iot.vmp.service; - -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; - -import java.util.List; - -/** - * 平台关联通道管理 - * @author lin - */ -public interface IPlatformChannelService { - - /** - * 更新目录下的通道 - * @param platformId 平台编号 - * @param channelReduces 通道信息 - * @param catalogId 目录编号 - * @return - */ - int updateChannelForGB(String platformId, List channelReduces, String catalogId); - - /** - * 移除目录下的所有通道 - * @param platformId - * @param catalogId - * @return - */ - int delAllChannelForGB(String platformId, String catalogId); -} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java deleted file mode 100755 index b3cab0e84..000000000 --- a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.genersoft.iot.vmp.service; - -import com.alibaba.fastjson2.JSONObject; -import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.exception.ServiceException; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.media.bean.MediaInfo; -import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.service.bean.ErrorCallback; -import com.genersoft.iot.vmp.service.bean.SSRCInfo; -import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; -import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; -import gov.nist.javax.sip.message.SIPResponse; - -import javax.sip.InvalidArgumentException; -import javax.sip.SipException; -import javax.sip.header.CallIdHeader; -import java.text.ParseException; -import java.util.Map; - -/** - * 点播处理 - */ -public interface IPlayService { - - void play(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channelId, - ErrorCallback callback); - SSRCInfo play(MediaServer mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback callback); - - StreamInfo onPublishHandlerForPlay(MediaServer mediaServerItem, MediaInfo mediaInfo, String deviceId, String channelId); - - MediaServer getNewMediaServerItem(Device device); - - void playBack(String deviceId, String channelId, String startTime, String endTime, ErrorCallback callback); - void playBack(MediaServer mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, ErrorCallback callback); - void zlmServerOffline(String mediaServerId); - - void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback callback); - void download(MediaServer mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback callback); - - StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream); - - void zlmServerOnline(String mediaServerId); - - AudioBroadcastResult audioBroadcast(Device device, String channelId, Boolean broadcastMode); - - boolean audioBroadcastCmd(Device device, String channelId, MediaServer mediaServerItem, String app, String stream, int timeout, boolean isFromPlatform, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException; - - boolean audioBroadcastInUse(Device device, String channelId); - - void stopAudioBroadcast(String deviceId, String channelId); - - void pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; - - void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; - - void startPushStream(SendRtpItem sendRtpItem, SIPResponse sipResponse, ParentPlatform platform, CallIdHeader callIdHeader); - - void startSendRtpStreamFailHand(SendRtpItem sendRtpItem,ParentPlatform platform, CallIdHeader callIdHeader); - - void talkCmd(Device device, String channelId, MediaServer mediaServerItem, String stream, AudioBroadcastEvent event); - - void stopTalk(Device device, String channelId, Boolean streamIsReady); - - void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback); - - void stopPlay(Device device, String channelId); -} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IReceiveRtpServerService.java b/src/main/java/com/genersoft/iot/vmp/service/IReceiveRtpServerService.java new file mode 100644 index 000000000..caf2b9044 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IReceiveRtpServerService.java @@ -0,0 +1,13 @@ +package com.genersoft.iot.vmp.service; + +import com.genersoft.iot.vmp.gb28181.bean.OpenRTPServerResult; +import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.service.bean.ErrorCallback; +import com.genersoft.iot.vmp.service.bean.RTPServerParam; +import com.genersoft.iot.vmp.service.bean.SSRCInfo; + +public interface IReceiveRtpServerService { + SSRCInfo openRTPServer(RTPServerParam rtpServerParam, ErrorCallback callback); + + void closeRTPServer(MediaServer mediaServer, SSRCInfo ssrcInfo); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/ISendRtpServerService.java b/src/main/java/com/genersoft/iot/vmp/service/ISendRtpServerService.java new file mode 100644 index 000000000..8b05d3a4c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/ISendRtpServerService.java @@ -0,0 +1,43 @@ +package com.genersoft.iot.vmp.service; + +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; +import com.genersoft.iot.vmp.media.bean.MediaServer; + +import java.util.List; + +public interface ISendRtpServerService { + + SendRtpInfo createSendRtpInfo(MediaServer mediaServer, String ip, Integer port, String ssrc, String requesterId, + String deviceId, Integer channelId, Boolean isTcp, Boolean rtcp); + + SendRtpInfo createSendRtpInfo(MediaServer mediaServer, String ip, Integer port, String ssrc, String platformId, + String app, String stream, Integer channelId, Boolean tcp, Boolean rtcp); + + void update(SendRtpInfo sendRtpItem); + + SendRtpInfo queryByChannelId(Integer channelId, String targetId); + + SendRtpInfo queryByCallId(String callId); + + List queryByStream(String stream); + + SendRtpInfo queryByStream(String stream, String targetId); + + void delete(SendRtpInfo sendRtpInfo); + + void deleteByCallId(String callId); + + void deleteByStream(String Stream, String targetId); + + void deleteByChannel(Integer channelId, String targetId); + + List queryAll(); + + boolean isChannelSendingRTP(Integer channelId); + + List queryForPlatform(String platformId); + + List queryByChannelId(int id); + + void deleteByStream(String stream); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java b/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java deleted file mode 100755 index e315d927a..000000000 --- a/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.genersoft.iot.vmp.service; - -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; -import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; -import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis; -import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; -import com.github.pagehelper.PageInfo; - -import java.util.List; -import java.util.Map; - -/** - * @author lin - */ -public interface IStreamPushService { - - /** - * 将应用名和流ID加入国标关联 - * @param stream - * @return - */ - boolean saveToGB(GbStream stream); - - /** - * 将应用名和流ID移出国标关联 - * @param stream - * @return - */ - boolean removeFromGB(GbStream stream); - - /** - * 获取 - */ - PageInfo getPushList(Integer page, Integer count, String query, Boolean pushing, String mediaServerId); - - List getPushList(String mediaSererId); - - StreamPushItem transform(OnStreamChangedHookParam item); - - StreamPushItem getPush(String app, String streamId); - - /** - * 停止一路推流 - * @param app 应用名 - * @param stream 流ID - */ - boolean stop(String app, String stream); - - /** - * 新的节点加入 - */ - void zlmServerOnline(String mediaServerId); - - /** - * 节点离线 - */ - void zlmServerOffline(String mediaServerId); - - /** - * 清空 - */ - void clean(); - - - boolean saveToRandomGB(); - - /** - * 批量添加 - */ - void batchAdd(List streamPushExcelDtoList); - - /** - * 中止多个推流 - */ - boolean batchStop(List streamPushItems); - - /** - * 导入时批量增加 - */ - void batchAddForUpload(List streamPushItems, Map> streamPushItemsForAll); - - /** - * 全部离线 - */ - void allStreamOffline(); - - /** - * 推流离线 - */ - void offline(List offlineStreams); - - /** - * 推流上线 - */ - void online(List onlineStreams); - - /** - * 增加推流 - */ - boolean add(StreamPushItem stream); - - /** - * 获取全部的app+Streanm 用于判断推流列表是新增还是修改 - * @return - */ - List getAllAppAndStream(); - - /** - * 获取统计信息 - * @return - */ - ResourceBaseInfo getOverview(); - - Map getAllAppAndStreamMap(); - - - void updatePush(OnStreamChangedHookParam param); -} diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/GPSMsgInfo.java b/src/main/java/com/genersoft/iot/vmp/service/bean/GPSMsgInfo.java index 3b84420ff..7156985c6 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/bean/GPSMsgInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/GPSMsgInfo.java @@ -2,14 +2,21 @@ package com.genersoft.iot.vmp.service.bean; import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; import com.genersoft.iot.vmp.utils.DateUtil; +import lombok.Data; +@Data public class GPSMsgInfo { /** - * + * 通道国标ID */ private String id; + /** + * 通道ID + */ + private Integer channelId; + /** * 经度 (必选) */ @@ -44,7 +51,7 @@ public class GPSMsgInfo { public static GPSMsgInfo getInstance(MobilePosition mobilePosition) { GPSMsgInfo gpsMsgInfo = new GPSMsgInfo(); - gpsMsgInfo.setId(mobilePosition.getChannelId()); + gpsMsgInfo.setChannelId(mobilePosition.getChannelId()); gpsMsgInfo.setAltitude(mobilePosition.getAltitude() + ""); gpsMsgInfo.setLng(mobilePosition.getLongitude()); gpsMsgInfo.setLat(mobilePosition.getLatitude()); @@ -53,69 +60,4 @@ public class GPSMsgInfo { gpsMsgInfo.setTime(DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(mobilePosition.getTime())); return gpsMsgInfo; } - - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public double getLng() { - return lng; - } - - public void setLng(double lng) { - this.lng = lng; - } - - public double getLat() { - return lat; - } - - public void setLat(double lat) { - this.lat = lat; - } - - public double getSpeed() { - return speed; - } - - public void setSpeed(double speed) { - this.speed = speed; - } - - public String getTime() { - return time; - } - - public void setTime(String time) { - this.time = time; - } - - public String getDirection() { - return direction; - } - - public void setDirection(String direction) { - this.direction = direction; - } - - public String getAltitude() { - return altitude; - } - - public void setAltitude(String altitude) { - this.altitude = altitude; - } - - public boolean isStored() { - return stored; - } - - public void setStored(boolean stored) { - this.stored = stored; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannel.java b/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannel.java index 6a4f866ca..3c8aba529 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannel.java +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannel.java @@ -1,9 +1,13 @@ package com.genersoft.iot.vmp.service.bean; +import lombok.Data; + /** * 当上级平台 * @author lin */ + +@Data public class MessageForPushChannel { /** * 消息类型 @@ -67,77 +71,4 @@ public class MessageForPushChannel { messageForPushChannel.setPlatFormName(platFormName); return messageForPushChannel; } - - - public int getType() { - return type; - } - - public void setType(int type) { - this.type = type; - } - - 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 getGbId() { - return gbId; - } - - public void setGbId(String gbId) { - this.gbId = gbId; - } - - public String getPlatFormId() { - return platFormId; - } - - public void setPlatFormId(String platFormId) { - this.platFormId = platFormId; - } - - public String getPlatFormName() { - return platFormName; - } - - public void setPlatFormName(String platFormName) { - this.platFormName = platFormName; - } - - public String getServerId() { - return serverId; - } - - public void setServerId(String serverId) { - this.serverId = serverId; - } - - public String getMediaServerId() { - return mediaServerId; - } - - public void setMediaServerId(String mediaServerId) { - this.mediaServerId = mediaServerId; - } - - public int getPlatFormIndex() { - return platFormIndex; - } - - public void setPlatFormIndex(int platFormIndex) { - this.platFormIndex = platFormIndex; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/PushStreamStatusChangeFromRedisDto.java b/src/main/java/com/genersoft/iot/vmp/service/bean/PushStreamStatusChangeFromRedisDto.java index 5bb7b77f8..9e9ce3514 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/bean/PushStreamStatusChangeFromRedisDto.java +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/PushStreamStatusChangeFromRedisDto.java @@ -1,11 +1,14 @@ package com.genersoft.iot.vmp.service.bean; +import lombok.Data; + import java.util.List; /** * 收到redis通知修改推流通道状态 * @author lin */ +@Data public class PushStreamStatusChangeFromRedisDto { private boolean setAllOffline; @@ -13,29 +16,4 @@ public class PushStreamStatusChangeFromRedisDto { private List onlineStreams; private List offlineStreams; - - - public boolean isSetAllOffline() { - return setAllOffline; - } - - public void setSetAllOffline(boolean setAllOffline) { - this.setAllOffline = setAllOffline; - } - - public List getOnlineStreams() { - return onlineStreams; - } - - public void setOnlineStreams(List onlineStreams) { - this.onlineStreams = onlineStreams; - } - - public List getOfflineStreams() { - return offlineStreams; - } - - public void setOfflineStreams(List offlineStreams) { - this.offlineStreams = offlineStreams; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/RTPServerParam.java b/src/main/java/com/genersoft/iot/vmp/service/bean/RTPServerParam.java new file mode 100644 index 000000000..2baf335a8 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/RTPServerParam.java @@ -0,0 +1,25 @@ +package com.genersoft.iot.vmp.service.bean; + +import com.genersoft.iot.vmp.media.bean.MediaServer; +import lombok.Data; + +@Data +public class RTPServerParam { + + private MediaServer mediaServerItem; + private String streamId; + private String presetSsrc; + private boolean ssrcCheck; + private boolean playback; + private Integer port; + private boolean onlyAuto; + private boolean disableAudio; + private boolean reUsePort; + + /** + * tcp模式,0时为不启用tcp监听,1时为启用tcp监听,2时为tcp主动连接模式 + */ + private Integer tcpMode; + + +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/RequestPushStreamMsg.java b/src/main/java/com/genersoft/iot/vmp/service/bean/RequestPushStreamMsg.java index 9b446f68c..84ee7ba31 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/bean/RequestPushStreamMsg.java +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/RequestPushStreamMsg.java @@ -1,6 +1,6 @@ package com.genersoft.iot.vmp.service.bean; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; /** * redis消息:请求下级推送流信息 @@ -82,7 +82,7 @@ public class RequestPushStreamMsg { return requestPushStreamMsg; } - public static RequestPushStreamMsg getInstance(SendRtpItem sendRtpItem) { + public static RequestPushStreamMsg getInstance(SendRtpInfo sendRtpItem) { RequestPushStreamMsg requestPushStreamMsg = new RequestPushStreamMsg(); requestPushStreamMsg.setMediaServerId(sendRtpItem.getMediaServerId()); requestPushStreamMsg.setApp(sendRtpItem.getApp()); diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/RequestStopPushStreamMsg.java b/src/main/java/com/genersoft/iot/vmp/service/bean/RequestStopPushStreamMsg.java index fcba51118..a63d916d1 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/bean/RequestStopPushStreamMsg.java +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/RequestStopPushStreamMsg.java @@ -1,12 +1,12 @@ package com.genersoft.iot.vmp.service.bean; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; public class RequestStopPushStreamMsg { - private SendRtpItem sendRtpItem; + private SendRtpInfo sendRtpItem; private String platformName; @@ -14,11 +14,11 @@ public class RequestStopPushStreamMsg { private int platFormIndex; - public SendRtpItem getSendRtpItem() { + public SendRtpInfo getSendRtpItem() { return sendRtpItem; } - public void setSendRtpItem(SendRtpItem sendRtpItem) { + public void setSendRtpItem(SendRtpInfo sendRtpItem) { this.sendRtpItem = sendRtpItem; } @@ -39,7 +39,7 @@ public class RequestStopPushStreamMsg { this.platFormIndex = platFormIndex; } - public static RequestStopPushStreamMsg getInstance(SendRtpItem sendRtpItem, String platformName, int platFormIndex) { + public static RequestStopPushStreamMsg getInstance(SendRtpInfo sendRtpItem, String platformName, int platFormIndex) { RequestStopPushStreamMsg streamMsg = new RequestStopPushStreamMsg(); streamMsg.setSendRtpItem(sendRtpItem); streamMsg.setPlatformName(platformName); diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/ResponseSendItemMsg.java b/src/main/java/com/genersoft/iot/vmp/service/bean/ResponseSendItemMsg.java index 9d733845f..868009996 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/bean/ResponseSendItemMsg.java +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/ResponseSendItemMsg.java @@ -1,6 +1,6 @@ package com.genersoft.iot.vmp.service.bean; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; /** @@ -9,15 +9,15 @@ import com.genersoft.iot.vmp.media.bean.MediaServer; */ public class ResponseSendItemMsg { - private SendRtpItem sendRtpItem; + private SendRtpInfo sendRtpItem; private MediaServer mediaServerItem; - public SendRtpItem getSendRtpItem() { + public SendRtpInfo getSendRtpItem() { return sendRtpItem; } - public void setSendRtpItem(SendRtpItem sendRtpItem) { + public void setSendRtpItem(SendRtpInfo sendRtpItem) { this.sendRtpItem = sendRtpItem; } diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java b/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java index 1723bc594..ac0e2b3b6 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java @@ -1,38 +1,19 @@ package com.genersoft.iot.vmp.service.bean; +import lombok.Data; + +@Data public class SSRCInfo { private int port; private String ssrc; private String Stream; + private String timeOutTaskKey; - public SSRCInfo(int port, String ssrc, String stream) { + public SSRCInfo(int port, String ssrc, String stream, String timeOutTaskKey) { this.port = port; this.ssrc = ssrc; - Stream = stream; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public String getSsrc() { - return ssrc; - } - - public void setSsrc(String ssrc) { - this.ssrc = ssrc; - } - - public String getStream() { - return Stream; - } - - public void setStream(String stream) { - Stream = stream; + this.Stream = stream; + this.timeOutTaskKey = timeOutTaskKey; } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java index a7b582d78..4c521e449 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java @@ -4,13 +4,12 @@ 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.gb28181.service.ICloudRecordService; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.event.media.MediaRecordMp4Event; import com.genersoft.iot.vmp.media.service.IMediaServerService; import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils; import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; -import com.genersoft.iot.vmp.service.ICloudRecordService; import com.genersoft.iot.vmp.service.bean.CloudRecordItem; import com.genersoft.iot.vmp.service.bean.DownloadFileInfo; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; @@ -20,13 +19,13 @@ 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 lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; +import org.springframework.util.Assert; import java.time.LocalDate; import java.time.ZoneOffset; @@ -35,13 +34,11 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +@Slf4j @Service @DS("share") public class CloudRecordServiceImpl implements ICloudRecordService { - - private final static Logger logger = LoggerFactory.getLogger(CloudRecordServiceImpl.class); - @Autowired private CloudRecordServiceMapper cloudRecordServiceMapper; @@ -54,9 +51,6 @@ public class CloudRecordServiceImpl implements ICloudRecordService { @Autowired private AssistRESTfulUtils assistRESTfulUtils; - @Autowired - private VideoStreamSessionManager streamSession; - @Override public PageInfo getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List mediaServerItems, String callId) { // 开始时间和结束时间在数据库中都是以秒为单位的 @@ -91,8 +85,8 @@ public class CloudRecordServiceImpl implements ICloudRecordService { }else { endDate = LocalDate.of(year, month + 1, 1); } - long startTimeStamp = startDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).getEpochSecond() * 1000; - long endTimeStamp = endDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).getEpochSecond() * 1000; + long startTimeStamp = startDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).toEpochMilli(); + long endTimeStamp = endDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).toEpochMilli(); List cloudRecordItemList = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp, endTimeStamp, null, mediaServerItems, null); if (cloudRecordItemList.isEmpty()) { @@ -116,7 +110,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService { cloudRecordItem.setCallId(streamAuthorityInfo.getCallId()); } } - logger.info("[添加录像记录] {}/{}, callId: {}, 内容:{}", event.getApp(), event.getStream(), cloudRecordItem.getCallId(), event.getRecordInfo()); + log.info("[添加录像记录] {}/{}, callId: {}, 内容:{}", event.getApp(), event.getStream(), cloudRecordItem.getCallId(), event.getRecordInfo()); cloudRecordServiceMapper.add(cloudRecordItem); } @@ -124,8 +118,8 @@ public class CloudRecordServiceImpl implements ICloudRecordService { public String addTask(String app, String stream, MediaServer mediaServerItem, String startTime, String endTime, String callId, String remoteHost, boolean filterMediaServer) { // 参数校验 - assert app != null; - assert stream != null; + Assert.notNull(app,"应用名为NULL"); + Assert.notNull(stream,"流ID为NULL"); if (mediaServerItem.getRecordAssistPort() == 0) { throw new ControllerException(ErrorCode.ERROR100.getCode(), "为配置Assist服务"); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java deleted file mode 100755 index 75cfb1251..000000000 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java +++ /dev/null @@ -1,429 +0,0 @@ -package com.genersoft.iot.vmp.service.impl; - -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.InviteSessionType; -import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.utils.Coordtransform; -import com.genersoft.iot.vmp.service.IDeviceChannelService; -import com.genersoft.iot.vmp.service.IInviteStreamService; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; -import com.genersoft.iot.vmp.storager.dao.DeviceMapper; -import com.genersoft.iot.vmp.storager.dao.DeviceMobilePositionMapper; -import com.genersoft.iot.vmp.utils.DateUtil; -import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.ObjectUtils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * @author lin - */ -@Service -@DS("master") -public class DeviceChannelServiceImpl implements IDeviceChannelService { - - private final static Logger logger = LoggerFactory.getLogger(DeviceChannelServiceImpl.class); - - @Autowired - private EventPublisher eventPublisher; - - @Autowired - private IInviteStreamService inviteStreamService; - - @Autowired - private DeviceChannelMapper channelMapper; - - @Autowired - private DeviceMapper deviceMapper; - - @Autowired - private DeviceMobilePositionMapper deviceMobilePositionMapper; - - @Autowired - private UserSetting userSetting; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Override - public DeviceChannel updateGps(DeviceChannel deviceChannel, Device device) { - if (deviceChannel.getLongitude()*deviceChannel.getLatitude() > 0) { - if (device == null) { - device = deviceMapper.getDeviceByDeviceId(deviceChannel.getDeviceId()); - } - - if ("WGS84".equals(device.getGeoCoordSys())) { - deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); - deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); - Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude()); - deviceChannel.setLongitudeGcj02(position[0]); - deviceChannel.setLatitudeGcj02(position[1]); - }else if ("GCJ02".equals(device.getGeoCoordSys())) { - deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); - deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); - Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude()); - deviceChannel.setLongitudeWgs84(position[0]); - deviceChannel.setLatitudeWgs84(position[1]); - }else { - deviceChannel.setLongitudeGcj02(0.00); - deviceChannel.setLatitudeGcj02(0.00); - deviceChannel.setLongitudeWgs84(0.00); - deviceChannel.setLatitudeWgs84(0.00); - } - }else { - deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); - deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); - deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); - deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); - } - return deviceChannel; - } - - @Override - public void updateChannel(String deviceId, DeviceChannel channel) { - String channelId = channel.getChannelId(); - channel.setDeviceId(deviceId); - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); - if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { - channel.setStreamId(inviteInfo.getStreamInfo().getStream()); - } - String now = DateUtil.getNow(); - channel.setUpdateTime(now); - DeviceChannel deviceChannel = channelMapper.queryChannel(deviceId, channelId); - channel = updateGps(channel, null); - if (deviceChannel == null) { - channel.setCreateTime(now); - channelMapper.add(channel); - }else { - channelMapper.update(channel); - } - channelMapper.updateChannelSubCount(deviceId,channel.getParentId()); - } - - @Override - public int updateChannels(String deviceId, List channels) { - List addChannels = new ArrayList<>(); - List updateChannels = new ArrayList<>(); - HashMap channelsInStore = new HashMap<>(); - Device device = deviceMapper.getDeviceByDeviceId(deviceId); - if (channels != null && channels.size() > 0) { - List channelList = channelMapper.queryChannels(deviceId, null, null, null, null,null); - if (channelList.size() == 0) { - for (DeviceChannel channel : channels) { - channel.setDeviceId(deviceId); - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channel.getChannelId()); - if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { - channel.setStreamId(inviteInfo.getStreamInfo().getStream()); - } - String now = DateUtil.getNow(); - channel.setUpdateTime(now); - channel.setCreateTime(now); - channel = updateGps(channel, device); - addChannels.add(channel); - } - }else { - for (DeviceChannel deviceChannel : channelList) { - channelsInStore.put(deviceChannel.getChannelId(), deviceChannel); - } - for (DeviceChannel channel : channels) { - channel.setDeviceId(deviceId); - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channel.getChannelId()); - if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { - channel.setStreamId(inviteInfo.getStreamInfo().getStream()); - } - String now = DateUtil.getNow(); - channel.setUpdateTime(now); - channel = updateGps(channel, device); - if (channelsInStore.get(channel.getChannelId()) != null) { - updateChannels.add(channel); - }else { - addChannels.add(channel); - channel.setCreateTime(now); - } - } - } - int limitCount = 50; - if (addChannels.size() > 0) { - if (addChannels.size() > limitCount) { - for (int i = 0; i < addChannels.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > addChannels.size()) { - toIndex = addChannels.size(); - } - channelMapper.batchAdd(addChannels.subList(i, toIndex)); - } - }else { - channelMapper.batchAdd(addChannels); - } - } - if (updateChannels.size() > 0) { - if (updateChannels.size() > limitCount) { - for (int i = 0; i < updateChannels.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > updateChannels.size()) { - toIndex = updateChannels.size(); - } - channelMapper.batchUpdate(updateChannels.subList(i, toIndex)); - } - }else { - channelMapper.batchUpdate(updateChannels); - } - } - } - return addChannels.size() + updateChannels.size(); - } - - @Override - public ResourceBaseInfo getOverview() { - - int online = channelMapper.getOnlineCount(); - int total = channelMapper.getAllChannelCount(); - - return new ResourceBaseInfo(total, online); - } - - - @Override - public List queryAllChannelList(String platformId) { - return channelMapper.queryChannelListInAll(null, null, null, platformId, null); - } - - @Override - public boolean updateAllGps(Device device) { - List deviceChannels = channelMapper.getChannelsWithoutTransform(device.getDeviceId()); - List result = new CopyOnWriteArrayList<>(); - if (deviceChannels.size() == 0) { - return true; - } - String now = DateUtil.getNow(); - deviceChannels.parallelStream().forEach(deviceChannel -> { - deviceChannel.setUpdateTime(now); - result.add(updateGps(deviceChannel, device)); - }); - int limitCount = 50; - if (result.size() > limitCount) { - for (int i = 0; i < result.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > result.size()) { - toIndex = result.size(); - } - channelMapper.batchUpdate(result.subList(i, toIndex)); - } - }else { - channelMapper.batchUpdate(result); - } - - return true; - } - - @Override - public List getDeviceByChannelId(String channelId) { - - return channelMapper.getDeviceByChannelId(channelId); - } - - @Override - public int deleteChannels(List deleteChannelList) { - return channelMapper.batchDel(deleteChannelList); - } - - @Override - public int channelsOnline(List channels) { - return channelMapper.batchOnline(channels); - } - - @Override - public void online(DeviceChannel channel) { - channelMapper.online(channel.getDeviceId(), channel.getChannelId()); - } - - @Override - public int channelsOffline(List channels) { - return channelMapper.batchOffline(channels); - } - - - @Override - public void offline(DeviceChannel channel) { - channelMapper.offline(channel.getDeviceId(), channel.getChannelId()); - } - - @Override - public void delete(DeviceChannel channel) { - channelMapper.del(channel.getDeviceId(), channel.getChannelId()); - } - - @Override - public DeviceChannel getOne(String deviceId, String channelId){ - return channelMapper.queryChannel(deviceId, channelId); - } - - @Override - public synchronized void batchUpdateChannel(List channels) { - String now = DateUtil.getNow(); - for (DeviceChannel channel : channels) { - channel.setUpdateTime(now); - } - int limitCount = 1000; - if (!channels.isEmpty()) { - if (channels.size() > limitCount) { - for (int i = 0; i < channels.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > channels.size()) { - toIndex = channels.size(); - } - channelMapper.batchUpdate(channels.subList(i, toIndex)); - } - }else { - channelMapper.batchUpdate(channels); - } - } - } - - @Override - public void batchAddChannel(List channels) { - channelMapper.batchAdd(channels); - for (DeviceChannel channel : channels) { - if (channel.getParentId() != null) { - channelMapper.updateChannelSubCount(channel.getDeviceId(), channel.getParentId()); - } - } - } - - @Override - public void updateChannelStreamIdentification(DeviceChannel channel) { - assert !ObjectUtils.isEmpty(channel.getDeviceId()); - assert !ObjectUtils.isEmpty(channel.getStreamIdentification()); - if (ObjectUtils.isEmpty(channel.getStreamIdentification())) { - logger.info("[重置通道码流类型] 设备: {}, 码流: {}", channel.getDeviceId(), channel.getStreamIdentification()); - }else { - logger.info("[更新通道码流类型] 设备: {}, 通道:{}, 码流: {}", channel.getDeviceId(), channel.getChannelId(), - channel.getStreamIdentification()); - } - channelMapper.updateChannelStreamIdentification(channel); - } - - @Override - public List queryChaneListByDeviceId(String deviceId) { - return channelMapper.queryAllChannels(deviceId); - } - - @Override - public void updateChannelGPS(Device device, DeviceChannel deviceChannel, MobilePosition mobilePosition) { - if (userSetting.getSavePositionHistory()) { - deviceMobilePositionMapper.insertNewPosition(mobilePosition); - } - - if (deviceChannel.getChannelId().equals(deviceChannel.getDeviceId())) { - deviceChannel.setChannelId(null); - } - if (deviceChannel.getGpsTime() == null) { - deviceChannel.setGpsTime(DateUtil.getNow()); - } - - int updated = channelMapper.updatePosition(deviceChannel); - if (updated == 0) { - return; - } - - List deviceChannels = new ArrayList<>(); - if (deviceChannel.getChannelId() == null) { - // 有的设备这里上报的deviceId与通道Id是一样,这种情况更新设备下的全部通道 - List deviceChannelsInDb = queryChaneListByDeviceId(device.getDeviceId()); - deviceChannels.addAll(deviceChannelsInDb); - }else { - deviceChannels.add(deviceChannel); - } - if (deviceChannels.isEmpty()) { - return; - } - if (deviceChannels.size() > 100) { - logger.warn("[更新通道位置信息后发送通知] 设备可能是平台,上报的位置信息未标明通道编号," + - "导致所有通道被更新位置, deviceId:{}", device.getDeviceId()); - } - for (DeviceChannel channel : deviceChannels) { - // 向关联了该通道并且开启移动位置订阅的上级平台发送移动位置订阅消息 - mobilePosition.setChannelId(channel.getChannelId()); - try { - eventPublisher.mobilePositionEventPublish(mobilePosition); - }catch (Exception e) { - logger.error("[向上级转发移动位置失败] ", e); - } - // 发送redis消息。 通知位置信息的变化 - JSONObject jsonObject = new JSONObject(); - jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(mobilePosition.getTime())); - jsonObject.put("serial", mobilePosition.getDeviceId()); - jsonObject.put("code", mobilePosition.getChannelId()); - jsonObject.put("longitude", mobilePosition.getLongitude()); - jsonObject.put("latitude", mobilePosition.getLatitude()); - jsonObject.put("altitude", mobilePosition.getAltitude()); - jsonObject.put("direction", mobilePosition.getDirection()); - jsonObject.put("speed", mobilePosition.getSpeed()); - redisCatchStorage.sendMobilePositionMsg(jsonObject); - } - } - - @Override - public void stopPlay(String deviceId, String channelId) { - channelMapper.stopPlay(deviceId, channelId); - } - - @Override - @Transactional - public void batchUpdateChannelGPS(List channelList) { - for (DeviceChannel deviceChannel : channelList) { - deviceChannel.setUpdateTime(DateUtil.getNow()); - if (deviceChannel.getGpsTime() == null) { - deviceChannel.setGpsTime(DateUtil.getNow()); - } - } - int count = 1000; - if (channelList.size() > count) { - for (int i = 0; i < channelList.size(); i+=count) { - int toIndex = i+count; - if ( i + count > channelList.size()) { - toIndex = channelList.size(); - } - List channels = channelList.subList(i, toIndex); - channelMapper.batchUpdatePosition(channels); - } - }else { - channelMapper.batchUpdatePosition(channelList); - } - } - - @Override - @Transactional - public void batchAddMobilePosition(List mobilePositions) { -// int count = 500; -// if (mobilePositions.size() > count) { -// for (int i = 0; i < mobilePositions.size(); i+=count) { -// int toIndex = i+count; -// if ( i + count > mobilePositions.size()) { -// toIndex = mobilePositions.size(); -// } -// List mobilePositionsSub = mobilePositions.subList(i, toIndex); -// deviceMobilePositionMapper.batchadd(mobilePositionsSub); -// } -// }else { -// deviceMobilePositionMapper.batchadd(mobilePositions); -// } - deviceMobilePositionMapper.batchadd(mobilePositions); - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java deleted file mode 100755 index c2c9d725a..000000000 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java +++ /dev/null @@ -1,276 +0,0 @@ -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; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; -import com.genersoft.iot.vmp.service.IGbStreamService; -import com.genersoft.iot.vmp.storager.dao.GbStreamMapper; -import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper; -import com.genersoft.iot.vmp.storager.dao.PlatformCatalogMapper; -import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper; -import com.github.pagehelper.PageHelper; -import com.github.pagehelper.PageInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.datasource.DataSourceTransactionManager; -import org.springframework.stereotype.Service; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.util.ObjectUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -@Service -@DS("master") -public class GbStreamServiceImpl implements IGbStreamService { - - private final static Logger logger = LoggerFactory.getLogger(GbStreamServiceImpl.class); - - @Autowired - DataSourceTransactionManager dataSourceTransactionManager; - - @Autowired - TransactionDefinition transactionDefinition; - - @Autowired - private GbStreamMapper gbStreamMapper; - - @Autowired - private PlatformGbStreamMapper platformGbStreamMapper; - - @Autowired - private SubscribeHolder subscribeHolder; - - @Autowired - private ParentPlatformMapper platformMapper; - - @Autowired - private PlatformCatalogMapper catalogMapper; - - @Autowired - private EventPublisher eventPublisher; - - @Override - public PageInfo getAll(Integer page, Integer count, String platFormId, String catalogId, String query, String mediaServerId) { - PageHelper.startPage(page, count); - List all = gbStreamMapper.selectAll(platFormId, catalogId, query, mediaServerId); - return new PageInfo<>(all); - } - - @Override - public void del(String app, String stream) { - gbStreamMapper.del(app, stream); - } - - - @Override - public boolean addPlatformInfo(List gbStreams, String platformId, String catalogId) { - // 放在事务内执行 - boolean result = false; - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); - ParentPlatform parentPlatform = platformMapper.getParentPlatByServerGBId(platformId); - if (catalogId == null) { - catalogId = parentPlatform.getCatalogId(); - } - try { - List deviceChannelList = new ArrayList<>(); - for (int i = 0; i < gbStreams.size(); i++) { - GbStream gbStream = gbStreams.get(i); - gbStream.setCatalogId(catalogId); - gbStream.setPlatformId(platformId); - // TODO 修改为批量提交 - platformGbStreamMapper.add(gbStream); - logger.info("[关联通道]直播流通道 平台:{}, 共需关联通道数:{}, 已关联:{}", platformId, gbStreams.size(), i + 1); - DeviceChannel deviceChannelListByStream = getDeviceChannelListByStreamWithStatus(gbStream, catalogId, parentPlatform); - deviceChannelList.add(deviceChannelListByStream); - } - dataSourceTransactionManager.commit(transactionStatus); //手动提交 - if (subscribeHolder.getCatalogSubscribe(platformId) != null) { - eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD); - } - - result = true; - }catch (Exception e) { - logger.error("批量保存流与平台的关系时错误", e); - dataSourceTransactionManager.rollback(transactionStatus); - } - return result; - } - - @Override - public DeviceChannel getDeviceChannelListByStream(GbStream gbStream, String catalogId, ParentPlatform platform) { - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setChannelId(gbStream.getGbId()); - deviceChannel.setName(gbStream.getName()); - deviceChannel.setLongitude(gbStream.getLongitude()); - deviceChannel.setLatitude(gbStream.getLatitude()); - deviceChannel.setDeviceId(platform.getDeviceGBId()); - deviceChannel.setManufacture("wvp-pro"); - deviceChannel.setStatus(gbStream.isStatus()); - - deviceChannel.setRegisterWay(1); - - PlatformCatalog catalog = catalogMapper.selectByPlatFormAndCatalogId(platform.getServerGBId(), catalogId); - if (catalog != null) { - deviceChannel.setCivilCode(catalog.getCivilCode()); - deviceChannel.setParentId(catalog.getParentId()); - deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); - }else { - deviceChannel.setCivilCode(platform.getAdministrativeDivision()); - deviceChannel.setParentId(platform.getDeviceGBId()); - } - - deviceChannel.setModel("live"); - deviceChannel.setOwner("wvp-pro"); - deviceChannel.setParental(0); - deviceChannel.setSecrecy("0"); - return deviceChannel; - } - - @Override - public boolean delPlatformInfo(String platformId, List gbStreams) { - // 放在事务内执行 - boolean result = false; - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); - try { - List deviceChannelList = new ArrayList<>(); - platformGbStreamMapper.delByAppAndStreamsByPlatformId(gbStreams, platformId); - for (GbStream gbStream : gbStreams) { - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setChannelId(gbStream.getGbId()); - deviceChannelList.add(deviceChannel); - } - - eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL); - dataSourceTransactionManager.commit(transactionStatus); //手动提交 - result = true; - }catch (Exception e) { - logger.error("批量移除流与平台的关系时错误", e); - dataSourceTransactionManager.rollback(transactionStatus); - } - return result; - } - - @Override - public void sendCatalogMsg(GbStream gbStream, String type) { - if (gbStream == null || type == null) { - logger.warn("[发送目录订阅]类型:流信息或类型为NULL"); - return; - } - List gbStreams = new ArrayList<>(); - if (gbStream.getGbId() != null) { - gbStreams.add(gbStream); - }else { - GbStream gbStreamIndb = gbStreamMapper.selectOne(gbStream.getApp(), gbStream.getStream()); - if (gbStreamIndb != null && gbStreamIndb.getGbId() != null){ - gbStreams.add(gbStreamIndb); - } - } - sendCatalogMsgs(gbStreams, type); - } - - @Override - public void sendCatalogMsgs(List gbStreams, String type) { - if (gbStreams.size() > 0) { - for (GbStream gs : gbStreams) { - if (ObjectUtils.isEmpty(gs.getGbId())){ - continue; - } - List parentPlatforms = platformGbStreamMapper.selectByAppAndStream(gs.getApp(), gs.getStream()); - if (parentPlatforms.size() > 0) { - for (ParentPlatform parentPlatform : parentPlatforms) { - if (parentPlatform != null) { - eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), gs, type); - } - } - } - } - } - } - - @Override - public int updateGbIdOrName(List streamPushItemForUpdate) { - return gbStreamMapper.updateGbIdOrName(streamPushItemForUpdate); - } - - @Override - public DeviceChannel getDeviceChannelListByStreamWithStatus(GbStream gbStream, String catalogId, ParentPlatform platform) { - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setChannelId(gbStream.getGbId()); - deviceChannel.setName(gbStream.getName()); - deviceChannel.setLongitude(gbStream.getLongitude()); - deviceChannel.setLatitude(gbStream.getLatitude()); - deviceChannel.setDeviceId(platform.getDeviceGBId()); - deviceChannel.setManufacture("wvp-pro"); - // todo 目前是每一条查询一次,需要优化 - Boolean status = null; - if ("proxy".equals(gbStream.getStreamType())) { - status = gbStreamMapper.selectStatusForProxy(gbStream.getApp(), gbStream.getStream()); - }else { - status = gbStreamMapper.selectStatusForPush(gbStream.getApp(), gbStream.getStream()); - } - deviceChannel.setStatus(status != null && status); - - deviceChannel.setRegisterWay(1); - PlatformCatalog catalog = catalogMapper.selectByPlatFormAndCatalogId(platform.getServerGBId(), catalogId); - if (catalog != null) { - deviceChannel.setCivilCode(catalog.getCivilCode()); - deviceChannel.setParentId(catalog.getParentId()); - deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); - }else { - deviceChannel.setCivilCode(platform.getAdministrativeDivision()); - deviceChannel.setParentId(platform.getDeviceGBId()); - } - - deviceChannel.setModel("live"); - deviceChannel.setOwner("wvp-pro"); - deviceChannel.setParental(0); - deviceChannel.setSecrecy("0"); - return deviceChannel; - } - - @Override - public List getAllGBChannels(String platformId) { - - return gbStreamMapper.selectAll(platformId, null, null, null); - - } - - @Override - public void delAllPlatformInfo(String platformId, String catalogId) { - if (platformId == null) { - return ; - } - ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId); - if (platform == null) { - return ; - } - if (ObjectUtils.isEmpty(catalogId)) { - catalogId = null; - } - List gbStreams = platformGbStreamMapper.queryChannelInParentPlatformAndCatalog(platformId, catalogId); - List 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 - public List getGbChannelWithGbid(String gbId) { - return gbStreamMapper.selectByGBId(gbId); - } - - @Override - public Map getAllGBId() { - return gbStreamMapper.getAllGBId(); - } -} 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 87312a465..14d74ac6c 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 @@ -1,13 +1,16 @@ 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.common.InviteSessionType; import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.UserSetting; 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.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; +import com.genersoft.iot.vmp.gb28181.service.*; +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; +import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.jt1078.bean.JTMediaStreamType; @@ -15,32 +18,28 @@ import com.genersoft.iot.vmp.jt1078.service.Ijt1078Service; 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.media.zlm.dto.StreamProxyItem; -import com.genersoft.iot.vmp.service.*; +import com.genersoft.iot.vmp.service.IMediaService; +import com.genersoft.iot.vmp.service.ISendRtpServerService; +import com.genersoft.iot.vmp.service.IUserService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy; +import com.genersoft.iot.vmp.streamProxy.service.IStreamProxyService; import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.MediaServerUtils; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.OtherPsSendInfo; import com.genersoft.iot.vmp.vmanager.bean.OtherRtpSendInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; -import javax.sip.InvalidArgumentException; -import javax.sip.SipException; -import java.text.ParseException; -import java.util.List; import java.util.Map; +@Slf4j @Service public class MediaServiceImpl implements IMediaService { - private final static Logger logger = LoggerFactory.getLogger(MediaServiceImpl.class); - @Autowired private IRedisCatchStorage redisCatchStorage; @@ -60,10 +59,19 @@ public class MediaServiceImpl implements IMediaService { private IInviteStreamService inviteStreamService; @Autowired - private VideoStreamSessionManager sessionManager; + private SSRCFactory ssrcFactory; @Autowired - private IVideoManagerStorage storager; + private IDeviceChannelService deviceChannelService; + + @Autowired + private SipInviteSessionManager sessionManager; + + @Autowired + private IPlatformService platformService; + + @Autowired + private IGbChannelService channelService; @Autowired private IDeviceService deviceService; @@ -77,6 +85,9 @@ public class MediaServiceImpl implements IMediaService { @Autowired private Ijt1078Service ijt1078Service; + @Autowired + private ISendRtpServerService sendRtpServerService; + @Override public boolean authenticatePlay(String app, String stream, String callId) { if (app == null || stream == null) { @@ -96,7 +107,7 @@ public class MediaServiceImpl implements IMediaService { public ResultForOnPublish authenticatePublish(MediaServer mediaServer, String app, String stream, String params) { // 推流鉴权的处理 if (!"rtp".equals(app)) { - StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(app, stream); + StreamProxy streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(app, stream); if (streamProxyItem != null) { ResultForOnPublish result = new ResultForOnPublish(); result.setEnable_audio(streamProxyItem.isEnableAudio()); @@ -108,13 +119,13 @@ public class MediaServiceImpl implements IMediaService { Map paramMap = MediaServerUtils.urlParamToMap(params); // 推流鉴权 if (params == null) { - logger.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)"); + log.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)"); throw new ControllerException(ErrorCode.ERROR401.getCode(), "Unauthorized"); } String sign = paramMap.get("sign"); if (sign == null) { - logger.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)"); + log.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)"); throw new ControllerException(ErrorCode.ERROR401.getCode(), "Unauthorized"); } // 推流自定义播放鉴权码 @@ -122,7 +133,7 @@ public class MediaServiceImpl implements IMediaService { // 鉴权配置 boolean hasAuthority = userService.checkPushAuthority(callId, sign); if (!hasAuthority) { - logger.info("推流鉴权失败: sign 无权限: callId={}. sign={}", callId, sign); + log.info("推流鉴权失败: sign 无权限: callId={}. sign={}", callId, sign); throw new ControllerException(ErrorCode.ERROR401.getCode(), "Unauthorized"); } StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(app, stream, mediaServer.getId()); @@ -154,33 +165,33 @@ public class MediaServiceImpl implements IMediaService { inviteInfo = inviteStreamService.getInviteInfoBySSRC(ssrc); if (inviteInfo != null) { result.setStream_replace(inviteInfo.getStream()); - logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", stream, inviteInfo.getStream()); + log.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", stream, inviteInfo.getStream()); stream = inviteInfo.getStream(); } } // 设置音频信息及录制信息 - List ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, stream); - if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) { + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(stream); + if (ssrcTransaction != null ) { // 为录制国标模拟一个鉴权信息, 方便后续写入录像文件时使用 StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(app, stream, mediaServer.getId()); streamAuthorityInfo.setApp(app); - streamAuthorityInfo.setStream(ssrcTransactionForAll.get(0).getStream()); - streamAuthorityInfo.setCallId(ssrcTransactionForAll.get(0).getSipTransactionInfo().getCallId()); + streamAuthorityInfo.setStream(ssrcTransaction.getStream()); + streamAuthorityInfo.setCallId(ssrcTransaction.getSipTransactionInfo().getCallId()); - redisCatchStorage.updateStreamAuthorityInfo(app, ssrcTransactionForAll.get(0).getStream(), streamAuthorityInfo); + redisCatchStorage.updateStreamAuthorityInfo(app, ssrcTransaction.getStream(), streamAuthorityInfo); - String deviceId = ssrcTransactionForAll.get(0).getDeviceId(); - String channelId = ssrcTransactionForAll.get(0).getChannelId(); - DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); + String deviceId = ssrcTransaction.getDeviceId(); + Integer channelId = ssrcTransaction.getChannelId(); + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channelId); if (deviceChannel != null) { - result.setEnable_audio(deviceChannel.getHasAudio()); + result.setEnable_audio(deviceChannel.isHasAudio()); } // 如果是录像下载就设置视频间隔十秒 - if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) { + if (ssrcTransaction.getType() == InviteSessionType.DOWNLOAD) { // 获取录像的总时长,然后设置为这个视频的时长 - InviteInfo inviteInfoForDownload = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, stream); + InviteInfo inviteInfoForDownload = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, channelId, stream); if (inviteInfoForDownload != null && inviteInfoForDownload.getStreamInfo() != null) { String startTime = inviteInfoForDownload.getStreamInfo().getStartTime(); String endTime = inviteInfoForDownload.getStreamInfo().getEndTime(); @@ -192,7 +203,7 @@ public class MediaServiceImpl implements IMediaService { } } // 如果是talk对讲,则默认获取声音 - if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) { + if (ssrcTransaction.getType() == InviteSessionType.TALK) { result.setEnable_audio(true); } } @@ -223,54 +234,15 @@ public class MediaServiceImpl implements IMediaService { // 国标流, 点播/录像回放/录像下载 InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, stream); // 点播 - if (inviteInfo != null) { + if (inviteInfo != null && inviteInfo.getStatus() == InviteSessionStatus.ok) { // 录像下载 if (inviteInfo.getType() == InviteSessionType.DOWNLOAD) { return false; } - // 收到无人观看说明流也没有在往上级推送 - if (redisCatchStorage.isChannelSendingRTP(inviteInfo.getChannelId())) { - List sendRtpItems = redisCatchStorage.querySendRTPServerByChannelId( - inviteInfo.getChannelId()); - if (!sendRtpItems.isEmpty()) { - for (SendRtpItem sendRtpItem : sendRtpItems) { - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId()); - try { - commanderForPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId()); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); - } - redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(), - sendRtpItem.getCallId(), sendRtpItem.getStream()); - if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) { - redisCatchStorage.sendPlatformStopPlayMsg(sendRtpItem,parentPlatform); - } - } - } + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(inviteInfo.getChannelId()); + if (deviceChannel == null) { + return false; } - Device device = deviceService.getDevice(inviteInfo.getDeviceId()); - if (device != null) { - try { - // 多查询一次防止已经被处理了 - InviteInfo info = inviteStreamService.getInviteInfo(inviteInfo.getType(), - inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); - if (info != null) { - commander.streamByeCmd(device, inviteInfo.getChannelId(), - inviteInfo.getStream(), null); - } else { - logger.info("[无人观看] 未找到设备的点播信息: {}, 流:{}", inviteInfo.getDeviceId(), stream); - } - } catch (InvalidArgumentException | ParseException | SipException | - SsrcTransactionNotFoundException e) { - logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage()); - } - } else { - logger.info("[无人观看] 未找到设备: {},流:{}", inviteInfo.getDeviceId(), stream); - } - - inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), - inviteInfo.getChannelId(), inviteInfo.getStream()); - storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId()); return result; }else { // 判断是否是1078点播 @@ -282,6 +254,8 @@ public class MediaServiceImpl implements IMediaService { }else if (jtMediaStreamType.equals(JTMediaStreamType.PLAYBACK)) { ijt1078Service.stopPlayback(streamParamArray[1], Integer.parseInt(streamParamArray[2])); } + }else { + return false; } } @@ -294,25 +268,25 @@ public class MediaServiceImpl implements IMediaService { } else { // 非国标流 推流/拉流代理 // 拉流代理 - StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(app, stream); - if (streamProxyItem != null) { - if (streamProxyItem.isEnableRemoveNoneReader()) { + StreamProxy streamProxy = streamProxyService.getStreamProxyByAppAndStream(app, stream); + if (streamProxy != null) { + if (streamProxy.isEnableRemoveNoneReader()) { // 无人观看自动移除 - result = true; - streamProxyService.del(app, stream); - String url = streamProxyItem.getUrl() != null ? streamProxyItem.getUrl() : streamProxyItem.getSrcUrl(); - logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", app, stream, url); - } else if (streamProxyItem.isEnableDisableNoneReader()) { + streamProxyService.delteByAppAndStream(app, stream); + log.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", app, stream, streamProxy.getSrcUrl()); + return true; + } else if (streamProxy.isEnableDisableNoneReader()) { // 无人观看停用 - result = true; // 修改数据 - streamProxyService.stop(app, stream); + streamProxyService.stopByAppAndStream(app, stream); + return true; } else { // 无人观看不做处理 - result = false; + return false; } + }else { + return false; } } - return result; } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MobilePositionServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MobilePositionServiceImpl.java index 277493ad1..97f26a10b 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/MobilePositionServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MobilePositionServiceImpl.java @@ -3,11 +3,14 @@ package com.genersoft.iot.vmp.service.impl; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.dao.PlatformMapper; import com.genersoft.iot.vmp.service.IMobilePositionService; -import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; -import com.genersoft.iot.vmp.storager.dao.DeviceMobilePositionMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.genersoft.iot.vmp.gb28181.dao.DeviceChannelMapper; +import com.genersoft.iot.vmp.gb28181.dao.DeviceMobilePositionMapper; +import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; +import com.genersoft.iot.vmp.utils.DateUtil; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; @@ -19,7 +22,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - +@Slf4j @Service public class MobilePositionServiceImpl implements IMobilePositionService { @@ -32,11 +35,13 @@ public class MobilePositionServiceImpl implements IMobilePositionService { @Autowired private UserSetting userSetting; + + @Autowired + private PlatformMapper platformMapper; + @Autowired private RedisTemplate redisTemplate; - private final static Logger logger = LoggerFactory.getLogger(MobilePositionServiceImpl.class); - private final String REDIS_MOBILE_POSITION_LIST = "redis_mobile_position_list"; @Override @@ -67,6 +72,33 @@ public class MobilePositionServiceImpl implements IMobilePositionService { + /** + * 查询移动位置轨迹 + */ + @Override + public synchronized List queryMobilePositions(String deviceId, String channelId, String startTime, String endTime) { + return mobilePositionMapper.queryPositionByDeviceIdAndTime(deviceId, channelId, startTime, endTime); + } + + @Override + public List queryEnablePlatformListWithAsMessageChannel() { + return platformMapper.queryEnablePlatformListWithAsMessageChannel(); + } + + /** + * 查询最新移动位置 + * @param deviceId + */ + @Override + public MobilePosition queryLatestPosition(String deviceId) { + return mobilePositionMapper.queryLatestPositionByDevice(deviceId); + } + + @Override + public void updateStreamGPS(List gpsMsgInfoList) { + channelMapper.updateStreamGPS(gpsMsgInfoList); + } + @Scheduled(fixedRate = 1000) @Transactional public void executeTaskQueue() { @@ -78,14 +110,16 @@ public class MobilePositionServiceImpl implements IMobilePositionService { if (userSetting.getSavePositionHistory()) { mobilePositionMapper.batchadd(mobilePositions); } - logger.info("[移动位置订阅]更新通道位置: {}", mobilePositions.size()); + log.info("[移动位置订阅]更新通道位置: {}", mobilePositions.size()); Map updateChannelMap = new HashMap<>(); for (MobilePosition mobilePosition : mobilePositions) { DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setId(mobilePosition.getChannelId()); deviceChannel.setDeviceId(mobilePosition.getDeviceId()); deviceChannel.setLongitude(mobilePosition.getLongitude()); deviceChannel.setLatitude(mobilePosition.getLatitude()); deviceChannel.setGpsTime(mobilePosition.getTime()); + deviceChannel.setUpdateTime(DateUtil.getNow()); updateChannelMap.put(mobilePosition.getDeviceId() + mobilePosition.getChannelId(), deviceChannel); } List channels = new ArrayList<>(updateChannelMap.values()); diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java deleted file mode 100755 index 35028dff4..000000000 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java +++ /dev/null @@ -1,175 +0,0 @@ -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; -import com.genersoft.iot.vmp.service.IPlatformChannelService; -import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; -import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper; -import com.genersoft.iot.vmp.storager.dao.PlatformCatalogMapper; -import com.genersoft.iot.vmp.storager.dao.PlatformChannelMapper; -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.datasource.DataSourceTransactionManager; -import org.springframework.stereotype.Service; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.util.ObjectUtils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @author lin - */ -@Service -@DS("master") -public class PlatformChannelServiceImpl implements IPlatformChannelService { - - private final static Logger logger = LoggerFactory.getLogger(PlatformChannelServiceImpl.class); - - @Autowired - private PlatformChannelMapper platformChannelMapper; - - @Autowired - TransactionDefinition transactionDefinition; - - @Autowired - DataSourceTransactionManager dataSourceTransactionManager; - - @Autowired - private SubscribeHolder subscribeHolder; - - - @Autowired - private DeviceChannelMapper deviceChannelMapper; - - @Autowired - private PlatformCatalogMapper catalogManager; - - @Autowired - private ParentPlatformMapper platformMapper; - - @Autowired - EventPublisher eventPublisher; - - @Override - public int updateChannelForGB(String platformId, List channelReduces, String catalogId) { - ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId); - if (platform == null) { - logger.warn("更新级联通道信息时未找到平台{}的信息", platformId); - return 0; - } - Map deviceAndChannels = new HashMap<>(); - for (ChannelReduce channelReduce : channelReduces) { - channelReduce.setCatalogId(catalogId); - deviceAndChannels.put(channelReduce.getId(), channelReduce); - } - List deviceAndChannelList = new ArrayList<>(deviceAndChannels.keySet()); - // 查询当前已经存在的 - List channelIds = platformChannelMapper.findChannelRelatedPlatform(platformId, channelReduces); - if (deviceAndChannelList != null) { - deviceAndChannelList.removeAll(channelIds); - } - for (Integer channelId : channelIds) { - deviceAndChannels.remove(channelId); - } - List channelReducesToAdd = new ArrayList<>(deviceAndChannels.values()); - // 对剩下的数据进行存储 - int allCount = 0; - boolean result = false; - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); - int limitCount = 50; - if (channelReducesToAdd.size() > 0) { - if (channelReducesToAdd.size() > limitCount) { - for (int i = 0; i < channelReducesToAdd.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > channelReducesToAdd.size()) { - toIndex = channelReducesToAdd.size(); - } - int count = platformChannelMapper.addChannels(platformId, channelReducesToAdd.subList(i, toIndex)); - result = result || count < 0; - allCount += count; - logger.info("[关联通道]国标通道 平台:{}, 共需关联通道数:{}, 已关联:{}", platformId, channelReducesToAdd.size(), toIndex); - } - }else { - allCount = platformChannelMapper.addChannels(platformId, channelReducesToAdd); - result = result || allCount < 0; - logger.info("[关联通道]国标通道 平台:{}, 关联通道数:{}", platformId, channelReducesToAdd.size()); - } - - if (result) { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - allCount = 0; - }else { - logger.info("[关联通道]国标通道 平台:{}, 正在存入数据库", platformId); - dataSourceTransactionManager.commit(transactionStatus); - - } - SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(platformId); - if (catalogSubscribe != null) { - List deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId, platform); - if (deviceChannelList != null) { - eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD); - } - } - logger.info("[关联通道]国标通道 平台:{}, 存入数据库成功", platformId); - } - return allCount; - } - - private List getDeviceChannelListByChannelReduceList(List channelReduces, String catalogId, ParentPlatform platform) { - List deviceChannelList = new ArrayList<>(); - if (channelReduces.size() > 0){ - PlatformCatalog catalog = catalogManager.selectByPlatFormAndCatalogId(platform.getServerGBId(),catalogId); - if (catalog == null && catalogId.equals(platform.getDeviceGBId())) { - for (ChannelReduce channelReduce : channelReduces) { - DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId()); - deviceChannel.setParental(0); - deviceChannel.setCivilCode(platform.getServerGBDomain()); - deviceChannelList.add(deviceChannel); - } - return deviceChannelList; - } else if (catalog == null && !catalogId.equals(platform.getDeviceGBId())) { - logger.warn("未查询到目录{}的信息", catalogId); - return null; - } - for (ChannelReduce channelReduce : channelReduces) { - DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId()); - deviceChannel.setParental(0); - deviceChannel.setCivilCode(catalog.getCivilCode()); - deviceChannel.setParentId(catalog.getParentId()); - deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); - deviceChannelList.add(deviceChannel); - } - } - return deviceChannelList; - } - - @Override - public int delAllChannelForGB(String platformId, String catalogId) { - - int result; - if (platformId == null) { - return 0; - } - ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId); - if (platform == null) { - return 0; - } - if (ObjectUtils.isEmpty(catalogId)) { - catalogId = null; - } - - List deviceChannels = platformChannelMapper.queryAllChannelInCatalog(platformId, catalogId); - eventPublisher.catalogEventPublish(platformId, deviceChannels, CatalogEvent.DEL); - - return platformChannelMapper.delChannelForGBByCatalogId(platformId, catalogId); - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java deleted file mode 100755 index 39c67c264..000000000 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java +++ /dev/null @@ -1,1625 +0,0 @@ -package com.genersoft.iot.vmp.service.impl; - -import com.baomidou.dynamic.datasource.annotation.DS; -import com.genersoft.iot.vmp.common.*; -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.exception.ServiceException; -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.AudioBroadcastManager; -import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; -import com.genersoft.iot.vmp.gb28181.utils.SipUtils; -import com.genersoft.iot.vmp.media.bean.MediaInfo; -import com.genersoft.iot.vmp.media.bean.RecordInfo; -import com.genersoft.iot.vmp.media.event.hook.Hook; -import com.genersoft.iot.vmp.media.event.hook.HookType; -import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent; -import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent; -import com.genersoft.iot.vmp.media.event.media.MediaNotFoundEvent; -import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; -import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; -import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.service.*; -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.utils.CloudRecordUtils; -import com.genersoft.iot.vmp.utils.DateUtil; -import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; -import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; -import com.genersoft.iot.vmp.vmanager.bean.StreamContent; -import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; -import gov.nist.javax.sip.message.SIPResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Service; -import org.springframework.util.ObjectUtils; - -import javax.sdp.*; -import javax.sip.InvalidArgumentException; -import javax.sip.ResponseEvent; -import javax.sip.SipException; -import javax.sip.header.CallIdHeader; -import java.io.File; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.text.ParseException; -import java.util.*; - -@SuppressWarnings(value = {"rawtypes", "unchecked"}) -@Service -@DS("master") -public class PlayServiceImpl implements IPlayService { - - private final static Logger logger = LoggerFactory.getLogger(PlayServiceImpl.class); - - @Autowired - private IVideoManagerStorage storager; - - @Autowired - private ISIPCommander cmder; - - @Autowired - private AudioBroadcastManager audioBroadcastManager; - - @Autowired - private IDeviceService deviceService; - - @Autowired - private ISIPCommanderForPlatform sipCommanderFroPlatform; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - private IInviteStreamService inviteStreamService; - - @Autowired - private HookSubscribe subscribe; - - @Autowired - private SendRtpPortManager sendRtpPortManager; - - @Autowired - private IMediaServerService mediaServerService; - - @Autowired - private VideoStreamSessionManager streamSession; - - @Autowired - private UserSetting userSetting; - - @Autowired - private IDeviceChannelService channelService; - - @Autowired - private DynamicTask dynamicTask; - - @Autowired - private ISIPCommanderForPlatform commanderForPlatform; - - @Autowired - private SSRCFactory ssrcFactory; - - /** - * 流到来的处理 - */ - @Async("taskExecutor") - @org.springframework.context.event.EventListener - public void onApplicationEvent(MediaArrivalEvent event) { - if ("broadcast".equals(event.getApp())) { - if (event.getStream().indexOf("_") > 0) { - String[] streamArray = event.getStream().split("_"); - if (streamArray.length == 2) { - String deviceId = streamArray[0]; - String channelId = streamArray[1]; - Device device = deviceService.getDevice(deviceId); - if (device == null) { - logger.info("[语音对讲/喊话] 未找到设备:{}", deviceId); - return; - } - if ("broadcast".equals(event.getApp())) { - if (audioBroadcastManager.exit(deviceId, channelId)) { - stopAudioBroadcast(deviceId, channelId); - } - // 开启语音对讲通道 - try { - audioBroadcastCmd(device, channelId, event.getMediaServer(), - event.getApp(), event.getStream(), 60, false, (msg) -> { - logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId); - }); - } catch (InvalidArgumentException | ParseException | SipException e) { - logger.error("[命令发送失败] 语音对讲: {}", e.getMessage()); - } - }else if ("talk".equals(event.getApp())) { - // 开启语音对讲通道 - talkCmd(device, channelId, event.getMediaServer(), event.getStream(), (msg) -> { - logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId); - }); - } - } - } - } - - - } - - /** - * 流离开的处理 - */ - @Async("taskExecutor") - @EventListener - public void onApplicationEvent(MediaDepartureEvent event) { - List sendRtpItems = redisCatchStorage.querySendRTPServerByStream(event.getStream()); - if (!sendRtpItems.isEmpty()) { - for (SendRtpItem sendRtpItem : sendRtpItems) { - if (sendRtpItem != null && sendRtpItem.getApp().equals(event.getApp())) { - String platformId = sendRtpItem.getPlatformId(); - Device device = deviceService.getDevice(platformId); - try { - if (device != null) { - cmder.streamByeCmd(device, sendRtpItem.getChannelId(), event.getStream(), sendRtpItem.getCallId()); - if (sendRtpItem.getPlayType().equals(InviteStreamType.BROADCAST) - || sendRtpItem.getPlayType().equals(InviteStreamType.TALK)) { - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); - if (audioBroadcastCatch != null) { - // 来自上级平台的停止对讲 - logger.info("[停止对讲] 来自上级,平台:{}, 通道:{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); - audioBroadcastManager.del(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); - } - } - } - } catch (SipException | InvalidArgumentException | ParseException | - SsrcTransactionNotFoundException e) { - logger.error("[命令发送失败] 发送BYE: {}", e.getMessage()); - } - } - } - } - - if ("broadcast".equals(event.getApp()) || "talk".equals(event.getApp())) { - if (event.getStream().indexOf("_") > 0) { - String[] streamArray = event.getStream().split("_"); - if (streamArray.length == 2) { - String deviceId = streamArray[0]; - String channelId = streamArray[1]; - Device device = deviceService.getDevice(deviceId); - if (device == null) { - logger.info("[语音对讲/喊话] 未找到设备:{}", deviceId); - return; - } - if ("broadcast".equals(event.getApp())) { - stopAudioBroadcast(deviceId, channelId); - }else if ("talk".equals(event.getApp())) { - stopTalk(device, channelId, false); - } - } - } - } - } - - /** - * 流未找到的处理 - */ - @Async("taskExecutor") - @EventListener - public void onApplicationEvent(MediaNotFoundEvent event) { - if (!"rtp".equals(event.getApp())) { - return; - } - String[] s = event.getStream().split("_"); - if ((s.length != 2 && s.length != 4)) { - return; - } - String deviceId = s[0]; - String channelId = s[1]; - Device device = redisCatchStorage.getDevice(deviceId); - if (device == null || !device.isOnLine()) { - return; - } - DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); - if (deviceChannel == null) { - return; - } - if (s.length == 2) { - logger.info("[ZLM HOOK] 预览流未找到, 发起自动点播:{}->{}->{}/{}", event.getMediaServer().getId(), event.getSchema(), event.getApp(), event.getStream()); - play(event.getMediaServer(), deviceId, channelId, null, null); - } else if (s.length == 4) { - // 此时为录像回放, 录像回放格式为> 设备ID_通道ID_开始时间_结束时间 - String startTimeStr = s[2]; - String endTimeStr = s[3]; - if (startTimeStr == null || endTimeStr == null || startTimeStr.length() != 14 || endTimeStr.length() != 14) { - return; - } - String startTime = DateUtil.urlToyyyy_MM_dd_HH_mm_ss(startTimeStr); - String endTime = DateUtil.urlToyyyy_MM_dd_HH_mm_ss(endTimeStr); - logger.info("[ZLM HOOK] 回放流未找到, 发起自动点播:{}->{}->{}/{}-{}-{}", - event.getMediaServer().getId(), event.getSchema(), - event.getApp(), event.getStream(), - startTime, endTime - ); - - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(event.getMediaServer(), event.getStream(), null, - device.isSsrcCheck(), true, 0, false, !deviceChannel.getHasAudio(), false, device.getStreamModeForParam()); - playBack(event.getMediaServer(), ssrcInfo, deviceId, channelId, startTime, endTime, null); - } - } - - - @Override - public SSRCInfo play(MediaServer mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback callback) { - if (mediaServerItem == null) { - logger.warn("[点播] 未找到可用的zlm deviceId: {},channelId:{}", deviceId, channelId); - throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm"); - } - Device device = redisCatchStorage.getDevice(deviceId); - if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE") && !mediaServerItem.isRtpEnable()) { - logger.warn("[点播] 单端口收流时不支持TCP主动方式收流 deviceId: {},channelId:{}", deviceId, channelId); - throw new ControllerException(ErrorCode.ERROR100.getCode(), "单端口收流时不支持TCP主动方式收流"); - } - DeviceChannel channel = channelService.getOne(deviceId, channelId); - if (channel == null) { - logger.warn("[点播] 未找到通道 deviceId: {},channelId:{}", deviceId, channelId); - throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到通道"); - } - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); - if (inviteInfo != null ) { - if (inviteInfo.getStreamInfo() == null) { - // 释放生成的ssrc,使用上一次申请的 - ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc); - // 点播发起了但是尚未成功, 仅注册回调等待结果即可 - inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); - logger.info("[点播开始] 已经请求中,等待结果, deviceId: {}, channelId: {}", device.getDeviceId(), channelId); - return inviteInfo.getSsrcInfo(); - }else { - StreamInfo streamInfo = inviteInfo.getStreamInfo(); - String streamId = streamInfo.getStream(); - if (streamId == null) { - callback.run(InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), "点播失败, redis缓存streamId等于null", null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), - "点播失败, redis缓存streamId等于null", - null); - return inviteInfo.getSsrcInfo(); - } - String mediaServerId = streamInfo.getMediaServerId(); - MediaServer mediaInfo = mediaServerService.getOne(mediaServerId); - Boolean ready = mediaServerService.isStreamReady(mediaInfo, "rtp", streamId); - if (ready != null && ready) { - callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.SUCCESS.getCode(), - InviteErrorCode.SUCCESS.getMsg(), - streamInfo); - logger.info("[点播已存在] 直接返回, deviceId: {}, channelId: {}", device.getDeviceId(), channelId); - return inviteInfo.getSsrcInfo(); - }else { - // 点播发起了但是尚未成功, 仅注册回调等待结果即可 - inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback); - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); - } - } - } - String streamId = String.format("%s_%s", device.getDeviceId(), channelId); - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, ssrc, device.isSsrcCheck(), false, 0, false, !channel.getHasAudio(), false, device.getStreamModeForParam()); - if (ssrcInfo == null) { - callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), - InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), - null); - return null; - } - play(mediaServerItem, ssrcInfo, device, channel, callback); - return ssrcInfo; - } - - private void talk(MediaServer mediaServerItem, Device device, String channelId, String stream, - HookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, - Runnable timeoutCallback, AudioBroadcastEvent audioEvent) { - - String playSsrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId()); - - if (playSsrc == null) { - audioEvent.call("ssrc已经用尽"); - return; - } - SendRtpItem sendRtpItem = new SendRtpItem(); - sendRtpItem.setApp("talk"); - sendRtpItem.setStream(stream); - sendRtpItem.setSsrc(playSsrc); - sendRtpItem.setDeviceId(device.getDeviceId()); - sendRtpItem.setPlatformId(device.getDeviceId()); - sendRtpItem.setChannelId(channelId); - sendRtpItem.setRtcp(false); - sendRtpItem.setMediaServerId(mediaServerItem.getId()); - sendRtpItem.setOnlyAudio(true); - sendRtpItem.setPlayType(InviteStreamType.TALK); - sendRtpItem.setPt(8); - sendRtpItem.setStatus(1); - sendRtpItem.setTcpActive(false); - sendRtpItem.setTcp(true); - sendRtpItem.setUsePs(false); - sendRtpItem.setReceiveStream(stream + "_talk"); - - String callId = SipUtils.getNewCallId(); - int port = sendRtpPortManager.getNextPort(mediaServerItem); - //端口获取失败的ssrcInfo 没有必要发送点播指令 - if (port <= 0) { - logger.info("[语音对讲] 端口分配异常,deviceId={},channelId={}", device.getDeviceId(), channelId); - audioEvent.call("端口分配异常"); - return; - } - sendRtpItem.setLocalPort(port); - sendRtpItem.setPort(port); - logger.info("[语音对讲]开始 deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sendRtpItem.getLocalPort(), device.getStreamMode(), sendRtpItem.getSsrc(), false); - // 超时处理 - String timeOutTaskKey = UUID.randomUUID().toString(); - dynamicTask.startDelay(timeOutTaskKey, () -> { - - logger.info("[语音对讲] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, sendRtpItem.getPort(), sendRtpItem.getSsrc()); - timeoutCallback.run(); - // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 - try { - cmder.streamByeCmd(device, channelId, sendRtpItem.getStream(), null); - } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { - logger.error("[语音对讲]超时, 发送BYE失败 {}", e.getMessage()); - } finally { - timeoutCallback.run(); - mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); - streamSession.remove(device.getDeviceId(), channelId, sendRtpItem.getStream()); - } - }, userSetting.getPlayTimeout()); - - try { - mediaServerService.startSendRtpPassive(mediaServerItem, sendRtpItem, userSetting.getPlayTimeout() * 1000); - }catch (ControllerException e) { - mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); - logger.info("[语音对讲]失败 deviceId: {}, channelId: {}", device.getDeviceId(), channelId); - audioEvent.call("失败, " + e.getMessage()); - // 查看是否已经建立了通道,存在则发送bye - stopTalk(device, channelId); - } - - - // 查看设备是否已经在推流 - try { - cmder.talkStreamCmd(mediaServerItem, sendRtpItem, device, channelId, callId, (hookData) -> { - logger.info("[语音对讲] 流已生成, 开始推流: " + hookData); - dynamicTask.stop(timeOutTaskKey); - // TODO 暂不做处理 - }, (hookData) -> { - logger.info("[语音对讲] 设备开始推流: " + hookData); - dynamicTask.stop(timeOutTaskKey); - - }, (event) -> { - dynamicTask.stop(timeOutTaskKey); - - if (event.event instanceof ResponseEvent) { - ResponseEvent responseEvent = (ResponseEvent) event.event; - if (responseEvent.getResponse() instanceof SIPResponse) { - SIPResponse response = (SIPResponse) responseEvent.getResponse(); - sendRtpItem.setFromTag(response.getFromTag()); - sendRtpItem.setToTag(response.getToTag()); - sendRtpItem.setCallId(response.getCallIdHeader().getCallId()); - redisCatchStorage.updateSendRTPSever(sendRtpItem); - - streamSession.put(device.getDeviceId(), channelId, "talk", - sendRtpItem.getStream(), sendRtpItem.getSsrc(), sendRtpItem.getMediaServerId(), - response, InviteSessionType.TALK); - } else { - logger.error("[语音对讲]收到的消息错误,response不是SIPResponse"); - } - } else { - logger.error("[语音对讲]收到的消息错误,event不是ResponseEvent"); - } - - }, (event) -> { - dynamicTask.stop(timeOutTaskKey); - mediaServerService.closeRTPServer(mediaServerItem, sendRtpItem.getStream()); - // 释放ssrc - mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); - streamSession.remove(device.getDeviceId(), channelId, sendRtpItem.getStream()); - errorEvent.response(event); - }); - } catch (InvalidArgumentException | SipException | ParseException e) { - - logger.error("[命令发送失败] 对讲消息: {}", e.getMessage()); - dynamicTask.stop(timeOutTaskKey); - mediaServerService.closeRTPServer(mediaServerItem, sendRtpItem.getStream()); - // 释放ssrc - mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); - - streamSession.remove(device.getDeviceId(), channelId, sendRtpItem.getStream()); - SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(); - eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent; - eventResult.statusCode = -1; - eventResult.msg = "命令发送失败"; - errorEvent.response(eventResult); - } -// } - - } - - - - @Override - public void play(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel, - ErrorCallback callback) { - - if (mediaServerItem == null || ssrcInfo == null) { - callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), - InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), - null); - return; - } - logger.info("[点播开始] deviceId: {}, channelId: {},码流类型:{}, 收流端口: {}, 码流:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", - device.getDeviceId(), channel.getChannelId(), channel.getStreamIdentification(), ssrcInfo.getPort(), ssrcInfo.getStream(), - device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); - //端口获取失败的ssrcInfo 没有必要发送点播指令 - if (ssrcInfo.getPort() <= 0) { - logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channel.getChannelId(), ssrcInfo); - // 释放ssrc - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream()); - - callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId(), null, - InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null); - return; - } - - // 初始化redis中的invite消息状态 - InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream(), ssrcInfo, - mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, - InviteSessionStatus.ready); - inviteStreamService.updateInviteInfo(inviteInfo); - // 超时处理 - String timeOutTaskKey = UUID.randomUUID().toString(); - dynamicTask.startDelay(timeOutTaskKey, () -> { - // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况 - InviteInfo inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId()); - if (inviteInfoForTimeOut == null || inviteInfoForTimeOut.getStreamInfo() == null) { - logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},码流:{},端口:{}, SSRC: {}", - device.getDeviceId(), channel.getChannelId(), channel.getStreamIdentification(), - ssrcInfo.getPort(), ssrcInfo.getSsrc()); - - callback.run(InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId(), null, - InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null); - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId()); - - try { - cmder.streamByeCmd(device, channel.getChannelId(), ssrcInfo.getStream(), null); - } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { - logger.error("[点播超时], 发送BYE失败 {}", e.getMessage()); - } finally { - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); - streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream()); - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); - // 取消订阅消息监听 - subscribe.removeSubscribe(Hook.getInstance(HookType.on_media_arrival, "rtp", ssrcInfo.getStream(), mediaServerItem.getId())); - } - }else { - logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},码流:{},端口:{}, SSRC: {}", - device.getDeviceId(), channel.getChannelId(), channel.getStreamIdentification(), - ssrcInfo.getPort(), ssrcInfo.getSsrc()); - - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - - mediaServerService.closeRTPServer(mediaServerItem.getId(), ssrcInfo.getStream()); - streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream()); - } - }, userSetting.getPlayTimeout()); - - try { - cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channel, (hookData ) -> { - logger.info("收到订阅消息: " + hookData); - dynamicTask.stop(timeOutTaskKey); - // hook响应 - StreamInfo streamInfo = onPublishHandlerForPlay(hookData.getMediaServer(), hookData.getMediaInfo(), device.getDeviceId(), channel.getChannelId()); - if (streamInfo == null){ - callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId(), null, - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); - return; - } - callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId(), null, - InviteErrorCode.SUCCESS.getCode(), - InviteErrorCode.SUCCESS.getMsg(), - streamInfo); - logger.info("[点播成功] deviceId: {}, channelId:{}, 码流类型:{}", device.getDeviceId(), channel.getChannelId(), - channel.getStreamIdentification()); - snapOnPlay(hookData.getMediaServer(), device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream()); - }, (eventResult) -> { - // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题 - InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channel.getChannelId(), - timeOutTaskKey, callback, inviteInfo, InviteSessionType.PLAY); - }, (event) -> { - logger.info("[点播失败] deviceId: {}, channelId:{}, {}: {}", device.getDeviceId(), channel.getChannelId(), event.statusCode, event.msg); - dynamicTask.stop(timeOutTaskKey); - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); - // 释放ssrc - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - - streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream()); - - callback.run(event.statusCode, event.msg, null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId(), null, - InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), - String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null); - - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId()); - }); - } catch (InvalidArgumentException | SipException | ParseException e) { - - logger.error("[命令发送失败] 点播消息: {}", e.getMessage()); - dynamicTask.stop(timeOutTaskKey); - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); - // 释放ssrc - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - - streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream()); - - callback.run(InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), - InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); - inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId(), null, - InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), - InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); - - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId()); - } - } - - private void tcpActiveHandler(Device device, String channelId, String contentString, - MediaServer mediaServerItem, - String timeOutTaskKey, SSRCInfo ssrcInfo, ErrorCallback callback){ - if (!device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { - return; - } - - String substring; - if (contentString.indexOf("y=") > 0) { - substring = contentString.substring(0, contentString.indexOf("y=")); - }else { - substring = contentString; - } - try { - SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); - int port = -1; - Vector mediaDescriptions = sdp.getMediaDescriptions(true); - for (Object description : mediaDescriptions) { - MediaDescription mediaDescription = (MediaDescription) description; - Media media = mediaDescription.getMedia(); - - Vector mediaFormats = media.getMediaFormats(false); - if (mediaFormats.contains("96")) { - port = media.getMediaPort(); - break; - } - } - logger.info("[TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sdp.getConnection().getAddress(), port, device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); - Boolean result = mediaServerService.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream()); - logger.info("[TCP主动连接对方] 结果: {}" , result); - if (!result) { - // 主动连接失败,结束流程, 清理数据 - dynamicTask.stop(timeOutTaskKey); - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); - // 释放ssrc - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); - - callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); - inviteStreamService.call(InviteSessionType.BROADCAST, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); - } - } catch (SdpException e) { - logger.error("[TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channelId, e); - dynamicTask.stop(timeOutTaskKey); - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); - // 释放ssrc - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); - - callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); - inviteStreamService.call(InviteSessionType.BROADCAST, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), - InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); - } - } - - /** - * 点播成功时调用截图. - * - * @param mediaServerItemInuse media - * @param deviceId 设备 ID - * @param channelId 通道 ID - * @param stream ssrc - */ - private void snapOnPlay(MediaServer mediaServerItemInuse, String deviceId, String channelId, String stream) { - String streamUrl; - if (mediaServerItemInuse.getRtspPort() != 0) { - streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", stream); - } else { - streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.mp4", mediaServerItemInuse.getHttpPort(), "rtp", stream); - } - String path = "snap"; - String fileName = deviceId + "_" + channelId + ".jpg"; - // 请求截图 - logger.info("[请求截图]: " + fileName); - mediaServerService.getSnap(mediaServerItemInuse, streamUrl, 15, 1, path, fileName); - } - - public StreamInfo onPublishHandlerForPlay(MediaServer mediaServerItem, MediaInfo mediaInfo, String deviceId, String channelId) { - StreamInfo streamInfo = null; - Device device = redisCatchStorage.getDevice(deviceId); - streamInfo = onPublishHandler(mediaServerItem, mediaInfo, deviceId, channelId); - if (streamInfo != null) { - DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); - if (deviceChannel != null) { - deviceChannel.setStreamId(streamInfo.getStream()); - storager.startPlay(deviceId, channelId, streamInfo.getStream()); - } - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); - if (inviteInfo != null) { - inviteInfo.setStatus(InviteSessionStatus.ok); - inviteInfo.setStreamInfo(streamInfo); - inviteStreamService.updateInviteInfo(inviteInfo); - } - } - return streamInfo; - - } - - private StreamInfo onPublishHandlerForPlayback(MediaServer mediaServerItem, MediaInfo mediaInfo, String deviceId, String channelId, String startTime, String endTime) { - StreamInfo streamInfo = onPublishHandler(mediaServerItem, mediaInfo, deviceId, channelId); - if (streamInfo != null) { - streamInfo.setStartTime(startTime); - streamInfo.setEndTime(endTime); - DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); - if (deviceChannel != null) { - deviceChannel.setStreamId(streamInfo.getStream()); - storager.startPlay(deviceId, channelId, streamInfo.getStream()); - } - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, mediaInfo.getStream()); - if (inviteInfo != null) { - inviteInfo.setStatus(InviteSessionStatus.ok); - - inviteInfo.setStreamInfo(streamInfo); - inviteStreamService.updateInviteInfo(inviteInfo); - } - - } - return streamInfo; - } - - @Override - public MediaServer getNewMediaServerItem(Device device) { - if (device == null) { - return null; - } - MediaServer mediaServerItem; - if (ObjectUtils.isEmpty(device.getMediaServerId()) || "auto".equals(device.getMediaServerId())) { - mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(null); - } else { - mediaServerItem = mediaServerService.getOne(device.getMediaServerId()); - } - if (mediaServerItem == null) { - logger.warn("点播时未找到可使用的ZLM..."); - } - return mediaServerItem; - } - - @Override - public void playBack(String deviceId, String channelId, String startTime, - String endTime, ErrorCallback callback) { - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - logger.warn("[录像回放] 未找到设备 deviceId: {},channelId:{}", deviceId, channelId); - throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到设备:" + deviceId); - } - - DeviceChannel channel = channelService.getOne(deviceId, channelId); - if (channel == null) { - logger.warn("[录像回放] 未找到通道 deviceId: {},channelId:{}", deviceId, channelId); - throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到通道:" + channelId); - } - - MediaServer newMediaServerItem = getNewMediaServerItem(device); - if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE") && ! newMediaServerItem.isRtpEnable()) { - logger.warn("[录像回放] 单端口收流时不支持TCP主动方式收流 deviceId: {},channelId:{}", deviceId, channelId); - throw new ControllerException(ErrorCode.ERROR100.getCode(), "单端口收流时不支持TCP主动方式收流"); - } - String startTimeStr = startTime.replace("-", "") - .replace(":", "") - .replace(" ", ""); - String endTimeTimeStr = endTime.replace("-", "") - .replace(":", "") - .replace(" ", ""); - String stream = deviceId + "_" + channelId + "_" + startTimeStr + "_" + endTimeTimeStr; - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, stream, null, device.isSsrcCheck(), true, 0, false, !channel.getHasAudio(), false, device.getStreamModeForParam()); - playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, callback); - } - - @Override - public void playBack(MediaServer mediaServerItem, SSRCInfo ssrcInfo, - String deviceId, String channelId, String startTime, - String endTime, ErrorCallback callback) { - if (mediaServerItem == null || ssrcInfo == null) { - callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), - InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), - null); - return; - } - - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备: " + deviceId + "不存在"); - } - logger.info("[录像回放] deviceId: {}, channelId: {}, 开始时间: {}, 结束时间: {}, 收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", - device.getDeviceId(), channelId, startTime, endTime, ssrcInfo.getPort(), device.getStreamMode(), - ssrcInfo.getSsrc(), device.isSsrcCheck()); - // 初始化redis中的invite消息状态 - InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo, - mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAYBACK, - InviteSessionStatus.ready); - inviteStreamService.updateInviteInfo(inviteInfo); - String playBackTimeOutTaskKey = UUID.randomUUID().toString(); - dynamicTask.startDelay(playBackTimeOutTaskKey, () -> { - logger.warn("[录像回放] 超时,deviceId:{} ,channelId:{}", deviceId, channelId); - inviteStreamService.removeInviteInfo(inviteInfo); - callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getMsg(), null); - - try { - cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); - } catch (InvalidArgumentException | ParseException | SipException e) { - logger.error("[录像回放] 超时 发送BYE失败 {}", e.getMessage()); - } catch (SsrcTransactionNotFoundException e) { - // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); - streamSession.remove(deviceId, channelId, ssrcInfo.getStream()); - } - }, userSetting.getPlayTimeout()); - - SipSubscribe.Event errorEvent = event -> { - logger.info("[录像回放] 失败,{} {}", event.statusCode, event.msg); - dynamicTask.stop(playBackTimeOutTaskKey); - callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_ERROR.getCode(), - String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg), null); - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); - inviteStreamService.removeInviteInfo(inviteInfo); - }; - - HookSubscribe.Event hookEvent = (hookData) -> { - logger.info("收到回放订阅消息: " + hookData); - dynamicTask.stop(playBackTimeOutTaskKey); - StreamInfo streamInfo = onPublishHandlerForPlayback(hookData.getMediaServer(), hookData.getMediaInfo(), deviceId, channelId, startTime, endTime); - if (streamInfo == null) { - logger.warn("设备回放API调用失败!"); - callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); - return; - } - callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); - logger.info("[录像回放] 成功 deviceId: {}, channelId: {}, 开始时间: {}, 结束时间: {}", device.getDeviceId(), channelId, startTime, endTime); - }; - - try { - cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, - hookEvent, eventResult -> { - // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题 - InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId, - playBackTimeOutTaskKey, callback, inviteInfo, InviteSessionType.PLAYBACK); - }, errorEvent); - } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 录像回放: {}", e.getMessage()); - - SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(); - eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent; - eventResult.statusCode = -1; - eventResult.msg = "命令发送失败"; - errorEvent.response(eventResult); - } - } - - - private void InviteOKHandler(SipSubscribe.EventResult eventResult, SSRCInfo ssrcInfo, MediaServer mediaServerItem, - Device device, String channelId, String timeOutTaskKey, ErrorCallback callback, - InviteInfo inviteInfo, InviteSessionType inviteSessionType){ - inviteInfo.setStatus(InviteSessionStatus.ok); - ResponseEvent responseEvent = (ResponseEvent) eventResult.event; - String contentString = new String(responseEvent.getResponse().getRawContent()); - String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); - // 兼容回复的消息中缺少ssrc(y字段)的情况 - if (ssrcInResponse == null) { - ssrcInResponse = ssrcInfo.getSsrc(); - } - if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { - // ssrc 一致 - if (mediaServerItem.isRtpEnable()) { - // 多端口 - if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { - tcpActiveHandler(device, channelId, contentString, mediaServerItem, timeOutTaskKey, ssrcInfo, callback); - } - }else { - // 单端口 - if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { - logger.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); - } - - } - }else { - logger.info("[Invite 200OK] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); - // ssrc 不一致 - if (mediaServerItem.isRtpEnable()) { - // 多端口 - if (device.isSsrcCheck()) { - // ssrc检验 - // 更新ssrc - logger.info("[Invite 200OK] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); - // 释放ssrc - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse); - if (!result) { - try { - logger.warn("[Invite 200OK] 更新ssrc失败,停止点播 {}/{}", device.getDeviceId(), channelId); - cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null); - } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { - logger.error("[命令发送失败] 停止播放, 发送BYE: {}", e.getMessage()); - } - - dynamicTask.stop(timeOutTaskKey); - // 释放ssrc - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); - - callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), - "下级自定义了ssrc,重新设置收流信息失败", null); - inviteStreamService.call(inviteSessionType, device.getDeviceId(), channelId, null, - InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), - "下级自定义了ssrc,重新设置收流信息失败", null); - - }else { - ssrcInfo.setSsrc(ssrcInResponse); - inviteInfo.setSsrcInfo(ssrcInfo); - inviteInfo.setStream(ssrcInfo.getStream()); - if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { - if (mediaServerItem.isRtpEnable()) { - tcpActiveHandler(device, channelId, contentString, mediaServerItem, timeOutTaskKey, ssrcInfo, callback); - }else { - logger.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); - } - } - inviteStreamService.updateInviteInfo(inviteInfo); - } - } - }else { - if (ssrcInResponse != null) { - // 单端口 - // 重新订阅流上线 - SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(inviteInfo.getDeviceId(), - inviteInfo.getChannelId(), null, inviteInfo.getStream()); - streamSession.remove(inviteInfo.getDeviceId(), - inviteInfo.getChannelId(), inviteInfo.getStream()); - inviteStreamService.updateInviteInfoForSSRC(inviteInfo, ssrcInResponse); - streamSession.put(device.getDeviceId(), channelId, ssrcTransaction.getCallId(), - inviteInfo.getStream(), ssrcInResponse, mediaServerItem.getId(), (SIPResponse) responseEvent.getResponse(), inviteSessionType); - } - } - } - } - - - - - @Override - public void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback callback) { - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - return; - } - DeviceChannel channel = channelService.getOne(deviceId, channelId); - if (channel == null) { - return; - } - MediaServer newMediaServerItem = this.getNewMediaServerItem(device); - if (newMediaServerItem == null) { - callback.run(InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getCode(), - InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getMsg(), - null); - return; - } - // 录像下载不使用固定流地址,固定流地址会导致如果开始时间与结束时间一致时文件错误的叠加在一起 - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, null, device.isSsrcCheck(), true, 0, false,!channel.getHasAudio(), false, device.getStreamModeForParam()); - download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed, callback); - } - - - @Override - public void download(MediaServer mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback callback) { - if (mediaServerItem == null || ssrcInfo == null) { - callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), - InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), - null); - return; - } - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), - "设备:" + deviceId + "不存在", - null); - return; - } - logger.info("[录像下载] deviceId: {}, channelId: {}, 下载速度:{}, 收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, downloadSpeed, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); - // 初始化redis中的invite消息状态 - InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo, - mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.DOWNLOAD, - InviteSessionStatus.ready); - inviteStreamService.updateInviteInfo(inviteInfo); - String downLoadTimeOutTaskKey = UUID.randomUUID().toString(); - dynamicTask.startDelay(downLoadTimeOutTaskKey, () -> { - logger.warn(String.format("录像下载请求超时,deviceId:%s ,channelId:%s", deviceId, channelId)); - inviteStreamService.removeInviteInfo(inviteInfo); - callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode(), - InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getMsg(), null); - - // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 - try { - cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); - } catch (InvalidArgumentException | ParseException | SipException e) { - logger.error("[录像流]录像下载请求超时, 发送BYE失败 {}", e.getMessage()); - } catch (SsrcTransactionNotFoundException e) { - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); - streamSession.remove(deviceId, channelId, ssrcInfo.getStream()); - } - }, userSetting.getPlayTimeout()); - - SipSubscribe.Event errorEvent = event -> { - dynamicTask.stop(downLoadTimeOutTaskKey); - callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode(), - String.format("录像下载失败, 错误码: %s, %s", event.statusCode, event.msg), null); - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); - inviteStreamService.removeInviteInfo(inviteInfo); - }; - HookSubscribe.Event hookEvent = (hookData) -> { - logger.info("[录像下载]收到订阅消息: " + hookData); - dynamicTask.stop(downLoadTimeOutTaskKey); - StreamInfo streamInfo = onPublishHandlerForDownload(hookData.getMediaServer(), hookData.getMediaInfo(), deviceId, channelId, startTime, endTime); - if (streamInfo == null) { - logger.warn("[录像下载] 获取流地址信息失败"); - callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(), - InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null); - return; - } - callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); - logger.info("[录像下载] 调用成功 deviceId: {}, channelId: {}, 开始时间: {}, 结束时间: {}", device.getDeviceId(), channelId, startTime, endTime); - }; - try { - cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, - hookEvent, errorEvent, eventResult ->{ - // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题 - InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId, - downLoadTimeOutTaskKey, callback, inviteInfo, InviteSessionType.DOWNLOAD); - - // 注册录像回调事件,录像下载结束后写入下载地址 - HookSubscribe.Event hookEventForRecord = (hookData) -> { - logger.info("[录像下载] 收到录像写入磁盘消息: , {}/{}-{}", - inviteInfo.getDeviceId(), inviteInfo.getChannelId(), ssrcInfo.getStream()); - logger.info("[录像下载] 收到录像写入磁盘消息内容: " + hookData); - RecordInfo recordInfo = hookData.getRecordInfo(); - String filePath = recordInfo.getFilePath(); - 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, 60*15L); - }; - Hook hook = Hook.getInstance(HookType.on_record_mp4, "rtp", ssrcInfo.getStream(), mediaServerItem.getId()); - // 设置过期时间,下载失败时自动处理订阅数据 - hook.setExpireTime(System.currentTimeMillis() + 24 * 60 * 60 * 1000); - subscribe.addSubscribe(hook, hookEventForRecord); - }); - } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 录像下载: {}", e.getMessage()); - - SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(); - eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent; - eventResult.statusCode = -1; - eventResult.msg = "命令发送失败"; - errorEvent.response(eventResult); - } - } - - @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.getStreamInfo().getProgress() == 1) { - return inviteInfo.getStreamInfo(); - } - - // 获取当前已下载时长 - String mediaServerId = inviteInfo.getStreamInfo().getMediaServerId(); - MediaServer 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; - } - String app = "rtp"; - Long duration = mediaServerService.updateDownloadProcess(mediaServerItem, app, stream); - if (duration == null || 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(MediaServer mediaServerItemInuse, MediaInfo mediaInfo, String deviceId, String channelId, String startTime, String endTime) { - StreamInfo streamInfo = onPublishHandler(mediaServerItemInuse, mediaInfo, deviceId, channelId); - if (streamInfo != null) { - streamInfo.setProgress(0); - streamInfo.setStartTime(startTime); - streamInfo.setEndTime(endTime); - InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, streamInfo.getStream()); - if (inviteInfo != null) { - logger.info("[录像下载] 更新invite消息中的stream信息"); - inviteInfo.setStatus(InviteSessionStatus.ok); - inviteInfo.setStreamInfo(streamInfo); - inviteStreamService.updateInviteInfo(inviteInfo); - } - } - return streamInfo; - } - - - public StreamInfo onPublishHandler(MediaServer mediaServerItem, MediaInfo mediaInfo, String deviceId, String channelId) { - StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(mediaServerItem, "rtp", mediaInfo.getStream(), mediaInfo, null); - streamInfo.setDeviceID(deviceId); - streamInfo.setChannelId(channelId); - return streamInfo; - } - - - @Override - public void zlmServerOffline(String mediaServerId) { - // 处理正在向上推流的上级平台 - List sendRtpItems = redisCatchStorage.querySendRTPServer(null); - if (sendRtpItems.size() > 0) { - for (SendRtpItem sendRtpItem : sendRtpItems) { - if (sendRtpItem.getMediaServerId().equals(mediaServerId)) { - ParentPlatform platform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId()); - try { - sipCommanderFroPlatform.streamByeCmd(platform, sendRtpItem.getCallId()); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); - } - } - } - } - // 处理正在观看的国标设备 - List allSsrc = streamSession.getAllSsrc(); - if (allSsrc.size() > 0) { - for (SsrcTransaction ssrcTransaction : allSsrc) { - if (ssrcTransaction.getMediaServerId().equals(mediaServerId)) { - Device device = deviceService.getDevice(ssrcTransaction.getDeviceId()); - if (device == null) { - continue; - } - try { - cmder.streamByeCmd(device, ssrcTransaction.getChannelId(), - ssrcTransaction.getStream(), null); - } catch (InvalidArgumentException | ParseException | SipException | - SsrcTransactionNotFoundException e) { - logger.error("[zlm离线]为正在使用此zlm的设备, 发送BYE失败 {}", e.getMessage()); - } - } - } - } - } - - @Override - public AudioBroadcastResult audioBroadcast(Device device, String channelId, Boolean broadcastMode) { - // TODO 必须多端口模式才支持语音喊话鹤语音对讲 - if (device == null || channelId == null) { - return null; - } - logger.info("[语音喊话] device: {}, channel: {}", device.getDeviceId(), channelId); - DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId); - if (deviceChannel == null) { - logger.warn("开启语音广播的时候未找到通道: {}", channelId); - return null; - } - MediaServer mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(null); - if (broadcastMode == null) { - broadcastMode = true; - } - String app = broadcastMode?"broadcast":"talk"; - String stream = device.getDeviceId() + "_" + channelId; - AudioBroadcastResult audioBroadcastResult = new AudioBroadcastResult(); - audioBroadcastResult.setApp(app); - audioBroadcastResult.setStream(stream); - audioBroadcastResult.setStreamInfo(new StreamContent(mediaServerService.getStreamInfoByAppAndStream(mediaServerItem, app, stream, null, null, null, false))); - audioBroadcastResult.setCodec("G.711"); - return audioBroadcastResult; - } - - @Override - public boolean audioBroadcastCmd(Device device, String channelId, MediaServer mediaServerItem, String app, String stream, int timeout, boolean isFromPlatform, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException { - if (device == null || channelId == null) { - return false; - } - logger.info("[语音喊话] device: {}, channel: {}", device.getDeviceId(), channelId); - DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId); - if (deviceChannel == null) { - logger.warn("开启语音广播的时候未找到通道: {}", channelId); - event.call("开启语音广播的时候未找到通道"); - return false; - } - // 查询通道使用状态 - if (audioBroadcastManager.exit(device.getDeviceId(), channelId)) { - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null); - if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) { - // 查询流是否存在,不存在则认为是异常状态 - Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStream()); - if (streamReady) { - logger.warn("语音广播已经开启: {}", channelId); - event.call("语音广播已经开启"); - return false; - } else { - stopAudioBroadcast(device.getDeviceId(), channelId); - } - } - } - - // 发送通知 - cmder.audioBroadcastCmd(device, channelId, eventResultForOk -> { - // 发送成功 - AudioBroadcastCatch audioBroadcastCatch = new AudioBroadcastCatch(device.getDeviceId(), channelId, mediaServerItem, app, stream, event, AudioBroadcastCatchStatus.Ready, isFromPlatform); - audioBroadcastManager.update(audioBroadcastCatch); - // 等待invite消息, 超时则结束 - String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId(); - if (!SipUtils.isFrontEnd(device.getDeviceId())) { - key += audioBroadcastCatch.getChannelId(); - } - dynamicTask.startDelay(key, ()->{ - logger.info("[语音广播]等待invite消息超时:{}/{}", device.getDeviceId(), channelId); - stopAudioBroadcast(device.getDeviceId(), channelId); - }, 10*1000); - }, eventResultForError -> { - // 发送失败 - logger.error("语音广播发送失败: {}:{}", channelId, eventResultForError.msg); - event.call("语音广播发送失败"); - stopAudioBroadcast(device.getDeviceId(), channelId); - }); - return true; - } - - @Override - public boolean audioBroadcastInUse(Device device, String channelId) { - if (audioBroadcastManager.exit(device.getDeviceId(), channelId)) { - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null); - if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) { - // 查询流是否存在,不存在则认为是异常状态 - MediaServer mediaServerServiceOne = mediaServerService.getOne(sendRtpItem.getMediaServerId()); - Boolean streamReady = mediaServerService.isStreamReady(mediaServerServiceOne, sendRtpItem.getApp(), sendRtpItem.getStream()); - if (streamReady) { - logger.warn("语音广播通道使用中: {}", channelId); - return true; - } - } - } - return false; - } - - - @Override - public void stopAudioBroadcast(String deviceId, String channelId) { - logger.info("[停止对讲] 设备:{}, 通道:{}", deviceId, channelId); - List audioBroadcastCatchList = new ArrayList<>(); - if (channelId == null) { - audioBroadcastCatchList.addAll(audioBroadcastManager.get(deviceId)); - } else { - audioBroadcastCatchList.add(audioBroadcastManager.get(deviceId, channelId)); - } - if (audioBroadcastCatchList.size() > 0) { - for (AudioBroadcastCatch audioBroadcastCatch : audioBroadcastCatchList) { - Device device = deviceService.getDevice(deviceId); - if (device == null || audioBroadcastCatch == null) { - return; - } - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(deviceId, audioBroadcastCatch.getChannelId(), null, null); - if (sendRtpItem != null) { - redisCatchStorage.deleteSendRTPServer(deviceId, sendRtpItem.getChannelId(), null, null); - MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); - mediaServerService.stopSendRtp(mediaServer, sendRtpItem.getApp(), sendRtpItem.getStream(), null); - try { - cmder.streamByeCmdForDeviceInvite(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null); - } catch (InvalidArgumentException | ParseException | SipException | - SsrcTransactionNotFoundException e) { - logger.error("[消息发送失败] 发送语音喊话BYE失败"); - } - } - - audioBroadcastManager.del(deviceId, channelId); - } - } - } - - - @Override - public void zlmServerOnline(String mediaServerId) { - // TODO 查找之前的点播,流如果不存在则给下级发送bye -// MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); -// zlmresTfulUtils.getMediaList(mediaServerItem, (mediaList ->{ -// Integer code = mediaList.getInteger("code"); -// if (code == 0) { -// JSONArray data = mediaList.getJSONArray("data"); -// if (data == null || data.size() == 0) { -// zlmServerOffline(mediaServerId); -// }else { -// Map mediaListMap = new HashMap<>(); -// for (int i = 0; i < data.size(); i++) { -// JSONObject json = data.getJSONObject(i); -// String app = json.getString("app"); -// if ("rtp".equals(app)) { -// String stream = json.getString("stream"); -// if (mediaListMap.get(stream) != null) { -// continue; -// } -// mediaListMap.put(stream, json); -// // 处理正在观看的国标设备 -// List ssrcTransactions = streamSession.getSsrcTransactionForAll(null, null, null, stream); -// if (ssrcTransactions.size() > 0) { -// for (SsrcTransaction ssrcTransaction : ssrcTransactions) { -// if(ssrcTransaction.getMediaServerId().equals(mediaServerId)) { -// cmder.streamByeCmd(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), -// ssrcTransaction.getStream(), null); -// } -// } -// } -// } -// } -// if (mediaListMap.size() > 0 ) { -// // 处理正在向上推流的上级平台 -// List sendRtpItems = redisCatchStorage.querySendRTPServer(null); -// if (sendRtpItems.size() > 0) { -// for (SendRtpItem sendRtpItem : sendRtpItems) { -// if (sendRtpItem.getMediaServerId().equals(mediaServerId)) { -// if (mediaListMap.get(sendRtpItem.getStreamId()) == null) { -// ParentPlatform platform = storager.queryPlatformByServerGBId(sendRtpItem.getPlatformId()); -// sipCommanderFroPlatform.streamByeCmd(platform, sendRtpItem.getCallId()); -// } -// } -// } -// } -// } -// } -// } -// })); - } - - @Override - public void pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException { - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); - if (null == inviteInfo || inviteInfo.getStreamInfo() == null) { - logger.warn("streamId不存在!"); - throw new ServiceException("streamId不存在"); - } - inviteInfo.getStreamInfo().setPause(true); - inviteStreamService.updateInviteInfo(inviteInfo); - MediaServer mediaServerItem = mediaServerService.getOne(inviteInfo.getStreamInfo().getMediaServerId()); - if (null == mediaServerItem) { - logger.warn("mediaServer 不存在!"); - throw new ServiceException("mediaServer不存在"); - } - // zlm 暂停RTP超时检查 - // 使用zlm中的流ID - String streamKey = inviteInfo.getStream(); - if (!mediaServerItem.isRtpEnable()) { - streamKey = Long.toHexString(Long.parseLong(inviteInfo.getSsrcInfo().getSsrc())).toUpperCase(); - } - Boolean result = mediaServerService.pauseRtpCheck(mediaServerItem, streamKey); - if (!result) { - throw new ServiceException("暂停RTP接收失败"); - } - Device device = storager.queryVideoDevice(inviteInfo.getDeviceId()); - cmder.playPauseCmd(device, inviteInfo.getStreamInfo()); - } - - @Override - public void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException { - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId); - if (null == inviteInfo || inviteInfo.getStreamInfo() == null) { - logger.warn("streamId不存在!"); - throw new ServiceException("streamId不存在"); - } - inviteInfo.getStreamInfo().setPause(false); - inviteStreamService.updateInviteInfo(inviteInfo); - MediaServer mediaServerItem = mediaServerService.getOne(inviteInfo.getStreamInfo().getMediaServerId()); - if (null == mediaServerItem) { - logger.warn("mediaServer 不存在!"); - throw new ServiceException("mediaServer不存在"); - } - // zlm 暂停RTP超时检查 - // 使用zlm中的流ID - String streamKey = inviteInfo.getStream(); - if (!mediaServerItem.isRtpEnable()) { - streamKey = Long.toHexString(Long.parseLong(inviteInfo.getSsrcInfo().getSsrc())).toUpperCase(); - } - boolean result = mediaServerService.resumeRtpCheck(mediaServerItem, streamKey); - if (!result) { - throw new ServiceException("继续RTP接收失败"); - } - Device device = storager.queryVideoDevice(inviteInfo.getDeviceId()); - cmder.playResumeCmd(device, inviteInfo.getStreamInfo()); - } - - @Override - public void startPushStream(SendRtpItem sendRtpItem, SIPResponse sipResponse, ParentPlatform platform, CallIdHeader callIdHeader) { - // 开始发流 - MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); - - if (mediaInfo != null) { - try { - if (sendRtpItem.isTcpActive()) { - mediaServerService.startSendRtpPassive(mediaInfo, sendRtpItem, null); - } else { - mediaServerService.startSendRtp(mediaInfo, sendRtpItem); - } - redisCatchStorage.sendPlatformStartPlayMsg(sendRtpItem, platform); - }catch (ControllerException e) { - logger.error("RTP推流失败: {}", e.getMessage()); - startSendRtpStreamFailHand(sendRtpItem, platform, callIdHeader); - return; - } - - logger.info("RTP推流成功[ {}/{} ],{}, ", sendRtpItem.getApp(), sendRtpItem.getStream(), - sendRtpItem.isTcpActive()?"被动发流": sendRtpItem.getIp() + ":" + sendRtpItem.getPort()); - - } - } - - @Override - public void startSendRtpStreamFailHand(SendRtpItem sendRtpItem, ParentPlatform platform, CallIdHeader callIdHeader) { - if (sendRtpItem.isOnlyAudio()) { - Device device = deviceService.getDevice(sendRtpItem.getDeviceId()); - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); - if (audioBroadcastCatch != null) { - try { - cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null); - } catch (SipException | ParseException | InvalidArgumentException | - SsrcTransactionNotFoundException exception) { - logger.error("[命令发送失败] 停止语音对讲: {}", exception.getMessage()); - } - } - } else { - if (platform != null) { - // 向上级平台 - try { - commanderForPlatform.streamByeCmd(platform, callIdHeader.getCallId()); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); - } - } - - } - } - - @Override - public void talkCmd(Device device, String channelId, MediaServer mediaServerItem, String stream, AudioBroadcastEvent event) { - if (device == null || channelId == null) { - return; - } - // TODO 必须多端口模式才支持语音喊话鹤语音对讲 - logger.info("[语音对讲] device: {}, channel: {}", device.getDeviceId(), channelId); - DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId); - if (deviceChannel == null) { - logger.warn("开启语音对讲的时候未找到通道: {}", channelId); - event.call("开启语音对讲的时候未找到通道"); - return; - } - // 查询通道使用状态 - if (audioBroadcastManager.exit(device.getDeviceId(), channelId)) { - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null); - if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) { - // 查询流是否存在,不存在则认为是异常状态 - MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); - Boolean streamReady = mediaServerService.isStreamReady(mediaServer, sendRtpItem.getApp(), sendRtpItem.getStream()); - if (streamReady) { - logger.warn("[语音对讲] 正在语音广播,无法开启语音通话: {}", channelId); - event.call("正在语音广播"); - return; - } else { - stopAudioBroadcast(device.getDeviceId(), channelId); - } - } - } - - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, stream, null); - if (sendRtpItem != null) { - MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); - Boolean streamReady = mediaServerService.isStreamReady(mediaServer, "rtp", sendRtpItem.getReceiveStream()); - if (streamReady) { - logger.warn("[语音对讲] 进行中: {}", channelId); - event.call("语音对讲进行中"); - return; - } else { - stopTalk(device, channelId); - } - } - - talk(mediaServerItem, device, channelId, stream, (hookData) -> { - logger.info("[语音对讲] 收到设备发来的流"); - }, eventResult -> { - logger.warn("[语音对讲] 失败,{}/{}, 错误码 {} {}", device.getDeviceId(), channelId, eventResult.statusCode, eventResult.msg); - event.call("失败,错误码 " + eventResult.statusCode + ", " + eventResult.msg); - }, () -> { - logger.warn("[语音对讲] 失败,{}/{} 超时", device.getDeviceId(), channelId); - event.call("失败,超时 "); - stopTalk(device, channelId); - }, errorMsg -> { - logger.warn("[语音对讲] 失败,{}/{} {}", device.getDeviceId(), channelId, errorMsg); - event.call(errorMsg); - stopTalk(device, channelId); - }); - } - - private void stopTalk(Device device, String channelId) { - stopTalk(device, channelId, null); - } - - @Override - public void stopTalk(Device device, String channelId, Boolean streamIsReady) { - logger.info("[语音对讲] 停止, {}/{}", device.getDeviceId(), channelId); - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null); - if (sendRtpItem == null) { - logger.info("[语音对讲] 停止失败, 未找到发送信息,可能已经停止"); - return; - } - // 停止向设备推流 - String mediaServerId = sendRtpItem.getMediaServerId(); - if (mediaServerId == null) { - return; - } - - MediaServer mediaServer = mediaServerService.getOne(mediaServerId); - - if (streamIsReady == null || streamIsReady) { - mediaServerService.stopSendRtp(mediaServer, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc()); - } - - ssrcFactory.releaseSsrc(mediaServerId, sendRtpItem.getSsrc()); - - SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, null, sendRtpItem.getStream()); - if (ssrcTransaction != null) { - try { - cmder.streamByeCmd(device, channelId, sendRtpItem.getStream(), null); - } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { - logger.info("[语音对讲] 停止消息发送失败,可能已经停止"); - } - } - redisCatchStorage.deleteSendRTPServer(device.getDeviceId(), channelId,null, null); - } - - @Override - public void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback) { - Device device = deviceService.getDevice(deviceId); - if (device == null) { - errorCallback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(), null); - return; - } - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); - if (inviteInfo != null) { - if (inviteInfo.getStreamInfo() != null) { - // 已存在线直接截图 - MediaServer mediaServerItemInuse = mediaServerService.getOne(inviteInfo.getStreamInfo().getMediaServerId()); - String streamUrl; - if (mediaServerItemInuse.getRtspPort() != 0) { - streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", inviteInfo.getStreamInfo().getStream()); - }else { - streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.mp4", mediaServerItemInuse.getHttpPort(), "rtp", inviteInfo.getStreamInfo().getStream()); - } - String path = "snap"; - // 请求截图 - logger.info("[请求截图]: " + fileName); - mediaServerService.getSnap(mediaServerItemInuse, streamUrl, 15, 1, path, fileName); - File snapFile = new File(path + File.separator + fileName); - if (snapFile.exists()) { - errorCallback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), snapFile.getAbsoluteFile()); - }else { - errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); - } - return; - } - } - - MediaServer newMediaServerItem = getNewMediaServerItem(device); - play(newMediaServerItem, deviceId, channelId, null, (code, msg, data)->{ - if (code == InviteErrorCode.SUCCESS.getCode()) { - InviteInfo inviteInfoForPlay = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); - if (inviteInfoForPlay != null && inviteInfoForPlay.getStreamInfo() != null) { - getSnap(deviceId, channelId, fileName, errorCallback); - }else { - errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); - } - }else { - errorCallback.run(InviteErrorCode.FAIL.getCode(), InviteErrorCode.FAIL.getMsg(), null); - } - }); - } - - @Override - public void stopPlay(Device device, String channelId) { - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); - if (inviteInfo == null) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "点播未找到"); - } - if (InviteSessionStatus.ok == inviteInfo.getStatus()) { - try { - logger.info("[停止点播] {}/{}", device.getDeviceId(), channelId); - cmder.streamByeCmd(device, channelId, inviteInfo.getStream(), null, null); - } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { - logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage()); - throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); - } - } - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId); - storager.stopPlay(device.getDeviceId(), channelId); - channelService.stopPlay(device.getDeviceId(), channelId); - if (inviteInfo.getStreamInfo() != null) { - mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStream()); - } - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RtpServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RtpServerServiceImpl.java new file mode 100644 index 000000000..ccd7780ec --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RtpServerServiceImpl.java @@ -0,0 +1,162 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.genersoft.iot.vmp.conf.DynamicTask; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.OpenRTPServerResult; +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; +import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; +import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.media.event.hook.Hook; +import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; +import com.genersoft.iot.vmp.media.event.hook.HookType; +import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent; +import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent; +import com.genersoft.iot.vmp.media.service.IMediaServerService; +import com.genersoft.iot.vmp.service.IReceiveRtpServerService; +import com.genersoft.iot.vmp.service.bean.ErrorCallback; +import com.genersoft.iot.vmp.service.bean.InviteErrorCode; +import com.genersoft.iot.vmp.service.bean.RTPServerParam; +import com.genersoft.iot.vmp.service.bean.SSRCInfo; +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.stereotype.Service; + +import java.util.UUID; + +@Slf4j +@Service +public class RtpServerServiceImpl implements IReceiveRtpServerService { + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private SSRCFactory ssrcFactory; + + @Autowired + private UserSetting userSetting; + + @Autowired + private HookSubscribe subscribe; + + @Autowired + private SipInviteSessionManager sessionManager; + + /** + * 流到来的处理 + */ + @Async("taskExecutor") + @org.springframework.context.event.EventListener + public void onApplicationEvent(MediaArrivalEvent event) { + + } + + /** + * 流离开的处理 + */ + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(MediaDepartureEvent event) { + + } + + @Override + public SSRCInfo openRTPServer(RTPServerParam rtpServerParam, ErrorCallback callback) { + if (callback == null) { + log.warn("[开启RTP收流] 失败,回调为NULL"); + return null; + } + if (rtpServerParam.getMediaServerItem() == null) { + log.warn("[开启RTP收流] 失败,媒体节点为NULL"); + return null; + } + + // 获取mediaServer可用的ssrc + final String ssrc; + if (rtpServerParam.getPresetSsrc() != null) { + ssrc = rtpServerParam.getPresetSsrc(); + }else { + if (rtpServerParam.isPlayback()) { + ssrc = ssrcFactory.getPlayBackSsrc(rtpServerParam.getMediaServerItem().getId()); + }else { + ssrc = ssrcFactory.getPlaySsrc(rtpServerParam.getMediaServerItem().getId()); + } + } + final String streamId; + if (rtpServerParam.getStreamId() == null) { + streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase(); + }else { + streamId = rtpServerParam.getStreamId(); + } + if (rtpServerParam.isSsrcCheck() && rtpServerParam.getTcpMode() > 0) { + // 目前zlm不支持 tcp模式更新ssrc,暂时关闭ssrc校验 + log.warn("[openRTPServer] 平台对接时下级可能自定义ssrc,但是tcp模式zlm收流目前无法更新ssrc,可能收流超时,此时请使用udp收流或者关闭ssrc校验"); + } + int rtpServerPort; + if (rtpServerParam.getMediaServerItem().isRtpEnable()) { + rtpServerPort = mediaServerService.createRTPServer(rtpServerParam.getMediaServerItem(), streamId, + rtpServerParam.isSsrcCheck() ? Long.parseLong(ssrc) : 0, rtpServerParam.getPort(), rtpServerParam.isOnlyAuto(), + rtpServerParam.isDisableAudio(), rtpServerParam.isReUsePort(), rtpServerParam.getTcpMode()); + } else { + rtpServerPort = rtpServerParam.getMediaServerItem().getRtpProxyPort(); + } + if (rtpServerPort == 0) { + callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "开启RTPServer失败", null); + // 释放ssrc + if (rtpServerParam.getPresetSsrc() == null) { + ssrcFactory.releaseSsrc(rtpServerParam.getMediaServerItem().getId(), ssrc); + } + return null; + } + + // 设置流超时的定时任务 + String timeOutTaskKey = UUID.randomUUID().toString(); + + SSRCInfo ssrcInfo = new SSRCInfo(rtpServerPort, ssrc, streamId, timeOutTaskKey); + OpenRTPServerResult openRTPServerResult = new OpenRTPServerResult(); + openRTPServerResult.setSsrcInfo(ssrcInfo); + + Hook rtpHook = Hook.getInstance(HookType.on_media_arrival, "rtp", streamId, rtpServerParam.getMediaServerItem().getId()); + dynamicTask.startDelay(timeOutTaskKey, () -> { + // 收流超时 + // 释放ssrc + if (rtpServerParam.getPresetSsrc() == null) { + ssrcFactory.releaseSsrc(rtpServerParam.getMediaServerItem().getId(), ssrc); + } + // 关闭收流端口 + mediaServerService.closeRTPServer(rtpServerParam.getMediaServerItem(), streamId); + subscribe.removeSubscribe(rtpHook); + callback.run(InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), openRTPServerResult); + }, userSetting.getPlayTimeout()); + // 开启流到来的监听 + subscribe.addSubscribe(rtpHook, (hookData) -> { + dynamicTask.stop(timeOutTaskKey); + // hook响应 + openRTPServerResult.setHookData(hookData); + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), openRTPServerResult); + subscribe.removeSubscribe(rtpHook); + }); + + return ssrcInfo; + } + + @Override + public void closeRTPServer(MediaServer mediaServer, SSRCInfo ssrcInfo) { + if (mediaServer == null) { + return; + } + if (ssrcInfo.getTimeOutTaskKey() != null) { + dynamicTask.stop(ssrcInfo.getTimeOutTaskKey()); + } + if (ssrcInfo.getSsrc() != null) { + // 释放ssrc + ssrcFactory.releaseSsrc(mediaServer.getId(), ssrcInfo.getSsrc()); + } + mediaServerService.closeRTPServer(mediaServer, ssrcInfo.getStream()); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/SendRtpServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/SendRtpServerServiceImpl.java new file mode 100644 index 000000000..c4cf7a0bd --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/SendRtpServerServiceImpl.java @@ -0,0 +1,196 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.PlayException; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; +import com.genersoft.iot.vmp.gb28181.conf.StackLoggerImpl; +import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; +import com.genersoft.iot.vmp.service.ISendRtpServerService; +import com.genersoft.iot.vmp.utils.JsonUtil; +import com.genersoft.iot.vmp.utils.redis.RedisUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +@Slf4j +public class SendRtpServerServiceImpl implements ISendRtpServerService { + + @Autowired + private UserSetting userSetting; + + @Autowired + private SendRtpPortManager sendRtpPortManager; + + @Autowired + private RedisTemplate redisTemplate; + @Autowired + private StackLoggerImpl stackLoggerImpl; + + @Override + public SendRtpInfo createSendRtpInfo(MediaServer mediaServer, String ip, Integer port, String ssrc, String requesterId, + String deviceId, Integer channelId, Boolean isTcp, Boolean rtcp) { + int localPort = sendRtpPortManager.getNextPort(mediaServer); + if (localPort == 0) { + return null; + } + return SendRtpInfo.getInstance(localPort, mediaServer, ip, port, ssrc, deviceId, null, channelId, + isTcp, rtcp, userSetting.getServerId()); + } + + @Override + public SendRtpInfo createSendRtpInfo(MediaServer mediaServer, String ip, Integer port, String ssrc, String platformId, + String app, String stream, Integer channelId, Boolean tcp, Boolean rtcp){ + + int localPort = sendRtpPortManager.getNextPort(mediaServer); + if (localPort <= 0) { + throw new PlayException(javax.sip.message.Response.SERVER_INTERNAL_ERROR, "server internal error"); + } + SendRtpInfo sendRtpInfo = SendRtpInfo.getInstance(localPort, mediaServer, ip, port, ssrc, null, platformId, channelId, + tcp, rtcp, userSetting.getServerId()); + if (sendRtpInfo == null) { + return null; + } + sendRtpInfo.setApp(app); + sendRtpInfo.setStream(stream); + return sendRtpInfo; + } + + @Override + public void update(SendRtpInfo sendRtpItem) { + redisTemplate.opsForValue().set(VideoManagerConstants.SEND_RTP_INFO_CALLID + sendRtpItem.getCallId(), sendRtpItem); + redisTemplate.opsForValue().set(VideoManagerConstants.SEND_RTP_INFO_STREAM + sendRtpItem.getStream() + ":" + sendRtpItem.getTargetId(), sendRtpItem); + redisTemplate.opsForValue().set(VideoManagerConstants.SEND_RTP_INFO_CHANNEL + sendRtpItem.getChannelId() + ":" + sendRtpItem.getTargetId(), sendRtpItem); + } + + @Override + public SendRtpInfo queryByChannelId(Integer channelId, String targetId) { + String key = VideoManagerConstants.SEND_RTP_INFO_CHANNEL + channelId + ":" + targetId; + return JsonUtil.redisJsonToObject(redisTemplate, key, SendRtpInfo.class); + } + + @Override + public SendRtpInfo queryByCallId(String callId) { + String key = VideoManagerConstants.SEND_RTP_INFO_CALLID + callId; + return JsonUtil.redisJsonToObject(redisTemplate, key, SendRtpInfo.class); + } + + @Override + public SendRtpInfo queryByStream(String stream, String targetId) { + String key = VideoManagerConstants.SEND_RTP_INFO_STREAM + stream + ":" + targetId; + return JsonUtil.redisJsonToObject(redisTemplate, key, SendRtpInfo.class); + } + + @Override + public List queryByStream(String stream) { + String key = VideoManagerConstants.SEND_RTP_INFO_STREAM + stream + ":*"; + List queryResult = RedisUtil.scan(redisTemplate, key); + List result= new ArrayList<>(); + + for (Object o : queryResult) { + String keyItem = (String) o; + result.add((SendRtpInfo) redisTemplate.opsForValue().get(keyItem)); + } + + return result; + } + + /** + * 删除RTP推送信息缓存 + */ + @Override + public void delete(SendRtpInfo sendRtpInfo) { + if (sendRtpInfo == null) { + return; + } + redisTemplate.delete(VideoManagerConstants.SEND_RTP_INFO_CALLID + sendRtpInfo.getCallId()); + redisTemplate.delete(VideoManagerConstants.SEND_RTP_INFO_STREAM + sendRtpInfo.getStream() + ":" + sendRtpInfo.getTargetId()); + redisTemplate.delete(VideoManagerConstants.SEND_RTP_INFO_CHANNEL + sendRtpInfo.getChannelId() + ":" + sendRtpInfo.getTargetId()); + } + @Override + public void deleteByCallId(String callId) { + SendRtpInfo sendRtpInfo = queryByCallId(callId); + if (sendRtpInfo == null) { + return; + } + delete(sendRtpInfo); + } + @Override + public void deleteByStream(String stream, String targetId) { + SendRtpInfo sendRtpInfo = queryByStream(stream, targetId); + if (sendRtpInfo == null) { + return; + } + delete(sendRtpInfo); + } + + @Override + public void deleteByStream(String stream) { + List sendRtpInfos = queryByStream(stream); + for (SendRtpInfo sendRtpInfo : sendRtpInfos) { + delete(sendRtpInfo); + } + } + + @Override + public void deleteByChannel(Integer channelId, String targetId) { + SendRtpInfo sendRtpInfo = queryByChannelId(channelId, targetId); + if (sendRtpInfo == null) { + return; + } + delete(sendRtpInfo); + } + + @Override + public List queryByChannelId(int channelId) { + String key = VideoManagerConstants.SEND_RTP_INFO_CHANNEL + channelId + ":*"; + List queryResult = RedisUtil.scan(redisTemplate, key); + List result= new ArrayList<>(); + + for (Object o : queryResult) { + String keyItem = (String) o; + result.add((SendRtpInfo) redisTemplate.opsForValue().get(keyItem)); + } + + return result; + } + + @Override + public List queryAll() { + String key = VideoManagerConstants.SEND_RTP_INFO_CALLID + + userSetting.getServerId() + ":*"; + List queryResult = RedisUtil.scan(redisTemplate, key); + List result= new ArrayList<>(); + + for (Object o : queryResult) { + String keyItem = (String) o; + result.add((SendRtpInfo) redisTemplate.opsForValue().get(keyItem)); + } + + return result; + } + + /** + * 查询某个通道是否存在上级点播(RTP推送) + */ + @Override + public boolean isChannelSendingRTP(Integer channelId) { + List sendRtpInfoList = queryByChannelId(channelId); + return !sendRtpInfoList.isEmpty(); + } + + @Override + public List queryForPlatform(String platformId) { + List sendRtpInfos = queryAll(); + if (!sendRtpInfos.isEmpty()) { + sendRtpInfos.removeIf(sendRtpInfo -> !sendRtpInfo.isSendToPlatform() || !sendRtpInfo.getTargetId().equals(platformId)); + } + return sendRtpInfos; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java deleted file mode 100755 index 920228dfd..000000000 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java +++ /dev/null @@ -1,620 +0,0 @@ -package com.genersoft.iot.vmp.service.impl; - -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; -import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.conf.exception.ControllerException; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; -import com.genersoft.iot.vmp.media.bean.MediaInfo; -import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.media.event.hook.Hook; -import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; -import com.genersoft.iot.vmp.media.event.hook.HookType; -import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent; -import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent; -import com.genersoft.iot.vmp.media.event.media.MediaNotFoundEvent; -import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; -import com.genersoft.iot.vmp.service.IGbStreamService; -import com.genersoft.iot.vmp.service.IStreamProxyService; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; -import com.genersoft.iot.vmp.storager.dao.GbStreamMapper; -import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper; -import com.genersoft.iot.vmp.storager.dao.StreamProxyMapper; -import com.genersoft.iot.vmp.utils.DateUtil; -import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; -import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -import com.github.pagehelper.PageInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.event.EventListener; -import org.springframework.jdbc.datasource.DataSourceTransactionManager; -import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.util.CollectionUtils; -import org.springframework.util.ObjectUtils; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.function.Function; -import java.util.stream.Collectors; - -/** - * 视频代理业务 - */ -@Service -@DS("master") -public class StreamProxyServiceImpl implements IStreamProxyService { - - private final static Logger logger = LoggerFactory.getLogger(StreamProxyServiceImpl.class); - - @Autowired - private IVideoManagerStorage videoManagerStorager; - - @Autowired - private StreamProxyMapper streamProxyMapper; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - private IVideoManagerStorage storager; - - @Autowired - private UserSetting userSetting; - - @Autowired - private GbStreamMapper gbStreamMapper; - - @Autowired - private PlatformGbStreamMapper platformGbStreamMapper; - - @Autowired - private IGbStreamService gbStreamService; - - @Autowired - private IMediaServerService mediaServerService; - - @Autowired - private HookSubscribe hookSubscribe; - - @Autowired - private DynamicTask dynamicTask; - - @Autowired - DataSourceTransactionManager dataSourceTransactionManager; - - @Autowired - TransactionDefinition transactionDefinition; - - /** - * 流到来的处理 - */ - @Async("taskExecutor") - @org.springframework.context.event.EventListener - public void onApplicationEvent(MediaArrivalEvent event) { - if ("rtsp".equals(event.getSchema())) { - updateStatus(true, event.getApp(), event.getStream()); - } - } - - /** - * 流离开的处理 - */ - @Async("taskExecutor") - @EventListener - public void onApplicationEvent(MediaDepartureEvent event) { - if ("rtsp".equals(event.getSchema())) { - updateStatus(false, event.getApp(), event.getStream()); - } - } - - /** - * 流离开的处理 - */ - @Async("taskExecutor") - @EventListener - public void onApplicationEvent(MediaNotFoundEvent event) { - if ("rtp".equals(event.getApp())) { - return; - } - // 拉流代理 - StreamProxyItem streamProxyByAppAndStream = getStreamProxyByAppAndStream(event.getApp(), event.getStream()); - if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnableDisableNoneReader()) { - start(event.getApp(), event.getStream()); - } - } - - - @Override - public void save(StreamProxyItem param, GeneralCallback callback) { - MediaServer mediaServer; - if (ObjectUtils.isEmpty(param.getMediaServerId()) || "auto".equals(param.getMediaServerId())){ - mediaServer = mediaServerService.getMediaServerForMinimumLoad(null); - }else { - mediaServer = mediaServerService.getOne(param.getMediaServerId()); - } - if (mediaServer == null) { - logger.warn("保存代理未找到在线的ZLM..."); - throw new ControllerException(ErrorCode.ERROR100.getCode(), "保存代理未找到在线的ZLM"); - } - String dstUrl; - if ("ffmpeg".equalsIgnoreCase(param.getType())) { - - String ffmpegCmd = mediaServerService.getFfmpegCmd(mediaServer, 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中获取到输出格式"); - } - int port; - String schemaForUri; - if (schema.equalsIgnoreCase("rtsp")) { - port = mediaServer.getRtspPort(); - schemaForUri = schema; - }else if (schema.equalsIgnoreCase("flv")) { - port = mediaServer.getRtmpPort(); - schemaForUri = schema; - }else { - port = mediaServer.getRtmpPort(); - schemaForUri = schema; - } - - dstUrl = String.format("%s://%s:%s/%s/%s", schemaForUri, "127.0.0.1", port, param.getApp(), - param.getStream()); - }else { - dstUrl = String.format("rtsp://%s:%s/%s/%s", "127.0.0.1", mediaServer.getRtspPort(), param.getApp(), - param.getStream()); - } - param.setDstUrl(dstUrl); - logger.info("[拉流代理] 输出地址为:{}", dstUrl); - param.setMediaServerId(mediaServer.getId()); - boolean saveResult; - // 更新 - if (videoManagerStorager.queryStreamProxy(param.getApp(), param.getStream()) != null) { - saveResult = updateStreamProxy(param); - }else { // 新增 - saveResult = addStreamProxy(param); - } - if (!saveResult) { - callback.run(ErrorCode.ERROR100.getCode(), "保存失败", null); - return; - } - Hook hook = Hook.getInstance(HookType.on_media_arrival, param.getApp(), param.getStream(), mediaServer.getId()); - hookSubscribe.addSubscribe(hook, (hookData) -> { - StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream( - mediaServer, param.getApp(), param.getStream(), null, null); - callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo); - }); - if (param.isEnable()) { - String talkKey = UUID.randomUUID().toString(); - String delayTalkKey = UUID.randomUUID().toString(); - dynamicTask.startDelay(delayTalkKey, ()->{ - StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStreamWithCheck(param.getApp(), param.getStream(), mediaServer.getId(), false); - if (streamInfo != null) { - callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo); - }else { - dynamicTask.stop(talkKey); - callback.run(ErrorCode.ERROR100.getCode(), "超时", null); - } - }, 7000); - WVPResult result = addStreamProxyToZlm(param); - if (result != null && result.getCode() == 0) { - hookSubscribe.removeSubscribe(hook); - dynamicTask.stop(talkKey); - StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream( - mediaServer, param.getApp(), param.getStream(), null, null); - callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo); - }else { - param.setEnable(false); - // 直接移除 - if (param.isEnableRemoveNoneReader()) { - del(param.getApp(), param.getStream()); - }else { - updateStreamProxy(param); - } - if (result == null){ - callback.run(ErrorCode.ERROR100.getCode(), "记录已保存,启用失败", null); - }else { - callback.run(ErrorCode.ERROR100.getCode(), result.getMsg(), null); - } - } - } - else{ - StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream( - mediaServer, param.getApp(), param.getStream(), null, null); - callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo); - } - } - - private String getSchemaFromFFmpegCmd(String ffmpegCmd) { - ffmpegCmd = ffmpegCmd.replaceAll(" + ", " "); - String[] paramArray = ffmpegCmd.split(" "); - if (paramArray.length == 0) { - return null; - } - for (int i = 0; i < paramArray.length; i++) { - if (paramArray[i].equalsIgnoreCase("-f")) { - if (i + 1 < paramArray.length - 1) { - return paramArray[i+1]; - }else { - return null; - } - - } - } - return null; - } - - /** - * 新增代理流 - * @param streamProxyItem - * @return - */ - private boolean addStreamProxy(StreamProxyItem streamProxyItem) { - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); - boolean result = false; - streamProxyItem.setStreamType("proxy"); - streamProxyItem.setStatus(true); - String now = DateUtil.getNow(); - streamProxyItem.setCreateTime(now); - try { - if (streamProxyMapper.add(streamProxyItem) > 0) { - if (!ObjectUtils.isEmpty(streamProxyItem.getGbId())) { - if (gbStreamMapper.add(streamProxyItem) < 0) { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - return false; - } - } - }else { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - return false; - } - result = true; - dataSourceTransactionManager.commit(transactionStatus); //手动提交 - }catch (Exception e) { - logger.error("向数据库添加流代理失败:", e); - dataSourceTransactionManager.rollback(transactionStatus); - } - - - return result; - } - - /** - * 更新代理流 - * @param streamProxyItem - * @return - */ - @Override - public boolean updateStreamProxy(StreamProxyItem streamProxyItem) { - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); - boolean result = false; - streamProxyItem.setStreamType("proxy"); - try { - if (streamProxyMapper.update(streamProxyItem) > 0) { - if (!ObjectUtils.isEmpty(streamProxyItem.getGbId())) { - if (gbStreamMapper.updateByAppAndStream(streamProxyItem) == 0) { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - return false; - } - } - } else { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - return false; - } - - dataSourceTransactionManager.commit(transactionStatus); //手动提交 - result = true; - }catch (Exception e) { - logger.error("未处理的异常 ", e); - dataSourceTransactionManager.rollback(transactionStatus); - } - return result; - } - - @Override - public WVPResult addStreamProxyToZlm(StreamProxyItem param) { - WVPResult result = null; - MediaServer mediaServer = null; - if (param.getMediaServerId() == null) { - logger.warn("添加代理时MediaServerId 为null"); - return null; - }else { - mediaServer = mediaServerService.getOne(param.getMediaServerId()); - } - if (mediaServer == null) { - return null; - } - if (mediaServerService.isStreamReady(mediaServer, param.getApp(), param.getStream())) { - mediaServerService.closeStreams(mediaServer, param.getApp(), param.getStream()); - } - String msgResult; - if ("ffmpeg".equalsIgnoreCase(param.getType())){ - if (param.getTimeoutMs() == 0) { - param.setTimeoutMs(15); - } - result = mediaServerService.addFFmpegSource(mediaServer, param.getSrcUrl().trim(), param.getDstUrl(), - param.getTimeoutMs(), param.isEnableAudio(), param.isEnableMp4(), - param.getFfmpegCmdKey()); - }else { - result = mediaServerService.addStreamProxy(mediaServer, param.getApp(), param.getStream(), param.getUrl().trim(), - param.isEnableAudio(), param.isEnableMp4(), param.getRtpType()); - } - if (result != null && result.getCode() == 0) { - String key = result.getData(); - if (key == null) { - logger.warn("[获取拉流代理的结果数据Data] 失败: {}", result ); - return result; - } - param.setStreamKey(key); - streamProxyMapper.update(param); - } - return result; - } - - @Override - public Boolean removeStreamProxyFromZlm(StreamProxyItem param) { - if (param ==null) { - return null; - } - MediaServer mediaServer = mediaServerService.getOne(param.getMediaServerId()); - if (mediaServer == null) { - return null; - } - List mediaList = mediaServerService.getMediaList(mediaServer, param.getApp(), param.getStream(), null); - if (mediaList == null || mediaList.isEmpty()) { - return true; - } - Boolean result = false; - if ("ffmpeg".equalsIgnoreCase(param.getType())){ - result = mediaServerService.delFFmpegSource(mediaServer, param.getStreamKey()); - }else { - result = mediaServerService.delStreamProxy(mediaServer, param.getStreamKey()); - } - return result; - } - - @Override - public PageInfo getAll(Integer page, Integer count) { - return videoManagerStorager.queryStreamProxyList(page, count); - } - - @Override - public void del(String app, String stream) { - StreamProxyItem streamProxyItem = videoManagerStorager.queryStreamProxy(app, stream); - if (streamProxyItem != null) { - gbStreamService.sendCatalogMsg(streamProxyItem, CatalogEvent.DEL); - - // 如果关联了国标那么移除关联 - platformGbStreamMapper.delByAppAndStream(app, stream); - gbStreamMapper.del(app, stream); - videoManagerStorager.deleteStreamProxy(app, stream); - redisCatchStorage.removeStream(streamProxyItem.getMediaServerId(), "PULL", app, stream); - redisCatchStorage.removeStream(streamProxyItem.getMediaServerId(), "PUSH", app, stream); - Boolean result = removeStreamProxyFromZlm(streamProxyItem); - if (result != null && result) { - logger.info("[移除代理]: 代理: {}/{}, 从zlm移除成功", app, stream); - }else { - logger.info("[移除代理]: 代理: {}/{}, 从zlm移除失败", app, stream); - } - } - } - - @Override - public boolean start(String app, String stream) { - boolean result = false; - StreamProxyItem streamProxy = videoManagerStorager.queryStreamProxy(app, stream); - if (streamProxy != null && !streamProxy.isEnable() ) { - WVPResult wvpResult = addStreamProxyToZlm(streamProxy); - if (wvpResult == null) { - return false; - } - if (wvpResult.getCode() == 0) { - result = true; - streamProxy.setEnable(true); - updateStreamProxy(streamProxy); - }else { - logger.info("启用代理失败: {}/{}->{}({})", app, stream, wvpResult.getMsg(), - streamProxy.getSrcUrl() == null? streamProxy.getUrl():streamProxy.getSrcUrl()); - } - } else if (streamProxy != null && streamProxy.isEnable()) { - return true ; - } - return result; - } - - @Override - public boolean stop(String app, String stream) { - boolean result = false; - StreamProxyItem streamProxyDto = videoManagerStorager.queryStreamProxy(app, stream); - if (streamProxyDto != null && streamProxyDto.isEnable()) { - Boolean removed = removeStreamProxyFromZlm(streamProxyDto); - if (removed != null && removed) { - streamProxyDto.setEnable(false); - result = updateStreamProxy(streamProxyDto); - } - } - return result; - } - - @Override - public Map getFFmpegCMDs(MediaServer mediaServer) { - return mediaServerService.getFFmpegCMDs(mediaServer); - } - - - @Override - public StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId) { - return videoManagerStorager.getStreamProxyByAppAndStream(app, streamId); - } - - @Override - public void zlmServerOnline(String mediaServerId) { - // 移除开启了无人观看自动移除的流 - List streamProxyItemList = streamProxyMapper.selectAutoRemoveItemByMediaServerId(mediaServerId); - if (streamProxyItemList.size() > 0) { - gbStreamMapper.batchDel(streamProxyItemList); - } - streamProxyMapper.deleteAutoRemoveItemByMediaServerId(mediaServerId); - - // 移除拉流代理生成的流信息 - syncPullStream(mediaServerId); - - // 恢复流代理, 只查找这个这个流媒体 - List streamProxyListForEnable = storager.getStreamProxyListForEnableInMediaServer( - mediaServerId, true); - for (StreamProxyItem streamProxyDto : streamProxyListForEnable) { - logger.info("恢复流代理," + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); - WVPResult wvpResult = addStreamProxyToZlm(streamProxyDto); - if (wvpResult == null) { - // 设置为离线 - logger.info("恢复流代理失败" + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); - updateStatus(false, streamProxyDto.getApp(), streamProxyDto.getStream()); - }else { - updateStatus(true, streamProxyDto.getApp(), streamProxyDto.getStream()); - } - } - } - - @Override - public void zlmServerOffline(String mediaServerId) { - // 移除开启了无人观看自动移除的流 - List streamProxyItemList = streamProxyMapper.selectAutoRemoveItemByMediaServerId(mediaServerId); - if (streamProxyItemList.size() > 0) { - gbStreamMapper.batchDel(streamProxyItemList); - } - streamProxyMapper.deleteAutoRemoveItemByMediaServerId(mediaServerId); - // 其他的流设置离线 - streamProxyMapper.updateStatusByMediaServerId(mediaServerId, false); - String type = "PULL"; - - // 发送redis消息 - List mediaInfoList = redisCatchStorage.getStreams(mediaServerId, type); - if (mediaInfoList.size() > 0) { - for (MediaInfo mediaInfo : mediaInfoList) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("serverId", userSetting.getServerId()); - jsonObject.put("app", mediaInfo.getApp()); - jsonObject.put("stream", mediaInfo.getStream()); - jsonObject.put("register", false); - jsonObject.put("mediaServerId", mediaServerId); - redisCatchStorage.sendStreamChangeMsg(type, jsonObject); - // 移除redis内流的信息 - redisCatchStorage.removeStream(mediaServerId, type, mediaInfo.getApp(), mediaInfo.getStream()); - } - } - } - - @Override - public void clean() { - - } - - @Override - public int updateStatus(boolean status, String app, String stream) { - // 状态变化时推送到国标上级 - StreamProxyItem streamProxyItem = streamProxyMapper.selectOne(app, stream); - if (streamProxyItem == null) { - return 0; - } - int result = streamProxyMapper.updateStatus(app, stream, status); - if (!ObjectUtils.isEmpty(streamProxyItem.getGbId())) { - gbStreamService.sendCatalogMsg(streamProxyItem, status?CatalogEvent.ON:CatalogEvent.OFF); - } - return result; - } - - private void syncPullStream(String mediaServerId){ - MediaServer mediaServer = mediaServerService.getOne(mediaServerId); - if (mediaServer != null) { - List mediaInfoList = redisCatchStorage.getStreams(mediaServerId, "PULL"); - if (!mediaInfoList.isEmpty()) { - List mediaList = mediaServerService.getMediaList(mediaServer, null, null, null); - Map stringStreamInfoMap = new HashMap<>(); - if (mediaList != null && !mediaList.isEmpty()) { - for (StreamInfo streamInfo : mediaList) { - stringStreamInfoMap.put(streamInfo.getApp() + streamInfo.getStream(), streamInfo); - } - } - if (stringStreamInfoMap.isEmpty()) { - redisCatchStorage.removeStream(mediaServerId, "PULL"); - }else { - for (String key : stringStreamInfoMap.keySet()) { - StreamInfo streamInfo = stringStreamInfoMap.get(key); - if (stringStreamInfoMap.get(streamInfo.getApp() + streamInfo.getStream()) == null) { - redisCatchStorage.removeStream(mediaServerId, "PULL", streamInfo.getApp(), - streamInfo.getStream()); - } - } - } - } - } - } - - @Override - public ResourceBaseInfo getOverview() { - - int total = streamProxyMapper.getAllCount(); - int online = streamProxyMapper.getOnline(); - - return new ResourceBaseInfo(total, online); - } - - - @Scheduled(cron = "* 0/10 * * * ?") - public void asyncCheckStreamProxyStatus() { - - List all = mediaServerService.getAllOnline(); - - if (CollectionUtils.isEmpty(all)){ - return; - } - - Map serverItemMap = all.stream().collect(Collectors.toMap(MediaServer::getId, Function.identity(), (m1, m2) -> m1)); - - List list = videoManagerStorager.getStreamProxyListForEnable(true); - - if (CollectionUtils.isEmpty(list)){ - return; - } - - for (StreamProxyItem streamProxyItem : list) { - - MediaServer mediaServerItem = serverItemMap.get(streamProxyItem.getMediaServerId()); - - MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServerItem, streamProxyItem.getApp(), streamProxyItem.getStream()); - - if (mediaInfo == null){ - streamProxyItem.setStatus(false); - } else { - if (mediaInfo.getOnline() != null && mediaInfo.getOnline()) { - streamProxyItem.setStatus(true); - } else { - streamProxyItem.setStatus(false); - } - } - - updateStreamProxy(streamProxyItem); - } - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java deleted file mode 100755 index 18de78a03..000000000 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java +++ /dev/null @@ -1,649 +0,0 @@ -package com.genersoft.iot.vmp.service.impl; - -import com.alibaba.fastjson2.JSONObject; -import com.baomidou.dynamic.datasource.annotation.DS; -import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.MediaConfig; -import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; -import com.genersoft.iot.vmp.media.bean.MediaInfo; -import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent; -import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent; -import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; -import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; -import com.genersoft.iot.vmp.media.zlm.dto.hook.OriginType; -import com.genersoft.iot.vmp.service.IGbStreamService; -import com.genersoft.iot.vmp.service.IStreamPushService; -import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.dao.*; -import com.genersoft.iot.vmp.utils.DateUtil; -import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; -import com.genersoft.iot.vmp.vmanager.bean.StreamContent; -import com.github.pagehelper.PageHelper; -import com.github.pagehelper.PageInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.event.EventListener; -import org.springframework.jdbc.datasource.DataSourceTransactionManager; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Service; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.util.ObjectUtils; - -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); - - @Autowired - private GbStreamMapper gbStreamMapper; - - @Autowired - private StreamPushMapper streamPushMapper; - - @Autowired - private StreamProxyMapper streamProxyMapper; - - @Autowired - private ParentPlatformMapper parentPlatformMapper; - - @Autowired - private PlatformCatalogMapper platformCatalogMapper; - - @Autowired - private PlatformGbStreamMapper platformGbStreamMapper; - - @Autowired - private IGbStreamService gbStreamService; - - @Autowired - private EventPublisher eventPublisher; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - private UserSetting userSetting; - - @Autowired - private IMediaServerService mediaServerService; - - @Autowired - DataSourceTransactionManager dataSourceTransactionManager; - - @Autowired - TransactionDefinition transactionDefinition; - - @Autowired - private MediaConfig mediaConfig; - - /** - * 流到来的处理 - */ - @Async("taskExecutor") - @EventListener - public void onApplicationEvent(MediaArrivalEvent event) { - MediaInfo mediaInfo = event.getMediaInfo(); - if (mediaInfo == null) { - return; - } - if (mediaInfo.getOriginType() != OriginType.RTMP_PUSH.ordinal() - && mediaInfo.getOriginType() != OriginType.RTSP_PUSH.ordinal() - && mediaInfo.getOriginType() != OriginType.RTC_PUSH.ordinal()) { - return; - } - - StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(event.getApp(), event.getStream()); - if (streamAuthorityInfo == null) { - streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(event); - } else { - streamAuthorityInfo.setOriginType(mediaInfo.getOriginType()); - } - redisCatchStorage.updateStreamAuthorityInfo(event.getApp(), event.getStream(), streamAuthorityInfo); - StreamPushItem transform = StreamPushItem.getInstance(event, userSetting.getServerId()); - transform.setPushIng(true); - transform.setUpdateTime(DateUtil.getNow()); - transform.setPushTime(DateUtil.getNow()); - transform.setSelf(true); - StreamPushItem pushInDb = getPush(event.getApp(), event.getStream()); - if (pushInDb == null) { - transform.setCreateTime(DateUtil.getNow()); - streamPushMapper.add(transform); - }else { - streamPushMapper.update(transform); - gbStreamMapper.updateMediaServer(event.getApp(), event.getStream(), event.getMediaServer().getId()); - } - // 冗余数据,自己系统中自用 - if (!"broadcast".equals(event.getApp()) && !"talk".equals(event.getApp())) { - StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream( - event.getMediaServer(), event.getApp(), event.getStream(), event.getMediaInfo(), event.getCallId()); - event.getHookParam().setStreamInfo(new StreamContent(streamInfo)); - redisCatchStorage.addPushListItem(event.getApp(), event.getStream(), event); - } - - // 发送流变化redis消息 - JSONObject jsonObject = new JSONObject(); - jsonObject.put("serverId", userSetting.getServerId()); - jsonObject.put("app", event.getApp()); - jsonObject.put("stream", event.getStream()); - jsonObject.put("register", true); - jsonObject.put("mediaServerId", event.getMediaServer().getId()); - redisCatchStorage.sendStreamChangeMsg(OriginType.values()[event.getMediaInfo().getOriginType()].getType(), jsonObject); - } - - /** - * 流离开的处理 - */ - @Async("taskExecutor") - @EventListener - public void onApplicationEvent(MediaDepartureEvent event) { - // 兼容流注销时类型从redis记录获取 - MediaInfo mediaInfo = redisCatchStorage.getStreamInfo( - event.getApp(), event.getStream(), event.getMediaServer().getId()); - if (mediaInfo != null) { - String type = OriginType.values()[mediaInfo.getOriginType()].getType(); - redisCatchStorage.removeStream(event.getMediaServer().getId(), type, event.getApp(), event.getStream()); - if ("PUSH".equalsIgnoreCase(type)) { - // 冗余数据,自己系统中自用 - redisCatchStorage.removePushListItem(event.getApp(), event.getStream(), event.getMediaServer().getId()); - } - if (type != null) { - // 发送流变化redis消息 - JSONObject jsonObject = new JSONObject(); - jsonObject.put("serverId", userSetting.getServerId()); - jsonObject.put("app", event.getApp()); - jsonObject.put("stream", event.getStream()); - jsonObject.put("register", false); - jsonObject.put("mediaServerId", event.getMediaServer().getId()); - redisCatchStorage.sendStreamChangeMsg(type, jsonObject); - } - } - GbStream gbStream = gbStreamMapper.selectOne(event.getApp(), event.getStream()); - if (gbStream != null) { - if (userSetting.isUsePushingAsStatus()) { - streamPushMapper.updatePushStatus(event.getApp(), event.getStream(), false); - eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF); - } - }else { - streamPushMapper.del(event.getApp(), event.getStream()); - } - } - - - private List handleJSON(List streamInfoList) { - if (streamInfoList == null || streamInfoList.isEmpty()) { - return null; - } - Map result = new HashMap<>(); - for (StreamInfo streamInfo : streamInfoList) { - // 不保存国标推理以及拉流代理的流 - if (streamInfo.getOriginType() == OriginType.RTSP_PUSH.ordinal() - || streamInfo.getOriginType() == OriginType.RTMP_PUSH.ordinal() - || streamInfo.getOriginType() == OriginType.RTC_PUSH.ordinal() ) { - String key = streamInfo.getApp() + "_" + streamInfo.getStream(); - StreamPushItem streamPushItem = result.get(key); - if (streamPushItem == null) { - streamPushItem = streamPushItem.getInstance(streamInfo); - result.put(key, streamPushItem); - } - } - } - return new ArrayList<>(result.values()); - } - - @Override - public StreamPushItem transform(OnStreamChangedHookParam item) { - StreamPushItem streamPushItem = new StreamPushItem(); - streamPushItem.setApp(item.getApp()); - streamPushItem.setMediaServerId(item.getMediaServerId()); - streamPushItem.setStream(item.getStream()); - streamPushItem.setAliveSecond(item.getAliveSecond()); - streamPushItem.setOriginSock(item.getOriginSock()); - streamPushItem.setTotalReaderCount(item.getTotalReaderCount()); - streamPushItem.setOriginType(item.getOriginType()); - streamPushItem.setOriginTypeStr(item.getOriginTypeStr()); - streamPushItem.setOriginUrl(item.getOriginUrl()); - streamPushItem.setCreateTime(DateUtil.getNow()); - streamPushItem.setAliveSecond(item.getAliveSecond()); - streamPushItem.setStatus(true); - streamPushItem.setStreamType("push"); - streamPushItem.setVhost(item.getVhost()); - streamPushItem.setServerId(item.getSeverId()); - return streamPushItem; - } - - @Override - public PageInfo getPushList(Integer page, Integer count, String query, Boolean pushing, String mediaServerId) { - PageHelper.startPage(page, count); - List all = streamPushMapper.selectAllForList(query, pushing, mediaServerId); - return new PageInfo<>(all); - } - - @Override - public List getPushList(String mediaServerId) { - return streamPushMapper.selectAllByMediaServerIdWithOutGbID(mediaServerId); - } - - @Override - public boolean saveToGB(GbStream stream) { - stream.setStreamType("push"); - stream.setStatus(true); - stream.setCreateTime(DateUtil.getNow()); - stream.setStreamType("push"); - stream.setMediaServerId(mediaConfig.getId()); - int add = gbStreamMapper.add(stream); - return add > 0; - } - - @Override - public boolean removeFromGB(GbStream stream) { - // 判断是否需要发送事件 - gbStreamService.sendCatalogMsg(stream, CatalogEvent.DEL); - platformGbStreamMapper.delByAppAndStream(stream.getApp(), stream.getStream()); - int del = gbStreamMapper.del(stream.getApp(), stream.getStream()); - MediaServer mediaInfo = mediaServerService.getOne(stream.getMediaServerId()); - List mediaList = mediaServerService.getMediaList(mediaInfo, stream.getApp(), stream.getStream(), null); - if (mediaList != null && mediaList.isEmpty()) { - streamPushMapper.del(stream.getApp(), stream.getStream()); - } - return del > 0; - } - - - @Override - public StreamPushItem getPush(String app, String streamId) { - return streamPushMapper.selectOne(app, streamId); - } - - @Override - public boolean stop(String app, String stream) { - logger.info("[推流 ] 停止流: {}/{}", app, stream); - StreamPushItem streamPushItem = streamPushMapper.selectOne(app, stream); - if (streamPushItem != null) { - gbStreamService.sendCatalogMsg(streamPushItem, CatalogEvent.DEL); - } - - platformGbStreamMapper.delByAppAndStream(app, stream); - gbStreamMapper.del(app, stream); - int delStream = streamPushMapper.del(app, stream); - if (delStream > 0) { - MediaServer mediaServerItem = mediaServerService.getOne(streamPushItem.getMediaServerId()); - mediaServerService.closeStreams(mediaServerItem,app, stream); - } - return true; - } - - @Override - public void zlmServerOnline(String mediaServerId) { - // 同步zlm推流信息 - MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId); - if (mediaServerItem == null) { - return; - } - // 数据库记录 - List pushList = getPushList(mediaServerId); - Map pushItemMap = new HashMap<>(); - // redis记录 - List mediaInfoList = redisCatchStorage.getStreams(mediaServerId, "PUSH"); - Map streamInfoPushItemMap = new HashMap<>(); - if (pushList.size() > 0) { - for (StreamPushItem streamPushItem : pushList) { - if (ObjectUtils.isEmpty(streamPushItem.getGbId())) { - pushItemMap.put(streamPushItem.getApp() + streamPushItem.getStream(), streamPushItem); - } - } - } - if (mediaInfoList.size() > 0) { - for (MediaInfo mediaInfo : mediaInfoList) { - streamInfoPushItemMap.put(mediaInfo.getApp() + mediaInfo.getStream(), mediaInfo); - } - } - // 获取所有推流鉴权信息,清理过期的 - List allStreamAuthorityInfo = redisCatchStorage.getAllStreamAuthorityInfo(); - Map streamAuthorityInfoInfoMap = new HashMap<>(); - for (StreamAuthorityInfo streamAuthorityInfo : allStreamAuthorityInfo) { - streamAuthorityInfoInfoMap.put(streamAuthorityInfo.getApp() + streamAuthorityInfo.getStream(), streamAuthorityInfo); - } - List mediaList = mediaServerService.getMediaList(mediaServerItem, null, null, null); - if (mediaList == null) { - return; - } - List streamPushItems = handleJSON(mediaList); - if (streamPushItems != null) { - for (StreamPushItem streamPushItem : streamPushItems) { - pushItemMap.remove(streamPushItem.getApp() + streamPushItem.getStream()); - streamInfoPushItemMap.remove(streamPushItem.getApp() + streamPushItem.getStream()); - streamAuthorityInfoInfoMap.remove(streamPushItem.getApp() + streamPushItem.getStream()); - } - } - List offlinePushItems = new ArrayList<>(pushItemMap.values()); - if (offlinePushItems.size() > 0) { - String type = "PUSH"; - int runLimit = 300; - if (offlinePushItems.size() > runLimit) { - for (int i = 0; i < offlinePushItems.size(); i += runLimit) { - int toIndex = i + runLimit; - if (i + runLimit > offlinePushItems.size()) { - toIndex = offlinePushItems.size(); - } - List streamPushItemsSub = offlinePushItems.subList(i, toIndex); - streamPushMapper.delAll(streamPushItemsSub); - } - }else { - streamPushMapper.delAll(offlinePushItems); - } - - } - Collection mediaInfos = streamInfoPushItemMap.values(); - if (mediaInfos.size() > 0) { - String type = "PUSH"; - for (MediaInfo mediaInfo : mediaInfos) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("serverId", userSetting.getServerId()); - jsonObject.put("app", mediaInfo.getApp()); - jsonObject.put("stream", mediaInfo.getStream()); - jsonObject.put("register", false); - jsonObject.put("mediaServerId", mediaServerId); - redisCatchStorage.sendStreamChangeMsg(type, jsonObject); - // 移除redis内流的信息 - redisCatchStorage.removeStream(mediaServerItem.getId(), "PUSH", mediaInfo.getApp(), mediaInfo.getStream()); - // 冗余数据,自己系统中自用 - redisCatchStorage.removePushListItem(mediaInfo.getApp(), mediaInfo.getStream(), mediaServerItem.getId()); - } - } - - Collection streamAuthorityInfos = streamAuthorityInfoInfoMap.values(); - if (streamAuthorityInfos.size() > 0) { - for (StreamAuthorityInfo streamAuthorityInfo : streamAuthorityInfos) { - // 移除redis内流的信息 - redisCatchStorage.removeStreamAuthorityInfo(streamAuthorityInfo.getApp(), streamAuthorityInfo.getStream()); - } - } - } - - @Override - public void zlmServerOffline(String mediaServerId) { - List streamPushItems = streamPushMapper.selectAllByMediaServerIdWithOutGbID(mediaServerId); - // 移除没有GBId的推流 - streamPushMapper.deleteWithoutGBId(mediaServerId); - gbStreamMapper.deleteWithoutGBId("push", mediaServerId); - // 其他的流设置未启用 - streamPushMapper.updateStatusByMediaServerId(mediaServerId, false); - streamProxyMapper.updateStatusByMediaServerId(mediaServerId, false); - // 发送流停止消息 - String type = "PUSH"; - // 发送redis消息 - List mediaInfoList = redisCatchStorage.getStreams(mediaServerId, type); - if (mediaInfoList.size() > 0) { - for (MediaInfo mediaInfo : mediaInfoList) { - // 移除redis内流的信息 - redisCatchStorage.removeStream(mediaServerId, type, mediaInfo.getApp(), mediaInfo.getStream()); - JSONObject jsonObject = new JSONObject(); - jsonObject.put("serverId", userSetting.getServerId()); - jsonObject.put("app", mediaInfo.getApp()); - jsonObject.put("stream", mediaInfo.getStream()); - jsonObject.put("register", false); - jsonObject.put("mediaServerId", mediaServerId); - redisCatchStorage.sendStreamChangeMsg(type, jsonObject); - - // 冗余数据,自己系统中自用 - redisCatchStorage.removePushListItem(mediaInfo.getApp(), mediaInfo.getStream(), mediaServerId); - } - } - } - - @Override - public void clean() { - - } - - @Override - public boolean saveToRandomGB() { - List streamPushItems = streamPushMapper.selectAll(); - long gbId = 100001; - for (StreamPushItem streamPushItem : streamPushItems) { - streamPushItem.setStreamType("push"); - streamPushItem.setStatus(true); - streamPushItem.setGbId("34020000004111" + gbId); - streamPushItem.setCreateTime(DateUtil.getNow()); - gbId ++; - } - int limitCount = 30; - - if (streamPushItems.size() > limitCount) { - for (int i = 0; i < streamPushItems.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > streamPushItems.size()) { - toIndex = streamPushItems.size(); - } - gbStreamMapper.batchAdd(streamPushItems.subList(i, toIndex)); - } - }else { - gbStreamMapper.batchAdd(streamPushItems); - } - return true; - } - - @Override - public void batchAdd(List streamPushItems) { - streamPushMapper.addAll(streamPushItems); - gbStreamMapper.batchAdd(streamPushItems); - } - - - @Override - public void batchAddForUpload(List streamPushItems, Map> streamPushItemsForAll ) { - // 存储数据到stream_push表 - streamPushMapper.addAll(streamPushItems); - List streamPushItemForGbStream = streamPushItems.stream() - .filter(streamPushItem-> streamPushItem.getGbId() != null) - .collect(Collectors.toList()); - // 存储数据到gb_stream表, id会返回到streamPushItemForGbStream里 - if (streamPushItemForGbStream.size() > 0) { - gbStreamMapper.batchAdd(streamPushItemForGbStream); - } - // 去除没有ID也就是没有存储到数据库的数据 - List streamPushItemsForPlatform = streamPushItemForGbStream.stream() - .filter(streamPushItem-> streamPushItem.getGbStreamId() != null) - .collect(Collectors.toList()); - - if (streamPushItemsForPlatform.size() > 0) { - // 获取所有平台,平台和目录信息一般不会特别大量。 - List parentPlatformList = parentPlatformMapper.getParentPlatformList(); - Map> platformInfoMap = new HashMap<>(); - if (parentPlatformList.size() == 0) { - return; - } - for (ParentPlatform platform : parentPlatformList) { - Map catalogMap = new HashMap<>(); - - // 创建根节点 - PlatformCatalog platformCatalog = new PlatformCatalog(); - platformCatalog.setId(platform.getServerGBId()); - catalogMap.put(platform.getServerGBId(), platformCatalog); - - // 查询所有节点信息 - List platformCatalogs = platformCatalogMapper.selectByPlatForm(platform.getServerGBId()); - if (platformCatalogs.size() > 0) { - for (PlatformCatalog catalog : platformCatalogs) { - catalogMap.put(catalog.getId(), catalog); - } - } - platformInfoMap.put(platform.getServerGBId(), catalogMap); - } - List streamPushItemListFroPlatform = new ArrayList<>(); - Map> platformForEvent = new HashMap<>(); - // 遍历存储结果,查找app+Stream->platformId+catalogId的对应关系,然后执行批量写入 - for (StreamPushItem streamPushItem : streamPushItemsForPlatform) { - List platFormInfoList = streamPushItemsForAll.get(streamPushItem.getApp() + streamPushItem.getStream()); - if (platFormInfoList != null && platFormInfoList.size() > 0) { - for (String[] platFormInfoArray : platFormInfoList) { - StreamPushItem streamPushItemForPlatform = new StreamPushItem(); - streamPushItemForPlatform.setGbStreamId(streamPushItem.getGbStreamId()); - if (platFormInfoArray.length > 0) { - // 数组 platFormInfoArray 0 为平台ID。 1为目录ID - // 不存在这个平台,则忽略导入此关联关系 - if (platformInfoMap.get(platFormInfoArray[0]) == null - || platformInfoMap.get(platFormInfoArray[0]).get(platFormInfoArray[1]) == null) { - logger.info("导入数据时不存在平台或目录{}/{},已导入未分配", platFormInfoArray[0], platFormInfoArray[1] ); - continue; - } - streamPushItemForPlatform.setPlatformId(platFormInfoArray[0]); - List gbStreamList = platformForEvent.get(platFormInfoArray[0]); - if (gbStreamList == null) { - gbStreamList = new ArrayList<>(); - platformForEvent.put(platFormInfoArray[0], gbStreamList); - } - // 为发送通知整理数据 - streamPushItemForPlatform.setName(streamPushItem.getName()); - streamPushItemForPlatform.setApp(streamPushItem.getApp()); - streamPushItemForPlatform.setStream(streamPushItem.getStream()); - streamPushItemForPlatform.setGbId(streamPushItem.getGbId()); - gbStreamList.add(streamPushItemForPlatform); - } - if (platFormInfoArray.length > 1) { - streamPushItemForPlatform.setCatalogId(platFormInfoArray[1]); - } - streamPushItemListFroPlatform.add(streamPushItemForPlatform); - } - - } - } - if (!streamPushItemListFroPlatform.isEmpty()) { - platformGbStreamMapper.batchAdd(streamPushItemListFroPlatform); - // 发送通知 - for (String platformId : platformForEvent.keySet()) { - eventPublisher.catalogEventPublishForStream( - platformId, platformForEvent.get(platformId), CatalogEvent.ADD); - } - } - } - } - - @Override - public boolean batchStop(List gbStreams) { - if (gbStreams == null || gbStreams.size() == 0) { - return false; - } - gbStreamService.sendCatalogMsgs(gbStreams, CatalogEvent.DEL); - - platformGbStreamMapper.delByGbStreams(gbStreams); - gbStreamMapper.batchDelForGbStream(gbStreams); - int delStream = streamPushMapper.delAllForGbStream(gbStreams); - if (delStream > 0) { - for (GbStream gbStream : gbStreams) { - MediaServer mediaServerItem = mediaServerService.getOne(gbStream.getMediaServerId()); - mediaServerService.closeStreams(mediaServerItem, gbStream.getApp(), gbStream.getStream()); - } - - } - return true; - } - - @Override - public void allStreamOffline() { - List onlinePushers = streamPushMapper.getOnlinePusherForGb(); - if (onlinePushers.size() == 0) { - return; - } - streamPushMapper.setAllStreamOffline(); - - // 发送通知 - eventPublisher.catalogEventPublishForStream(null, onlinePushers, CatalogEvent.OFF); - } - - @Override - public void offline(List offlineStreams) { - // 更新部分设备离线 - List onlinePushers = streamPushMapper.getOnlinePusherForGbInList(offlineStreams); - streamPushMapper.offline(offlineStreams); - // 发送通知 - eventPublisher.catalogEventPublishForStream(null, onlinePushers, CatalogEvent.OFF); - } - - @Override - public void online(List onlineStreams) { - // 更新部分设备上线streamPushService - List onlinePushers = streamPushMapper.getOfflinePusherForGbInList(onlineStreams); - streamPushMapper.online(onlineStreams); - // 发送通知 - eventPublisher.catalogEventPublishForStream(null, onlinePushers, CatalogEvent.ON); - } - - @Override - public boolean add(StreamPushItem stream) { - stream.setUpdateTime(DateUtil.getNow()); - stream.setCreateTime(DateUtil.getNow()); - stream.setServerId(userSetting.getServerId()); - stream.setMediaServerId(mediaConfig.getId()); - stream.setSelf(true); - stream.setPushIng(true); - - // 放在事务内执行 - boolean result = false; - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); - try { - int addStreamResult = streamPushMapper.add(stream); - if (!ObjectUtils.isEmpty(stream.getGbId())) { - stream.setStreamType("push"); - gbStreamMapper.add(stream); - } - dataSourceTransactionManager.commit(transactionStatus); - result = true; - }catch (Exception e) { - logger.error("批量移除流与平台的关系时错误", e); - dataSourceTransactionManager.rollback(transactionStatus); - } - return result; - } - - @Override - public List getAllAppAndStream() { - - return streamPushMapper.getAllAppAndStream(); - } - - @Override - public ResourceBaseInfo getOverview() { - int total = streamPushMapper.getAllCount(); - int online = streamPushMapper.getAllOnline(userSetting.isUsePushingAsStatus()); - - return new ResourceBaseInfo(total, online); - } - - @Override - public Map getAllAppAndStreamMap() { - return streamPushMapper.getAllAppAndStreamMap(); - } - - @Override - public void updatePush(OnStreamChangedHookParam param) { - StreamPushItem transform = transform(param); - StreamPushItem pushInDb = getPush(param.getApp(), param.getStream()); - transform.setPushIng(param.isRegist()); - transform.setUpdateTime(DateUtil.getNow()); - transform.setPushTime(DateUtil.getNow()); - transform.setSelf(userSetting.getServerId().equals(param.getSeverId())); - if (pushInDb == null) { - transform.setCreateTime(DateUtil.getNow()); - streamPushMapper.add(transform); - }else { - streamPushMapper.update(transform); - gbStreamMapper.updateMediaServer(param.getApp(), param.getStream(), param.getMediaServerId()); - } - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushUploadFileHandler.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushUploadFileHandler.java deleted file mode 100755 index 2c3a3fa43..000000000 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushUploadFileHandler.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.genersoft.iot.vmp.service.impl; - -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.event.AnalysisEventListener; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; -import com.genersoft.iot.vmp.service.IStreamPushService; -import com.genersoft.iot.vmp.utils.DateUtil; -import com.genersoft.iot.vmp.vmanager.bean.StreamPushExcelDto; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import org.springframework.util.ObjectUtils; - -import java.util.*; - -public class StreamPushUploadFileHandler extends AnalysisEventListener { - - /** - * 错误数据的回调,用于将错误数据发送给页面 - */ - private ErrorDataHandler errorDataHandler; - - /** - * 推流的业务类用于存储数据 - */ - private IStreamPushService pushService; - - /** - * 默认流媒体节点ID - */ - private String defaultMediaServerId; - - /** - * 用于存储不加过滤的所有数据 - */ - private final List streamPushItems = new ArrayList<>(); - - /** - * 用于存储更具APP+Stream过滤后的数据,可以直接存入stream_push表与gb_stream表 - */ - private final Map streamPushItemForSave = new HashMap<>(); - - /** - * 用于存储按照APP+Stream为KEY, 平台ID+目录Id 为value的数据,用于存储到gb_stream表后获取app+Stream对应的平台与目录信息,然后存入关联表 - */ - private final Map> streamPushItemsForPlatform = new HashMap<>(); - - /** - * 用于判断文件是否存在重复的app+Stream+平台ID - */ - private final Set streamPushStreamSet = new HashSet<>(); - - /** - * 用于存储APP+Stream->国标ID 的数据结构, 数据一一对应,全局判断APP+Stream->国标ID是否存在不对应 - */ - private final BiMap gBMap = HashBiMap.create(); - - /** - * 用于存储APP+Stream-> 在数据库中的数据 - */ - private final BiMap pushMapInDb = HashBiMap.create(); - - /** - * 记录错误的APP+Stream - */ - private final List errorStreamList = new ArrayList<>(); - - - /** - * 记录错误的国标ID - */ - private final List errorInfoList = new ArrayList<>(); - - /** - * 读取数量计数器 - */ - private int loadedSize = 0; - - public StreamPushUploadFileHandler(IStreamPushService pushService, String defaultMediaServerId, ErrorDataHandler errorDataHandler) { - this.pushService = pushService; - this.defaultMediaServerId = defaultMediaServerId; - this.errorDataHandler = errorDataHandler; - // 获取数据库已有的数据,已经存在的则忽略 - List allAppAndStreams = pushService.getAllAppAndStream(); - if (allAppAndStreams.size() > 0) { - for (String allAppAndStream : allAppAndStreams) { - pushMapInDb.put(allAppAndStream, allAppAndStream); - } - } - } - - public interface ErrorDataHandler{ - void handle(List streams, List gbId); - } - - @Override - public void invoke(StreamPushExcelDto streamPushExcelDto, AnalysisContext analysisContext) { - if (ObjectUtils.isEmpty(streamPushExcelDto.getApp()) - || ObjectUtils.isEmpty(streamPushExcelDto.getStream()) - || ObjectUtils.isEmpty(streamPushExcelDto.getGbId())) { - return; - } - Integer rowIndex = analysisContext.readRowHolder().getRowIndex(); - - if (gBMap.get(streamPushExcelDto.getApp() + streamPushExcelDto.getStream()) == null) { - try { - gBMap.put(streamPushExcelDto.getApp() + streamPushExcelDto.getStream(), streamPushExcelDto.getGbId()); - }catch (IllegalArgumentException e) { - errorInfoList.add("行:" + rowIndex + ", " + streamPushExcelDto.getGbId() + " 国标ID重复使用"); - return; - } - }else { - if (!gBMap.get(streamPushExcelDto.getApp() + streamPushExcelDto.getStream()).equals(streamPushExcelDto.getGbId())) { - errorInfoList.add("行:" + rowIndex + ", " + streamPushExcelDto.getGbId() + " 同样的应用名和流ID使用了不同的国标ID"); - return; - } - } - - if (streamPushStreamSet.contains(streamPushExcelDto.getApp() + streamPushExcelDto.getStream() + streamPushExcelDto.getPlatformId())) { - errorStreamList.add("行:" + rowIndex + ", " + streamPushExcelDto.getApp() + "/" + streamPushExcelDto.getStream()+ " 平台信息重复"); - return; - }else { - if (pushMapInDb.get(streamPushExcelDto.getApp()+streamPushExcelDto.getStream()) != null) { - errorStreamList.add("行:" + rowIndex + ", " + streamPushExcelDto.getApp() + "/" + streamPushExcelDto.getStream()+ " 数据已存在"); - return; - } - streamPushStreamSet.add(streamPushExcelDto.getApp()+streamPushExcelDto.getStream() + streamPushExcelDto.getPlatformId()); - } - - StreamPushItem streamPushItem = new StreamPushItem(); - streamPushItem.setApp(streamPushExcelDto.getApp()); - streamPushItem.setStream(streamPushExcelDto.getStream()); - streamPushItem.setGbId(streamPushExcelDto.getGbId()); - streamPushItem.setStatus(streamPushExcelDto.getStatus()); - streamPushItem.setStreamType("push"); - streamPushItem.setCreateTime(DateUtil.getNow()); - streamPushItem.setMediaServerId(defaultMediaServerId); - streamPushItem.setName(streamPushExcelDto.getName()); - streamPushItem.setOriginType(2); - streamPushItem.setOriginTypeStr("rtsp_push"); - streamPushItem.setTotalReaderCount(0); - streamPushItem.setPlatformId(streamPushExcelDto.getPlatformId()); - streamPushItem.setCatalogId(streamPushExcelDto.getCatalogId()); - - // 存入所有的通道信息 - streamPushItems.add(streamPushItem); - streamPushItemForSave.put(streamPushItem.getApp() + streamPushItem.getStream(), streamPushItem); - - if (!ObjectUtils.isEmpty(streamPushExcelDto.getPlatformId())) { - List platformList = streamPushItemsForPlatform.get(streamPushItem.getApp() + streamPushItem.getStream()); - if (platformList == null) { - platformList = new ArrayList<>(); - streamPushItemsForPlatform.put(streamPushItem.getApp() + streamPushItem.getStream(), platformList); - } - String platformId = streamPushExcelDto.getPlatformId(); - String catalogId = streamPushExcelDto.getCatalogId(); - if (ObjectUtils.isEmpty(streamPushExcelDto.getCatalogId())) { - catalogId = null; - } - String[] platFormInfoArray = new String[]{platformId, catalogId}; - platformList.add(platFormInfoArray); - } - - loadedSize ++; - if (loadedSize > 1000) { - saveData(); - streamPushItems.clear(); - streamPushItemForSave.clear(); - streamPushItemsForPlatform.clear(); - loadedSize = 0; - } - - } - - @Override - public void doAfterAllAnalysed(AnalysisContext analysisContext) { - // 这里也要保存数据,确保最后遗留的数据也存储到数据库 - saveData(); - streamPushItems.clear(); - streamPushItemForSave.clear(); - gBMap.clear(); - streamPushStreamSet.clear(); - streamPushItemsForPlatform.clear(); - errorDataHandler.handle(errorStreamList, errorInfoList); - } - - private void saveData(){ - if (streamPushItemForSave.size() > 0) { - // 向数据库查询是否存在重复的app - pushService.batchAddForUpload(new ArrayList<>(streamPushItemForSave.values()), streamPushItemsForPlatform); - } - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/IRedisRpcService.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/IRedisRpcService.java index b4bd72cfa..7e23e2767 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/IRedisRpcService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/IRedisRpcService.java @@ -1,22 +1,26 @@ package com.genersoft.iot.vmp.service.redisMsg; import com.genersoft.iot.vmp.common.CommonCallback; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; public interface IRedisRpcService { - SendRtpItem getSendRtpItem(String sendRtpItemKey); + SendRtpInfo getSendRtpItem(String callId); - WVPResult startSendRtp(String sendRtpItemKey, SendRtpItem sendRtpItem); + WVPResult startSendRtp(String callId, SendRtpInfo sendRtpItem); - WVPResult stopSendRtp(String sendRtpItemKey); + WVPResult stopSendRtp(String callId); - long waitePushStreamOnline(SendRtpItem sendRtpItem, CommonCallback callback); + long waitePushStreamOnline(SendRtpInfo sendRtpItem, CommonCallback callback); - void stopWaitePushStreamOnline(SendRtpItem sendRtpItem); + void stopWaitePushStreamOnline(SendRtpInfo sendRtpItem); - void rtpSendStopped(String sendRtpItemKey); + void rtpSendStopped(String callId); void removeCallback(long key); + + long onStreamOnlineEvent(String app, String stream, CommonCallback callback); + void unPushStreamOnlineEvent(String app, String stream); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java index 194e90fb9..e31c65cb5 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java @@ -2,13 +2,18 @@ package com.genersoft.iot.vmp.service.redisMsg; import com.alibaba.fastjson2.JSON; import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.bean.AlarmChannelMessage; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IPlatformService; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.service.IMobilePositionService; import com.genersoft.iot.vmp.utils.DateUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.Message; @@ -24,12 +29,14 @@ import java.text.ParseException; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; - +/** + * 监听 SUBSCRIBE alarm_receive + * 发布 PUBLISH alarm_receive '{ "gbId": "", "alarmSn": 1, "alarmType": "111", "alarmDescription": "222", }' + */ +@Slf4j @Component public class RedisAlarmMsgListener implements MessageListener { - private final static Logger logger = LoggerFactory.getLogger(RedisAlarmMsgListener.class); - @Autowired private ISIPCommander commander; @@ -37,7 +44,16 @@ public class RedisAlarmMsgListener implements MessageListener { private ISIPCommanderForPlatform commanderForPlatform; @Autowired - private IVideoManagerStorage storage; + private IDeviceService deviceService; + + @Autowired + private IDeviceChannelService channelService; + + @Autowired + private IMobilePositionService mobilePositionService; + + @Autowired + private IPlatformService platformService; private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @@ -50,8 +66,7 @@ public class RedisAlarmMsgListener implements MessageListener { @Override public void onMessage(@NotNull Message message, byte[] bytes) { - // 消息示例: PUBLISH alarm_receive '{ "gbId": "", "alarmSn": 1, "alarmType": "111", "alarmDescription": "222", }' - logger.info("收到来自REDIS的ALARM通知: {}", new String(message.getBody())); + log.info("收到来自REDIS的ALARM通知: {}", new String(message.getBody())); boolean isEmpty = taskQueue.isEmpty(); taskQueue.offer(message); if (isEmpty) { @@ -62,7 +77,7 @@ public class RedisAlarmMsgListener implements MessageListener { try { AlarmChannelMessage alarmChannelMessage = JSON.parseObject(msg.getBody(), AlarmChannelMessage.class); if (alarmChannelMessage == null) { - logger.warn("[REDIS的ALARM通知]消息解析失败"); + log.warn("[REDIS的ALARM通知]消息解析失败"); continue; } String gbId = alarmChannelMessage.getGbId(); @@ -81,67 +96,67 @@ public class RedisAlarmMsgListener implements MessageListener { if (ObjectUtils.isEmpty(gbId)) { if (userSetting.getSendToPlatformsWhenIdLost()) { // 发送给所有的上级 - List parentPlatforms = storage.queryEnableParentPlatformList(true); - if (parentPlatforms.size() > 0) { - for (ParentPlatform parentPlatform : parentPlatforms) { + List parentPlatforms = platformService.queryEnablePlatformList(); + if (!parentPlatforms.isEmpty()) { + for (Platform parentPlatform : parentPlatforms) { try { deviceAlarm.setChannelId(parentPlatform.getDeviceGBId()); commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage()); } } } }else { // 获取开启了消息推送的设备和平台 - List parentPlatforms = storage.queryEnablePlatformListWithAsMessageChannel(); + List parentPlatforms = mobilePositionService.queryEnablePlatformListWithAsMessageChannel(); if (parentPlatforms.size() > 0) { - for (ParentPlatform parentPlatform : parentPlatforms) { + for (Platform parentPlatform : parentPlatforms) { try { deviceAlarm.setChannelId(parentPlatform.getDeviceGBId()); commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage()); + log.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage()); } } } } // 获取开启了消息推送的设备和平台 - List devices = storage.queryDeviceWithAsMessageChannel(); + List devices = channelService.queryDeviceWithAsMessageChannel(); if (devices.size() > 0) { for (Device device : devices) { try { deviceAlarm.setChannelId(device.getDeviceId()); commander.sendAlarmMessage(device, deviceAlarm); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 发送报警: {}", e.getMessage()); + log.error("[命令发送失败] 发送报警: {}", e.getMessage()); } } } }else { - Device device = storage.queryVideoDevice(gbId); - ParentPlatform platform = storage.queryParentPlatByServerGBId(gbId); + Device device = deviceService.getDeviceByDeviceId(gbId); + Platform platform = platformService.queryPlatformByServerGBId(gbId); if (device != null && platform == null) { try { commander.sendAlarmMessage(device, deviceAlarm); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 发送报警: {}", e.getMessage()); + log.error("[命令发送失败] 发送报警: {}", e.getMessage()); } }else if (device == null && platform != null){ try { commanderForPlatform.sendAlarmMessage(platform, deviceAlarm); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 发送报警: {}", e.getMessage()); + log.error("[命令发送失败] 发送报警: {}", e.getMessage()); } }else { - logger.warn("无法确定" + gbId + "是平台还是设备"); + log.warn("无法确定" + gbId + "是平台还是设备"); } } }catch (Exception e) { - logger.error("未处理的异常 ", e); - logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage()); + log.error("未处理的异常 ", e); + log.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage()); } } }); diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisCloseStreamMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisCloseStreamMsgListener.java index d01047566..e0224839f 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisCloseStreamMsgListener.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisCloseStreamMsgListener.java @@ -2,10 +2,9 @@ package com.genersoft.iot.vmp.service.redisMsg; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; -import com.genersoft.iot.vmp.service.IStreamPushService; +import com.genersoft.iot.vmp.streamPush.service.IStreamPushService; +import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.Message; @@ -17,14 +16,13 @@ import java.util.concurrent.ConcurrentLinkedQueue; /** * 接收来自redis的关闭流更新通知 + * 消息举例: PUBLISH VM_MSG_STREAM_PUSH_CLOSE "{'app': 'live', 'stream': 'stream'}" * @author lin */ +@Slf4j @Component public class RedisCloseStreamMsgListener implements MessageListener { - private final static Logger logger = LoggerFactory.getLogger(RedisCloseStreamMsgListener.class); - - @Autowired private IStreamPushService pushService; @@ -46,11 +44,10 @@ public class RedisCloseStreamMsgListener implements MessageListener { JSONObject jsonObject = JSON.parseObject(msg.getBody()); String app = jsonObject.getString("app"); String stream = jsonObject.getString("stream"); - pushService.stop(app, stream); - + pushService.stopByAppAndStream(app, stream); }catch (Exception e) { - logger.warn("[REDIS的关闭推流通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); - logger.error("[REDIS的关闭推流通知] 异常内容: ", e); + log.warn("[REDIS的关闭推流通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + log.error("[REDIS的关闭推流通知] 异常内容: ", e); } } }); diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java index cea39b4c0..32dddddfc 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java @@ -1,12 +1,11 @@ package com.genersoft.iot.vmp.service.redisMsg; import com.alibaba.fastjson2.JSON; +import com.genersoft.iot.vmp.service.IMobilePositionService; import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.Message; @@ -21,17 +20,18 @@ import java.util.concurrent.ConcurrentLinkedQueue; /** * 接收来自redis的GPS更新通知 * @author lin + * 监听: SUBSCRIBE VM_MSG_GPS + * 发布 PUBLISH VM_MSG_GPS '{"messageId":"1727228507555","id":"24212345671381000047","lng":116.30307666666667,"lat":40.03295833333333,"time":"2024-09-25T09:41:47","direction":"56.0","speed":0.0,"altitude":60.0,"unitNo":"100000000","memberNo":"10000047"}' */ +@Slf4j @Component public class RedisGpsMsgListener implements MessageListener { - private final static Logger logger = LoggerFactory.getLogger(RedisGpsMsgListener.class); - @Autowired private IRedisCatchStorage redisCatchStorage; @Autowired - private IVideoManagerStorage storager; + private IMobilePositionService mobilePositionService; private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @@ -50,12 +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)); + log.info("[REDIS的位置变化通知], {}", JSON.toJSONString(gpsMsgInfo)); // 只是放入redis缓存起来 redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo); }catch (Exception e) { - logger.warn("[REDIS的位置变化通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); - logger.error("[REDIS的位置变化通知] 异常内容: ", e); + log.warn("[REDIS的位置变化通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + log.error("[REDIS的位置变化通知] 异常内容: ", e); } } }); @@ -67,10 +67,10 @@ public class RedisGpsMsgListener implements MessageListener { */ @Scheduled(fixedRate = 2 * 1000) //每2秒执行一次 public void execute(){ - List gpsMsgInfo = redisCatchStorage.getAllGpsMsgInfo(); - if (gpsMsgInfo.size() > 0) { - storager.updateStreamGPS(gpsMsgInfo); - for (GPSMsgInfo msgInfo : gpsMsgInfo) { + List gpsMsgInfoList = redisCatchStorage.getAllGpsMsgInfo(); + if (!gpsMsgInfoList.isEmpty()) { + mobilePositionService.updateStreamGPS(gpsMsgInfoList); + for (GPSMsgInfo msgInfo : gpsMsgInfoList) { msgInfo.setStored(true); redisCatchStorage.updateGpsMsgInfo(msgInfo); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamListMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamListMsgListener.java new file mode 100755 index 000000000..1c2845a49 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamListMsgListener.java @@ -0,0 +1,123 @@ +package com.genersoft.iot.vmp.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.media.service.IMediaServerService; +import com.genersoft.iot.vmp.streamPush.bean.RedisPushStreamMessage; +import com.genersoft.iot.vmp.streamPush.bean.StreamPush; +import com.genersoft.iot.vmp.streamPush.service.IStreamPushService; +import com.genersoft.iot.vmp.utils.DateUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * @Auther: JiangFeng + * @Date: 2022/8/16 11:32 + * @Description: 接收redis发送的推流设备列表更新通知 + * 监听: SUBSCRIBE VM_MSG_PUSH_STREAM_LIST_CHANGE + * 发布 PUBLISH VM_MSG_PUSH_STREAM_LIST_CHANGE '[{"app":1000,"stream":10000000,"gbId":"12345678901234567890","name":"A6","status":false},{"app":1000,"stream":10000021,"gbId":"24212345671381000021","name":"终端9273","status":false},{"app":1000,"stream":10000022,"gbId":"24212345671381000022","name":"终端9434","status":true},{"app":1000,"stream":10000025,"gbId":"24212345671381000025","name":"华为M10","status":false},{"app":1000,"stream":10000051,"gbId":"11111111111381111122","name":"终端9720","status":false}]' + */ +@Slf4j +@Component +public class RedisPushStreamListMsgListener implements MessageListener { + + @Resource + private IMediaServerService mediaServerService; + + @Resource + private IStreamPushService streamPushService; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Override + public void onMessage(Message message, byte[] bytes) { + log.info("[REDIS消息-推流设备列表更新]: {}", new String(message.getBody())); + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(message); + if (isEmpty) { + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + try { + List streamPushItems = JSON.parseArray(new String(msg.getBody()), RedisPushStreamMessage.class); + //查询全部的app+stream 用于判断是添加还是修改 + Map allAppAndStream = streamPushService.getAllAppAndStreamMap(); + Map allGBId = streamPushService.getAllGBId(); + + /** + * 用于存储更具APP+Stream过滤后的数据,可以直接存入stream_push表与gb_stream表 + */ + List streamPushItemForSave = new ArrayList<>(); + List streamPushItemForUpdate = new ArrayList<>(); + for (RedisPushStreamMessage pushStreamMessage : streamPushItems) { + String app = pushStreamMessage.getApp(); + String stream = pushStreamMessage.getStream(); + boolean contains = allAppAndStream.containsKey(app + stream); + //不存在就添加 + if (!contains) { + if (allGBId.containsKey(pushStreamMessage.getGbId())) { + StreamPush streamPushInDb = allGBId.get(pushStreamMessage.getGbId()); + log.warn("[REDIS消息-推流设备列表更新-INSERT] 国标编号重复: {}, 已分配给{}/{}", + streamPushInDb.getGbDeviceId(), streamPushInDb.getApp(), streamPushInDb.getStream()); + continue; + } + StreamPush streamPush = pushStreamMessage.buildstreamPush(); + streamPush.setCreateTime(DateUtil.getNow()); + streamPush.setUpdateTime(DateUtil.getNow()); + streamPush.setMediaServerId(mediaServerService.getDefaultMediaServer().getId()); + streamPushItemForSave.add(streamPush); + allGBId.put(streamPush.getGbDeviceId(), streamPush); + } else { + StreamPush streamPushForGbDeviceId = allGBId.get(pushStreamMessage.getGbId()); + if (streamPushForGbDeviceId != null + && (!streamPushForGbDeviceId.getApp().equals(pushStreamMessage.getApp()) + || !streamPushForGbDeviceId.getStream().equals(pushStreamMessage.getStream()))) { + StreamPush streamPushInDb = allGBId.get(pushStreamMessage.getGbId()); + log.warn("[REDIS消息-推流设备列表更新-UPDATE] 国标编号重复: {}, 已分配给{}/{}", + pushStreamMessage.getGbId(), streamPushInDb.getApp(), streamPushInDb.getStream()); + continue; + } + StreamPush streamPush = allAppAndStream.get(app + stream); + streamPush.setUpdateTime(DateUtil.getNow()); + streamPush.setGbDeviceId(pushStreamMessage.getGbId()); + streamPush.setGbName(pushStreamMessage.getName()); + streamPush.setGbStatus(pushStreamMessage.isStatus()?"ON":"OFF"); + //存在就只修改 name和gbId + streamPushItemForUpdate.add(streamPush); + } + } + if (!streamPushItemForSave.isEmpty()) { + log.info("添加{}条",streamPushItemForSave.size()); + log.info(JSONObject.toJSONString(streamPushItemForSave)); + streamPushService.batchAdd(streamPushItemForSave); + + } + if(!streamPushItemForUpdate.isEmpty()){ + log.info("修改{}条",streamPushItemForUpdate.size()); + log.info(JSONObject.toJSONString(streamPushItemForUpdate)); + streamPushService.batchUpdate(streamPushItemForUpdate); + } + }catch (Exception e) { + log.warn("[REDIS消息-推流设备列表更新] 发现未处理的异常, \r\n{}", new String(message.getBody())); + log.error("[REDIS消息-推流设备列表更新] 异常内容: ", e); + } + } + }); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java index c90771be8..47c93ecfe 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java @@ -2,8 +2,7 @@ package com.genersoft.iot.vmp.service.redisMsg; import com.alibaba.fastjson2.JSON; import com.genersoft.iot.vmp.service.bean.MessageForPushChannelResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.Message; @@ -19,19 +18,18 @@ import java.util.concurrent.ConcurrentLinkedQueue; /** * 接收redis返回的推流结果 * @author lin + * PUBLISH VM_MSG_STREAM_PUSH_RESPONSE '{"code":0,"msg":"失败","app":"1000","stream":"10000022"}' */ +@Slf4j @Component public class RedisPushStreamResponseListener implements MessageListener { - private final static Logger logger = LoggerFactory.getLogger(RedisPushStreamResponseListener.class); - private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @Qualifier("taskExecutor") @Autowired private ThreadPoolTaskExecutor taskExecutor; - private Map responseEvents = new ConcurrentHashMap<>(); public interface PushStreamResponseEvent{ @@ -40,7 +38,7 @@ public class RedisPushStreamResponseListener implements MessageListener { @Override public void onMessage(Message message, byte[] bytes) { - logger.info("[REDIS消息-请求推流结果]: {}", new String(message.getBody())); + log.info("[REDIS消息-请求推流结果]: {}", new String(message.getBody())); boolean isEmpty = taskQueue.isEmpty(); taskQueue.offer(message); if (isEmpty) { @@ -50,7 +48,7 @@ public class RedisPushStreamResponseListener implements MessageListener { try { MessageForPushChannelResponse response = JSON.parseObject(new String(msg.getBody()), MessageForPushChannelResponse.class); if (response == null || ObjectUtils.isEmpty(response.getApp()) || ObjectUtils.isEmpty(response.getStream())){ - logger.info("[REDIS消息-请求推流结果]:参数不全"); + log.info("[REDIS消息-请求推流结果]:参数不全"); continue; } // 查看正在等待的invite消息 @@ -58,8 +56,8 @@ public class RedisPushStreamResponseListener implements MessageListener { responseEvents.get(response.getApp() + response.getStream()).run(response); } }catch (Exception e) { - logger.warn("[REDIS消息-请求推流结果] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); - logger.error("[REDIS消息-请求推流结果] 异常内容: ", e); + log.warn("[REDIS消息-请求推流结果] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + log.error("[REDIS消息-请求推流结果] 异常内容: ", e); } } }); diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusListMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusListMsgListener.java deleted file mode 100755 index e65ea8d0d..000000000 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusListMsgListener.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.genersoft.iot.vmp.service.redisMsg; - -import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.JSONObject; -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; -import com.genersoft.iot.vmp.service.IGbStreamService; -import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.service.IStreamPushService; -import com.genersoft.iot.vmp.utils.DateUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.data.redis.connection.Message; -import org.springframework.data.redis.connection.MessageListener; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentLinkedQueue; - -/** - * @Auther: JiangFeng - * @Date: 2022/8/16 11:32 - * @Description: 接收redis发送的推流设备列表更新通知 - */ -@Component -public class RedisPushStreamStatusListMsgListener implements MessageListener { - - private final static Logger logger = LoggerFactory.getLogger(RedisPushStreamStatusListMsgListener.class); - @Resource - private IMediaServerService mediaServerService; - - @Resource - private IStreamPushService streamPushService; - @Resource - private IGbStreamService gbStreamService; - - - private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); - - @Qualifier("taskExecutor") - @Autowired - private ThreadPoolTaskExecutor taskExecutor; - - @Override - public void onMessage(Message message, byte[] bytes) { - logger.info("[REDIS消息-推流设备列表更新]: {}", new String(message.getBody())); - boolean isEmpty = taskQueue.isEmpty(); - taskQueue.offer(message); - if (isEmpty) { - taskExecutor.execute(() -> { - while (!taskQueue.isEmpty()) { - Message msg = taskQueue.poll(); - try { - List streamPushItems = JSON.parseArray(new String(msg.getBody()), StreamPushItem.class); - //查询全部的app+stream 用于判断是添加还是修改 - Map allAppAndStream = streamPushService.getAllAppAndStreamMap(); - Map allGBId = gbStreamService.getAllGBId(); - - /** - * 用于存储更具APP+Stream过滤后的数据,可以直接存入stream_push表与gb_stream表 - */ - List streamPushItemForSave = new ArrayList<>(); - List streamPushItemForUpdate = new ArrayList<>(); - for (StreamPushItem streamPushItem : streamPushItems) { - String app = streamPushItem.getApp(); - String stream = streamPushItem.getStream(); - boolean contains = allAppAndStream.containsKey(app + stream); - //不存在就添加 - if (!contains) { - if (allGBId.containsKey(streamPushItem.getGbId())) { - GbStream gbStream = allGBId.get(streamPushItem.getGbId()); - logger.warn("[REDIS消息-推流设备列表更新-INSERT] 国标编号重复: {}, 已分配给{}/{}", - streamPushItem.getGbId(), gbStream.getApp(), gbStream.getStream()); - continue; - } - streamPushItem.setStreamType("push"); - streamPushItem.setCreateTime(DateUtil.getNow()); - streamPushItem.setMediaServerId(mediaServerService.getDefaultMediaServer().getId()); - streamPushItem.setOriginType(2); - streamPushItem.setOriginTypeStr("rtsp_push"); - streamPushItem.setTotalReaderCount(0); - streamPushItemForSave.add(streamPushItem); - allGBId.put(streamPushItem.getGbId(), streamPushItem); - } else { - if (allGBId.containsKey(streamPushItem.getGbId()) - && (!allGBId.get(streamPushItem.getGbId()).getApp().equals(streamPushItem.getApp()) || !allGBId.get(streamPushItem.getGbId()).getStream().equals(streamPushItem.getStream()))) { - GbStream gbStream = allGBId.get(streamPushItem.getGbId()); - logger.warn("[REDIS消息-推流设备列表更新-UPDATE] 国标编号重复: {}, 已分配给{}/{}", - streamPushItem.getGbId(), gbStream.getApp(), gbStream.getStream()); - continue; - } - //存在就只修改 name和gbId - streamPushItemForUpdate.add(streamPushItem); - } - } - if (!streamPushItemForSave.isEmpty()) { - logger.info("添加{}条",streamPushItemForSave.size()); - logger.info(JSONObject.toJSONString(streamPushItemForSave)); - streamPushService.batchAdd(streamPushItemForSave); - - } - if(!streamPushItemForUpdate.isEmpty()){ - logger.info("修改{}条",streamPushItemForUpdate.size()); - logger.info(JSONObject.toJSONString(streamPushItemForUpdate)); - gbStreamService.updateGbIdOrName(streamPushItemForUpdate); - } - }catch (Exception e) { - logger.warn("[REDIS消息-推流设备列表更新] 发现未处理的异常, \r\n{}", new String(message.getBody())); - logger.error("[REDIS消息-推流设备列表更新] 异常内容: ", e); - } - } - }); - } - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java index c8f4b2ae8..428f743f7 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java @@ -4,11 +4,10 @@ import com.alibaba.fastjson2.JSON; import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.service.IStreamPushService; import com.genersoft.iot.vmp.service.bean.PushStreamStatusChangeFromRedisDto; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.genersoft.iot.vmp.streamPush.service.IStreamPushService; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.ApplicationArguments; @@ -24,12 +23,13 @@ import java.util.concurrent.ConcurrentLinkedQueue; /** * 接收redis发送的推流设备上线下线通知 * @author lin + * 发送 PUBLISH VM_MSG_PUSH_STREAM_STATUS_CHANGE '{"setAllOffline":false,"offlineStreams":[{"app":"1000","stream":"10000022","timeStamp":1726729716551}]}' + * 订阅 SUBSCRIBE VM_MSG_PUSH_STREAM_STATUS_CHANGE */ +@Slf4j @Component public class RedisPushStreamStatusMsgListener implements MessageListener, ApplicationRunner { - private final static Logger logger = LoggerFactory.getLogger(RedisPushStreamStatusMsgListener.class); - @Autowired private IRedisCatchStorage redisCatchStorage; @@ -42,9 +42,7 @@ public class RedisPushStreamStatusMsgListener implements MessageListener, Applic @Autowired private UserSetting userSetting; - - - private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @Qualifier("taskExecutor") @Autowired @@ -53,7 +51,7 @@ public class RedisPushStreamStatusMsgListener implements MessageListener, Applic @Override public void onMessage(Message message, byte[] bytes) { boolean isEmpty = taskQueue.isEmpty(); - logger.warn("[REDIS消息-推流设备状态变化]: {}", new String(message.getBody())); + log.warn("[REDIS消息-推流设备状态变化]: {}", new String(message.getBody())); taskQueue.offer(message); if (isEmpty) { @@ -61,30 +59,30 @@ public class RedisPushStreamStatusMsgListener implements MessageListener, Applic while (!taskQueue.isEmpty()) { Message msg = taskQueue.poll(); try { - PushStreamStatusChangeFromRedisDto statusChangeFromPushStream = JSON.parseObject(msg.getBody(), PushStreamStatusChangeFromRedisDto.class); - if (statusChangeFromPushStream == null) { - logger.warn("[REDIS消息]推流设备状态变化消息解析失败"); + PushStreamStatusChangeFromRedisDto streamStatusMessage = JSON.parseObject(msg.getBody(), PushStreamStatusChangeFromRedisDto.class); + if (streamStatusMessage == null) { + log.warn("[REDIS消息]推流设备状态变化消息解析失败"); continue; } // 取消定时任务 dynamicTask.stop(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED); - if (statusChangeFromPushStream.isSetAllOffline()) { + if (streamStatusMessage.isSetAllOffline()) { // 所有设备离线 - streamPushService.allStreamOffline(); + streamPushService.allOffline(); } - if (statusChangeFromPushStream.getOfflineStreams() != null - && statusChangeFromPushStream.getOfflineStreams().size() > 0) { + if (streamStatusMessage.getOfflineStreams() != null + && !streamStatusMessage.getOfflineStreams().isEmpty()) { // 更新部分设备离线 - streamPushService.offline(statusChangeFromPushStream.getOfflineStreams()); + streamPushService.offline(streamStatusMessage.getOfflineStreams()); } - if (statusChangeFromPushStream.getOnlineStreams() != null && - statusChangeFromPushStream.getOnlineStreams().size() > 0) { + if (streamStatusMessage.getOnlineStreams() != null && + !streamStatusMessage.getOnlineStreams().isEmpty()) { // 更新部分设备上线 - streamPushService.online(statusChangeFromPushStream.getOnlineStreams()); + streamPushService.online(streamStatusMessage.getOnlineStreams()); } }catch (Exception e) { - logger.warn("[REDIS消息-推流设备状态变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); - logger.error("[REDIS消息-推流设备状态变化] 异常内容: ", e); + log.warn("[REDIS消息-推流设备状态变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + log.error("[REDIS消息-推流设备状态变化] 异常内容: ", e); } } }); @@ -97,9 +95,9 @@ public class RedisPushStreamStatusMsgListener implements MessageListener, Applic // 启动时设置所有推流通道离线,发起查询请求 redisCatchStorage.sendStreamPushRequestedMsgForStatus(); dynamicTask.startDelay(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED, ()->{ - logger.info("[REDIS消息]未收到redis回复推流设备状态,执行推流设备离线"); + log.info("[REDIS消息]未收到redis回复推流设备状态,执行推流设备离线"); // 五秒收不到请求就设置通道离线,然后通知上级离线 - streamPushService.allStreamOffline(); + streamPushService.allOffline(); }, 5000); } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcController.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcController.java index adb23186e..dde2860a5 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcController.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcController.java @@ -1,14 +1,14 @@ package com.genersoft.iot.vmp.service.redisMsg.control; import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.redis.RedisRpcConfig; import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcMessage; import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcRequest; import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcResponse; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.media.bean.MediaInfo; @@ -18,28 +18,22 @@ import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; import com.genersoft.iot.vmp.media.event.hook.HookType; import com.genersoft.iot.vmp.media.service.IMediaServerService; import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; +import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; -import javax.sip.InvalidArgumentException; -import javax.sip.SipException; -import java.text.ParseException; - /** * 其他wvp发起的rpc调用,这里的方法被 RedisRpcConfig 通过反射寻找对应的方法名称调用 */ +@Slf4j @Component public class RedisRpcController { - private final static Logger logger = LoggerFactory.getLogger(RedisRpcController.class); - @Autowired private SSRCFactory ssrcFactory; @@ -65,24 +59,23 @@ public class RedisRpcController { @Autowired private ISIPCommanderForPlatform commanderFroPlatform; - @Autowired - private IVideoManagerStorage storager; + private ISendRtpServerService sendRtpServerService; /** * 获取发流的信息 */ public RedisRpcResponse getSendRtpItem(RedisRpcRequest request) { - String sendRtpItemKey = request.getParam().toString(); - SendRtpItem sendRtpItem = (SendRtpItem) redisTemplate.opsForValue().get(sendRtpItemKey); + String callId = request.getParam().toString(); + SendRtpInfo sendRtpItem = sendRtpServerService.queryByCallId(callId); if (sendRtpItem == null) { - logger.info("[redis-rpc] 获取发流的信息, 未找到redis中的发流信息, key:{}", sendRtpItemKey); + log.info("[redis-rpc] 获取发流的信息, 未找到redis中的发流信息, callId:{}", callId); RedisRpcResponse response = request.getResponse(); response.setStatusCode(200); return response; } - logger.info("[redis-rpc] 获取发流的信息: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort()); + log.info("[redis-rpc] 获取发流的信息: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort()); // 查询本级是否有这个流 MediaServer mediaServerItem = mediaServerService.getMediaServerByAppAndStream(sendRtpItem.getApp(), sendRtpItem.getStream()); if (mediaServerItem == null) { @@ -92,7 +85,7 @@ public class RedisRpcController { // 自平台内容 int localPort = sendRtpPortManager.getNextPort(mediaServerItem); if (localPort == 0) { - logger.info("[redis-rpc] getSendRtpItem->服务器端口资源不足" ); + log.info("[redis-rpc] getSendRtpItem->服务器端口资源不足" ); RedisRpcResponse response = request.getResponse(); response.setStatusCode(200); } @@ -105,11 +98,10 @@ public class RedisRpcController { String ssrc = "Play".equalsIgnoreCase(sendRtpItem.getSessionName()) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); sendRtpItem.setSsrc(ssrc); } - redisCatchStorage.updateSendRTPSever(sendRtpItem); - redisTemplate.opsForValue().set(sendRtpItemKey, sendRtpItem); + sendRtpServerService.update(sendRtpItem); RedisRpcResponse response = request.getResponse(); response.setStatusCode(200); - response.setBody(sendRtpItemKey); + response.setBody(callId); return response; } @@ -117,12 +109,12 @@ public class RedisRpcController { * 监听流上线 */ public RedisRpcResponse waitePushStreamOnline(RedisRpcRequest request) { - SendRtpItem sendRtpItem = JSONObject.parseObject(request.getParam().toString(), SendRtpItem.class); - logger.info("[redis-rpc] 监听流上线: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort()); + SendRtpInfo sendRtpItem = JSONObject.parseObject(request.getParam().toString(), SendRtpInfo.class); + log.info("[redis-rpc] 监听流上线: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort()); // 查询本级是否有这个流 MediaServer mediaServer = mediaServerService.getMediaServerByAppAndStream(sendRtpItem.getApp(), sendRtpItem.getStream()); if (mediaServer != null) { - logger.info("[redis-rpc] 监听流上线时发现流已存在直接返回: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort() ); + log.info("[redis-rpc] 监听流上线时发现流已存在直接返回: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort() ); // 读取redis中的上级点播信息,生成sendRtpItm发送出去 if (sendRtpItem.getSsrc() == null) { // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 @@ -133,15 +125,15 @@ public class RedisRpcController { sendRtpItem.setLocalIp(mediaServer.getSdpIp()); sendRtpItem.setServerId(userSetting.getServerId()); - redisTemplate.opsForValue().set(sendRtpItem.getRedisKey(), sendRtpItem); + sendRtpServerService.update(sendRtpItem); RedisRpcResponse response = request.getResponse(); - response.setBody(sendRtpItem.getRedisKey()); + response.setBody(sendRtpItem.getChannelId()); response.setStatusCode(200); } // 监听流上线。 流上线直接发送sendRtpItem消息给实际的信令处理者 Hook hook = Hook.getInstance(HookType.on_media_arrival, sendRtpItem.getApp(), sendRtpItem.getStream(), null); hookSubscribe.addSubscribe(hook, (hookData) -> { - logger.info("[redis-rpc] 监听流上线,流已上线: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort()); + log.info("[redis-rpc] 监听流上线,流已上线: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort()); // 读取redis中的上级点播信息,生成sendRtpItm发送出去 if (sendRtpItem.getSsrc() == null) { // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 @@ -152,9 +144,9 @@ public class RedisRpcController { sendRtpItem.setLocalIp(hookData.getMediaServer().getSdpIp()); sendRtpItem.setServerId(userSetting.getServerId()); - redisTemplate.opsForValue().set(sendRtpItem.getRedisKey(), sendRtpItem); + redisTemplate.opsForValue().set(sendRtpItem.getChannelId(), sendRtpItem); RedisRpcResponse response = request.getResponse(); - response.setBody(sendRtpItem.getRedisKey()); + response.setBody(sendRtpItem.getChannelId()); response.setStatusCode(200); // 手动发送结果 sendResponse(response); @@ -164,12 +156,45 @@ public class RedisRpcController { return null; } + /** + * 监听流上线 + */ + public RedisRpcResponse onStreamOnlineEvent(RedisRpcRequest request) { + StreamInfo streamInfo = JSONObject.parseObject(request.getParam().toString(), StreamInfo.class); + log.info("[redis-rpc] 监听流信息,等待流上线: {}/{}", streamInfo.getApp(), streamInfo.getStream()); + // 查询本级是否有这个流 + StreamInfo streamInfoInServer = mediaServerService.getMediaByAppAndStream(streamInfo.getApp(), streamInfo.getStream()); + if (streamInfoInServer != null) { + log.info("[redis-rpc] 监听流上线时发现流已存在直接返回: {}/{}", streamInfo.getApp(), streamInfo.getStream()); + RedisRpcResponse response = request.getResponse(); + response.setBody(JSONObject.toJSONString(streamInfoInServer)); + response.setStatusCode(200); + return response; + } + // 监听流上线。 流上线直接发送sendRtpItem消息给实际的信令处理者 + Hook hook = Hook.getInstance(HookType.on_media_arrival, streamInfo.getApp(), streamInfo.getStream()); + hookSubscribe.addSubscribe(hook, (hookData) -> { + log.info("[redis-rpc] 监听流上线,流已上线: {}/{}", streamInfo.getApp(), streamInfo.getStream()); + // 读取redis中的上级点播信息,生成sendRtpItm发送出去 + RedisRpcResponse response = request.getResponse(); + StreamInfo streamInfoByAppAndStream = mediaServerService.getStreamInfoByAppAndStream(hookData.getMediaServer(), + streamInfo.getApp(), streamInfo.getStream(), hookData.getMediaInfo(), + hookData.getMediaInfo() != null ? hookData.getMediaInfo().getCallId() : null); + response.setBody(JSONObject.toJSONString(streamInfoByAppAndStream)); + response.setStatusCode(200); + // 手动发送结果 + sendResponse(response); + hookSubscribe.removeSubscribe(hook); + }); + return null; + } + /** * 停止监听流上线 */ public RedisRpcResponse stopWaitePushStreamOnline(RedisRpcRequest request) { - SendRtpItem sendRtpItem = JSONObject.parseObject(request.getParam().toString(), SendRtpItem.class); - logger.info("[redis-rpc] 停止监听流上线: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort() ); + SendRtpInfo sendRtpItem = JSONObject.parseObject(request.getParam().toString(), SendRtpInfo.class); + log.info("[redis-rpc] 停止监听流上线: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort() ); // 监听流上线。 流上线直接发送sendRtpItem消息给实际的信令处理者 Hook hook = Hook.getInstance(HookType.on_media_arrival, sendRtpItem.getApp(), sendRtpItem.getStream(), null); hookSubscribe.removeSubscribe(hook); @@ -178,32 +203,46 @@ public class RedisRpcController { return response; } + /** + * 停止监听流上线 + */ + public RedisRpcResponse unPushStreamOnlineEvent(RedisRpcRequest request) { + StreamInfo streamInfo = JSONObject.parseObject(request.getParam().toString(), StreamInfo.class); + log.info("[redis-rpc] 停止监听流上线: {}/{}", streamInfo.getApp(), streamInfo.getStream()); + // 监听流上线。 流上线直接发送sendRtpItem消息给实际的信令处理者 + Hook hook = Hook.getInstance(HookType.on_media_arrival, streamInfo.getApp(), streamInfo.getStream(), null); + hookSubscribe.removeSubscribe(hook); + RedisRpcResponse response = request.getResponse(); + response.setStatusCode(200); + return response; + } + /** * 开始发流 */ public RedisRpcResponse startSendRtp(RedisRpcRequest request) { - String sendRtpItemKey = request.getParam().toString(); - SendRtpItem sendRtpItem = (SendRtpItem) redisTemplate.opsForValue().get(sendRtpItemKey); + String callId = request.getParam().toString(); + SendRtpInfo sendRtpItem = sendRtpServerService.queryByCallId(callId); RedisRpcResponse response = request.getResponse(); response.setStatusCode(200); if (sendRtpItem == null) { - logger.info("[redis-rpc] 开始发流, 未找到redis中的发流信息, key:{}", sendRtpItemKey); + log.info("[redis-rpc] 开始发流, 未找到redis中的发流信息, callId:{}", callId); WVPResult wvpResult = WVPResult.fail(ErrorCode.ERROR100.getCode(), "未找到redis中的发流信息"); response.setBody(wvpResult); return response; } - logger.info("[redis-rpc] 开始发流: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort()); + log.info("[redis-rpc] 开始发流: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort()); MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); if (mediaServer == null) { - logger.info("[redis-rpc] startSendRtp->未找到MediaServer: {}", sendRtpItem.getMediaServerId() ); + log.info("[redis-rpc] startSendRtp->未找到MediaServer: {}", sendRtpItem.getMediaServerId() ); WVPResult wvpResult = WVPResult.fail(ErrorCode.ERROR100.getCode(), "未找到MediaServer"); response.setBody(wvpResult); return response; } MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServer, sendRtpItem.getApp(), sendRtpItem.getStream()); - if (mediaInfo != null) { - logger.info("[redis-rpc] startSendRtp->流不在线: {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream() ); + if (mediaInfo == null) { + log.info("[redis-rpc] startSendRtp->流不在线: {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream() ); WVPResult wvpResult = WVPResult.fail(ErrorCode.ERROR100.getCode(), "流不在线"); response.setBody(wvpResult); return response; @@ -211,12 +250,12 @@ public class RedisRpcController { try { mediaServerService.startSendRtp(mediaServer, sendRtpItem); }catch (ControllerException exception) { - logger.info("[redis-rpc] 发流失败: {}/{}, 目标地址: {}:{}, {}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort(), exception.getMsg()); + log.info("[redis-rpc] 发流失败: {}/{}, 目标地址: {}:{}, {}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort(), exception.getMsg()); WVPResult wvpResult = WVPResult.fail(exception.getCode(), exception.getMsg()); response.setBody(wvpResult); return response; } - logger.info("[redis-rpc] 发流成功: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort()); + log.info("[redis-rpc] 发流成功: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort()); WVPResult wvpResult = WVPResult.success(); response.setBody(wvpResult); return response; @@ -226,20 +265,20 @@ public class RedisRpcController { * 停止发流 */ public RedisRpcResponse stopSendRtp(RedisRpcRequest request) { - String sendRtpItemKey = request.getParam().toString(); - SendRtpItem sendRtpItem = (SendRtpItem) redisTemplate.opsForValue().get(sendRtpItemKey); + String callId = request.getParam().toString(); + SendRtpInfo sendRtpItem = sendRtpServerService.queryByCallId(callId); RedisRpcResponse response = request.getResponse(); response.setStatusCode(200); if (sendRtpItem == null) { - logger.info("[redis-rpc] 停止推流, 未找到redis中的发流信息, key:{}", sendRtpItemKey); + log.info("[redis-rpc] 停止推流, 未找到redis中的发流信息, key:{}", callId); WVPResult wvpResult = WVPResult.fail(ErrorCode.ERROR100.getCode(), "未找到redis中的发流信息"); response.setBody(wvpResult); return response; } - logger.info("[redis-rpc] 停止推流: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort() ); + log.info("[redis-rpc] 停止推流: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort() ); MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); if (mediaServer == null) { - logger.info("[redis-rpc] stopSendRtp->未找到MediaServer: {}", sendRtpItem.getMediaServerId() ); + log.info("[redis-rpc] stopSendRtp->未找到MediaServer: {}", sendRtpItem.getMediaServerId() ); WVPResult wvpResult = WVPResult.fail(ErrorCode.ERROR100.getCode(), "未找到MediaServer"); response.setBody(wvpResult); return response; @@ -247,47 +286,18 @@ public class RedisRpcController { try { mediaServerService.stopSendRtp(mediaServer, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc()); }catch (ControllerException exception) { - logger.info("[redis-rpc] 停止推流失败: {}/{}, 目标地址: {}:{}, code: {}, msg: {}", sendRtpItem.getApp(), + log.info("[redis-rpc] 停止推流失败: {}/{}, 目标地址: {}:{}, code: {}, msg: {}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort(), exception.getCode(), exception.getMsg() ); response.setBody(WVPResult.fail(exception.getCode(), exception.getMsg())); return response; } - logger.info("[redis-rpc] 停止推流成功: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort() ); + log.info("[redis-rpc] 停止推流成功: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort() ); response.setBody(WVPResult.success()); return response; } - /** - * 其他wvp通知推流已经停止了 - */ - public RedisRpcResponse rtpSendStopped(RedisRpcRequest request) { - String sendRtpItemKey = request.getParam().toString(); - SendRtpItem sendRtpItem = (SendRtpItem) redisTemplate.opsForValue().get(sendRtpItemKey); - RedisRpcResponse response = request.getResponse(); - response.setStatusCode(200); - if (sendRtpItem == null) { - logger.info("[redis-rpc] 推流已经停止, 未找到redis中的发流信息, key:{}", sendRtpItemKey); - return response; - } - logger.info("[redis-rpc] 推流已经停止: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort() ); - String platformId = sendRtpItem.getPlatformId(); - ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); - if (platform == null) { - return response; - } - try { - commanderFroPlatform.streamByeCmd(platform, sendRtpItem); - redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(), - sendRtpItem.getCallId(), sendRtpItem.getStream()); - redisCatchStorage.sendPlatformStopPlayMsg(sendRtpItem, platform); - } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 发送BYE: {}", e.getMessage()); - } - return response; - } - private void sendResponse(RedisRpcResponse response){ - logger.info("[redis-rpc] >> {}", response); + log.info("[redis-rpc] >> {}", response); response.setToId(userSetting.getServerId()); RedisRpcMessage message = new RedisRpcMessage(); message.setResponse(response); diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/service/RedisRpcServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/service/RedisRpcServiceImpl.java index 95f7c27ac..861821353 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/service/RedisRpcServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/service/RedisRpcServiceImpl.java @@ -2,28 +2,30 @@ package com.genersoft.iot.vmp.service.redisMsg.service; import com.alibaba.fastjson2.JSON; import com.genersoft.iot.vmp.common.CommonCallback; +import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.redis.RedisRpcConfig; import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcRequest; import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcResponse; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; import com.genersoft.iot.vmp.media.event.hook.Hook; import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; import com.genersoft.iot.vmp.media.event.hook.HookType; +import com.genersoft.iot.vmp.media.service.IMediaServerService; +import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; +@Slf4j @Service public class RedisRpcServiceImpl implements IRedisRpcService { - private final static Logger logger = LoggerFactory.getLogger(RedisRpcServiceImpl.class); @Autowired private RedisRpcConfig redisRpcConfig; @@ -40,6 +42,12 @@ public class RedisRpcServiceImpl implements IRedisRpcService { @Autowired private RedisTemplate redisTemplate; + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private ISendRtpServerService sendRtpServerService; + private RedisRpcRequest buildRequest(String uri, Object param) { RedisRpcRequest request = new RedisRpcRequest(); request.setFromId(userSetting.getServerId()); @@ -49,41 +57,41 @@ public class RedisRpcServiceImpl implements IRedisRpcService { } @Override - public SendRtpItem getSendRtpItem(String sendRtpItemKey) { - RedisRpcRequest request = buildRequest("getSendRtpItem", sendRtpItemKey); + public SendRtpInfo getSendRtpItem(String callId) { + RedisRpcRequest request = buildRequest("getSendRtpItem", callId); RedisRpcResponse response = redisRpcConfig.request(request, 10); if (response.getBody() == null) { return null; } - return (SendRtpItem)redisTemplate.opsForValue().get(response.getBody().toString()); + return (SendRtpInfo)redisTemplate.opsForValue().get(response.getBody().toString()); } @Override - public WVPResult startSendRtp(String sendRtpItemKey, SendRtpItem sendRtpItem) { - logger.info("[请求其他WVP] 开始推流,wvp:{}, {}/{}", sendRtpItem.getServerId(), sendRtpItem.getApp(), sendRtpItem.getStream()); - RedisRpcRequest request = buildRequest("startSendRtp", sendRtpItemKey); + public WVPResult startSendRtp(String callId, SendRtpInfo sendRtpItem) { + log.info("[请求其他WVP] 开始推流,wvp:{}, {}/{}", sendRtpItem.getServerId(), sendRtpItem.getApp(), sendRtpItem.getStream()); + RedisRpcRequest request = buildRequest("startSendRtp", callId); request.setToId(sendRtpItem.getServerId()); RedisRpcResponse response = redisRpcConfig.request(request, 10); return JSON.parseObject(response.getBody().toString(), WVPResult.class); } @Override - public WVPResult stopSendRtp(String sendRtpItemKey) { - SendRtpItem sendRtpItem = (SendRtpItem)redisTemplate.opsForValue().get(sendRtpItemKey); + public WVPResult stopSendRtp(String callId) { + SendRtpInfo sendRtpItem = (SendRtpInfo)redisTemplate.opsForValue().get(callId); if (sendRtpItem == null) { - logger.info("[请求其他WVP] 停止推流, 未找到redis中的发流信息, key:{}", sendRtpItemKey); + log.info("[请求其他WVP] 停止推流, 未找到redis中的发流信息, key:{}", callId); return WVPResult.fail(ErrorCode.ERROR100.getCode(), "未找到发流信息"); } - logger.info("[请求其他WVP] 停止推流,wvp:{}, {}/{}", sendRtpItem.getServerId(), sendRtpItem.getApp(), sendRtpItem.getStream()); - RedisRpcRequest request = buildRequest("stopSendRtp", sendRtpItemKey); + log.info("[请求其他WVP] 停止推流,wvp:{}, {}/{}", sendRtpItem.getServerId(), sendRtpItem.getApp(), sendRtpItem.getStream()); + RedisRpcRequest request = buildRequest("stopSendRtp", callId); request.setToId(sendRtpItem.getServerId()); RedisRpcResponse response = redisRpcConfig.request(request, 10); return JSON.parseObject(response.getBody().toString(), WVPResult.class); } @Override - public long waitePushStreamOnline(SendRtpItem sendRtpItem, CommonCallback callback) { - logger.info("[请求所有WVP监听流上线] {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream()); + public long waitePushStreamOnline(SendRtpInfo sendRtpItem, CommonCallback callback) { + log.info("[请求所有WVP监听流上线] {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream()); // 监听流上线。 流上线直接发送sendRtpItem消息给实际的信令处理者 Hook hook = Hook.getInstance(HookType.on_media_arrival, sendRtpItem.getApp(), sendRtpItem.getStream(), null); RedisRpcRequest request = buildRequest("waitePushStreamOnline", sendRtpItem); @@ -99,9 +107,9 @@ public class RedisRpcServiceImpl implements IRedisRpcService { sendRtpItem.setMediaServerId(hookData.getMediaServer().getId()); sendRtpItem.setLocalIp(hookData.getMediaServer().getSdpIp()); sendRtpItem.setServerId(userSetting.getServerId()); - redisTemplate.opsForValue().set(sendRtpItem.getRedisKey(), sendRtpItem); + sendRtpServerService.update(sendRtpItem); if (callback != null) { - callback.run(sendRtpItem.getRedisKey()); + callback.run(sendRtpItem.getChannelId()); } hookSubscribe.removeSubscribe(hook); redisRpcConfig.removeCallback(request.getSn()); @@ -109,13 +117,13 @@ public class RedisRpcServiceImpl implements IRedisRpcService { redisRpcConfig.request(request, response -> { if (response.getBody() == null) { - logger.info("[请求所有WVP监听流上线] 流上线,但是未找到发流信息:{}/{}", sendRtpItem.getApp(), sendRtpItem.getStream()); + log.info("[请求所有WVP监听流上线] 流上线,但是未找到发流信息:{}/{}", sendRtpItem.getApp(), sendRtpItem.getStream()); return; } - logger.info("[请求所有WVP监听流上线] 流上线 {}/{}->{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.toString()); + log.info("[请求所有WVP监听流上线] 流上线 {}/{}->{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.toString()); if (callback != null) { - callback.run(response.getBody().toString()); + callback.run(Integer.parseInt(response.getBody().toString())); } hookSubscribe.removeSubscribe(hook); }); @@ -123,8 +131,8 @@ public class RedisRpcServiceImpl implements IRedisRpcService { } @Override - public void stopWaitePushStreamOnline(SendRtpItem sendRtpItem) { - logger.info("[停止WVP监听流上线] {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream()); + public void stopWaitePushStreamOnline(SendRtpInfo sendRtpItem) { + log.info("[停止WVP监听流上线] {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream()); Hook hook = Hook.getInstance(HookType.on_media_arrival, sendRtpItem.getApp(), sendRtpItem.getStream(), null); hookSubscribe.removeSubscribe(hook); RedisRpcRequest request = buildRequest("stopWaitePushStreamOnline", sendRtpItem); @@ -133,13 +141,13 @@ public class RedisRpcServiceImpl implements IRedisRpcService { } @Override - public void rtpSendStopped(String sendRtpItemKey) { - SendRtpItem sendRtpItem = (SendRtpItem)redisTemplate.opsForValue().get(sendRtpItemKey); + public void rtpSendStopped(String callId) { + SendRtpInfo sendRtpItem = (SendRtpInfo)redisTemplate.opsForValue().get(callId); if (sendRtpItem == null) { - logger.info("[停止WVP监听流上线] 未找到redis中的发流信息, key:{}", sendRtpItemKey); + log.info("[停止WVP监听流上线] 未找到redis中的发流信息, key:{}", callId); return; } - RedisRpcRequest request = buildRequest("rtpSendStopped", sendRtpItemKey); + RedisRpcRequest request = buildRequest("rtpSendStopped", callId); request.setToId(sendRtpItem.getServerId()); redisRpcConfig.request(request, 10); } @@ -148,4 +156,49 @@ public class RedisRpcServiceImpl implements IRedisRpcService { public void removeCallback(long key) { redisRpcConfig.removeCallback(key); } + + @Override + public long onStreamOnlineEvent(String app, String stream, CommonCallback callback) { + + log.info("[请求所有WVP监听流上线] {}/{}", app, stream); + // 监听流上线。 流上线直接发送sendRtpItem消息给实际的信令处理者 + Hook hook = Hook.getInstance(HookType.on_media_arrival, app, stream); + StreamInfo streamInfoParam = new StreamInfo(); + streamInfoParam.setApp(app); + streamInfoParam.setStream(stream); + RedisRpcRequest request = buildRequest("onStreamOnlineEvent", streamInfoParam); + hookSubscribe.addSubscribe(hook, (hookData) -> { + log.info("[请求所有WVP监听流上线] 监听流上线 {}/{}", app, stream); + if (callback != null) { + callback.run(mediaServerService.getStreamInfoByAppAndStream(hookData.getMediaServer(), + app, stream, hookData.getMediaInfo(), + hookData.getMediaInfo() != null ? hookData.getMediaInfo().getCallId() : null)); + } + hookSubscribe.removeSubscribe(hook); + redisRpcConfig.removeCallback(request.getSn()); + }); + + redisRpcConfig.request(request, response -> { + if (response.getBody() == null) { + log.info("[请求所有WVP监听流上线] 流上线,但是未找到发流信息:{}/{}", app, stream); + return; + } + log.info("[请求所有WVP监听流上线] 流上线 {}/{}", app, stream); + + if (callback != null) { + callback.run(JSON.parseObject(response.getBody().toString(), StreamInfo.class)); + } + hookSubscribe.removeSubscribe(hook); + }); + return request.getSn(); + } + + @Override + public void unPushStreamOnlineEvent(String app, String stream) { + StreamInfo streamInfoParam = new StreamInfo(); + streamInfoParam.setApp(app); + streamInfoParam.setStream(stream); + RedisRpcRequest request = buildRequest("unPushStreamOnlineEvent", streamInfoParam); + redisRpcConfig.request(request, 10); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java index 869d39bf9..6d7051010 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java @@ -5,9 +5,7 @@ import com.genersoft.iot.vmp.common.SystemAllInfo; import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent; import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; -import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo; @@ -24,9 +22,9 @@ public interface IRedisCatchStorage { */ Long getCSEQ(); - void updatePlatformCatchInfo(ParentPlatformCatch parentPlatformCatch); + void updatePlatformCatchInfo(PlatformCatch parentPlatformCatch); - ParentPlatformCatch queryPlatformCatchInfo(String platformGbId); + PlatformCatch queryPlatformCatchInfo(String platformGbId); void delPlatformCatchInfo(String platformGbId); @@ -40,33 +38,6 @@ public interface IRedisCatchStorage { void delPlatformRegisterInfo(String callId); - void updateSendRTPSever(SendRtpItem sendRtpItem); - - List querySendRTPServer(String platformGbId, String channelId, String streamId); - - /** - * 查询RTP推送信息缓存 - * @param platformGbId - * @param channelId - * @return sendRtpItem - */ - SendRtpItem querySendRTPServer(String platformGbId, String channelId, String streamId, String callId); - - List querySendRTPServer(String platformGbId); - - /** - * 删除RTP推送信息缓存 - * @param platformGbId - * @param channelId - */ - void deleteSendRTPServer(String platformGbId, String channelId, String callId, String streamId); - - /** - * 查询某个通道是否存在上级点播(RTP推送) - * @param channelId - */ - boolean isChannelSendingRTP(String channelId); - /** * 在redis添加wvp的信息 */ @@ -184,10 +155,6 @@ public interface IRedisCatchStorage { */ void sendStreamPushRequestedMsgForStatus(); - List querySendRTPServerByChannelId(String channelId); - - List querySendRTPServerByStream(String stream); - SystemAllInfo getSystemInfo(); int getPushStreamCount(String id); @@ -198,9 +165,7 @@ public interface IRedisCatchStorage { void addDiskInfo(List> diskInfo); - void deleteSendRTPServer(SendRtpItem sendRtpItem); - - List queryAllSendRTPServer(); + List queryAllSendRTPServer(); List getAllDevices(); @@ -210,23 +175,23 @@ public interface IRedisCatchStorage { void sendChannelAddOrDelete(String deviceId, String channelId, boolean add); - void sendPlatformStartPlayMsg(SendRtpItem sendRtpItem, ParentPlatform platform); + void sendPlatformStartPlayMsg(SendRtpInfo sendRtpItem, DeviceChannel channel, Platform platform); - void sendPlatformStopPlayMsg(SendRtpItem sendRtpItem, ParentPlatform platform); + void sendPlatformStopPlayMsg(SendRtpInfo sendRtpItem, Platform platform, CommonGBChannel channel); - void addPushListItem(String app, String stream, MediaArrivalEvent param); + void addPushListItem(String app, String stream, MediaInfo param); - OnStreamChangedHookParam getPushListItem(String app, String stream); + MediaInfo getPushListItem(String app, String stream); void removePushListItem(String app, String stream, String mediaServerId); void sendPushStreamClose(MessageForPushChannel messageForPushChannel); - void addWaiteSendRtpItem(SendRtpItem sendRtpItem, int platformPlayTimeout); + void addWaiteSendRtpItem(SendRtpInfo sendRtpItem, int platformPlayTimeout); - SendRtpItem getWaiteSendRtpItem(String app, String stream); + SendRtpInfo getWaiteSendRtpItem(String app, String stream); - void sendStartSendRtp(SendRtpItem sendRtpItem); + void sendStartSendRtp(SendRtpInfo sendRtpItem); - void sendPushStreamOnline(SendRtpItem sendRtpItem); + void sendPushStreamOnline(SendRtpInfo sendRtpItem); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java deleted file mode 100755 index b0ff01138..000000000 --- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java +++ /dev/null @@ -1,384 +0,0 @@ -package com.genersoft.iot.vmp.storager; - -import com.genersoft.iot.vmp.gb28181.bean.*; -import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; -import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; -import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo; -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; -import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; -import com.github.pagehelper.PageInfo; - -import java.util.List; - -/** - * @description:视频设备数据存储接口 - * @author: swwheihei - * @date: 2020年5月6日 下午2:14:31 - */ -@SuppressWarnings("rawtypes") -public interface IVideoManagerStorage { - - /** - * 根据设备ID判断设备是否存在 - * - * @param deviceId 设备ID - * @return true:存在 false:不存在 - */ - public boolean exists(String deviceId); - - /** - * 开始播放 - * @param deviceId 设备id - * @param channelId 通道ID - * @param streamId 流地址 - */ - public void startPlay(String deviceId, String channelId, String streamId); - - /** - * 停止播放 - * @param deviceId 设备id - * @param channelId 通道ID - */ - public void stopPlay(String deviceId, String channelId); - - /** - * 获取设备 - * - * @param deviceId 设备ID - * @return DShadow 设备对象 - */ - public Device queryVideoDevice(String deviceId); - - /** - * 获取某个设备的通道列表 - * - * @param deviceId 设备ID - * @param page 分页 当前页 - * @param count 每页数量 - * @return - */ - public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count); - - public List queryChannelsByDeviceIdWithStartAndLimit(String deviceId, List channelIds, String query, Boolean hasSubChannel, Boolean online, int start, int limit); - - - /** - * 获取某个设备的通道列表 - * - * @param deviceId 设备ID - * @return - */ - public List queryChannelsByDeviceId(String deviceId,Boolean online,List channelIds); - public List queryOnlineChannelsByDeviceId(String deviceId); - - /** - * 获取某个设备的通道 - * @param deviceId 设备ID - * @param channelId 通道ID - */ - public DeviceChannel queryChannel(String deviceId, String channelId); - - /** - * 删除通道 - * @param deviceId 设备ID - * @param channelId 通道ID - */ - public int delChannel(String deviceId, String channelId); - - /** - * 获取多个设备 - * @param page 当前页数 - * @param count 每页数量 - * @return List 设备对象数组 - */ - public PageInfo queryVideoDeviceList(int page, int count,Boolean online); - - /** - * 获取多个设备 - * - * @return List 设备对象数组 - */ - public List queryVideoDeviceList(Boolean online); - - - - /** - * 查询子设备 - * - * @param deviceId - * @param channelId - * @param page - * @param count - * @return - */ - PageInfo querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, Boolean online, int page, int count); - - - /** - * 清空通道 - * @param deviceId - */ - void cleanChannelsForDevice(String deviceId); - - - /** - * 更新上级平台 - * @param parentPlatform - */ - boolean updateParentPlatform(ParentPlatform parentPlatform); - - - /** - * 添加上级平台 - * @param parentPlatform - */ - boolean addParentPlatform(ParentPlatform parentPlatform); - - /** - * 删除上级平台 - * @param parentPlatform - */ - boolean deleteParentPlatform(ParentPlatform parentPlatform); - - /** - * 获取所有已启用的平台 - * @return - */ - List queryEnableParentPlatformList(boolean enable); - - /** - * 获取上级平台 - * @param platformGbId - * @return - */ - ParentPlatform queryParentPlatByServerGBId(String platformGbId); - - /** - * 所有平台离线 - */ - void outlineForAllParentPlatform(); - - /** - * 查询通道信息,不区分设备(已关联平台或全部) - */ - PageInfo queryAllChannelList(int page, int count, String query, Boolean online, Boolean channelType, String platformId, String catalogId); - - /** - * 查询设备的通道信息 - */ - List queryChannelListInParentPlatform(String platformId); - - - - /** - * 移除上级平台的通道信息 - * @param platformId - * @param channelReduces - * @return - */ - int delChannelForGB(String platformId, List channelReduces); - - - DeviceChannel queryChannelInParentPlatform(String platformId, String channelId); - - List queryChannelInParentPlatformAndCatalog(String platformId, String catalogId); - List queryStreamInParentPlatformAndCatalog(String platformId, String catalogId); - - Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId); - - /** - * 针对deviceinfo指令的查询接口 - * @param platformId 平台id - * @param channelId 通道id - * @return 设备信息 - */ - Device queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId); - /** - * 添加Mobile Position设备移动位置 - * @param mobilePosition - * @return - */ - public boolean insertMobilePosition(MobilePosition mobilePosition); - - /** - * 查询移动位置轨迹 - * @param deviceId - * @param startTime - * @param endTime - */ - public List queryMobilePositions(String deviceId, String channelId, String startTime, String endTime); - - /** - * 查询最新移动位置 - * @param deviceId - */ - public MobilePosition queryLatestPosition(String deviceId); - - /** - * 删除指定设备的所有移动位置 - * @param deviceId - */ - public int clearMobilePositionsByDeviceId(String deviceId); - - /** - * 移除代理流 - * @param app - * @param stream - * @return - */ - public int deleteStreamProxy(String app, String stream); - - /** - * 按照是否启用获取代理流 - * @param enable - * @return - */ - public List getStreamProxyListForEnable(boolean enable); - - /** - * 按照是app和stream获取代理流 - * @param app - * @param stream - * @return - */ - public StreamProxyItem queryStreamProxy(String app, String stream); - - /** - * 获取代理流 - * @param page - * @param count - * @return - */ - PageInfo queryStreamProxyList(Integer page, Integer count); - - /** - * 根据国标ID获取平台关联的直播流 - * @param platformId - * @param channelId - * @return - */ - GbStream queryStreamInParentPlatform(String platformId, String channelId); - - /** - * 获取平台关联的直播流 - * @param platformId - * @return - */ - List queryGbStreamListInPlatform(String platformId); - - /** - * 移除单个推流 - * @param app - * @param stream - */ - int removeMedia(String app, String stream); - - /** - * 设置流离线 - */ - int mediaOffline(String app, String streamId); - - /** - * 设置流上线 - */ - int mediaOnline(String app, String streamId); - - /** - * 设置平台在线/离线 - */ - void updateParentPlatformStatus(String platformGbID, boolean online); - - /** - * 根据媒体ID获取启用/不启用的代理列表 - * @param id 媒体ID - * @param enable 启用/不启用 - * @return - */ - List getStreamProxyListForEnableInMediaServer(String id, boolean enable); - - /** - * 根据通道ID获取其所在设备 - * @param channelId 通道ID - * @return - */ - Device queryVideoDeviceByChannelId(String channelId); - - /** - * 通道上线 - * @param channelId 通道ID - */ - void deviceChannelOnline(String deviceId, String channelId); - - /** - * 通道离线 - * @param channelId 通道ID - */ - void deviceChannelOffline(String deviceId, String channelId); - - /** - * 通过app与stream获取StreamProxy - * @param app - * @param streamId - * @return - */ - StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId); - - /** - * catlog查询结束后完全重写通道信息 - * @param deviceId - * @param deviceChannelList - */ - boolean resetChannels(String deviceId, List deviceChannelList); - - boolean updateChannels(String deviceId, List deviceChannelList); - - /** - * 获取目录信息 - * @param platformId - * @param parentId - * @return - */ - List getChildrenCatalogByPlatform(String platformId, String parentId); - - int addCatalog(PlatformCatalog platformCatalog); - - PlatformCatalog getCatalog(String platformId, String id); - - int delCatalog(String platformId, String id); - - int updateCatalog(PlatformCatalog platformCatalog); - - int setDefaultCatalog(String platformId, String catalogId); - - List queryCatalogInPlatform(String serverGBId); - - int delRelation(PlatformCatalog platformCatalog); - - int updateStreamGPS(List gpsMsgInfo); - - List queryPlatFormListForGBWithGBId(String channelId, List platforms); - - List queryPlatFormListForStreamWithGBId(String app, String stream, List platforms); - - GbStream getGbStream(String app, String streamId); - - void delCatalogByPlatformId(String serverGBId); - - void delRelationByPlatformId(String serverGBId); - - PlatformCatalog queryDefaultCatalogInPlatform(String platformId); - - List getChannelSource(String platformId, String gbId); - - void updateChannelPosition(DeviceChannel deviceChannel); - - void cleanContentForPlatform(String serverGBId); - - List queryChannelWithCatalog(String serverGBId); - - List queryChannelsByDeviceId(String serial, List channelIds, Boolean online); - - List queryEnablePlatformListWithAsMessageChannel(); - - List queryDeviceWithAsMessageChannel(); -} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java deleted file mode 100755 index 5cd50b947..000000000 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java +++ /dev/null @@ -1,578 +0,0 @@ -package com.genersoft.iot.vmp.storager.dao; - -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannelInPlatform; -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; -import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; -import org.apache.ibatis.annotations.*; -import org.springframework.stereotype.Repository; - -import java.util.List; - -/** - * 用于存储设备通道信息 - */ -@Mapper -@Repository -public interface DeviceChannelMapper { - - @Insert("INSERT INTO wvp_device_channel (channel_id, device_id, name, manufacture, model, owner, civil_code, block, " + - "address, parental, parent_id, safety_way, register_way, cert_num, certifiable, err_code, secrecy, " + - "ip_address, port, password, ptz_type, status, stream_id, longitude, latitude, longitude_gcj02, latitude_gcj02, " + - "longitude_wgs84, latitude_wgs84, has_audio, create_time, update_time, business_group_id, gps_time, stream_identification) " + - "VALUES (#{channelId}, #{deviceId}, #{name}, #{manufacture}, #{model}, #{owner}, #{civilCode}, #{block}," + - "#{address}, #{parental}, #{parentId}, #{safetyWay}, #{registerWay}, #{certNum}, #{certifiable}, #{errCode}, #{secrecy}, " + - "#{ipAddress}, #{port}, #{password}, #{ptzType}, #{status}, #{streamId}, #{longitude}, #{latitude}, #{longitudeGcj02}, " + - "#{latitudeGcj02}, #{longitudeWgs84}, #{latitudeWgs84}, #{hasAudio}, #{createTime}, #{updateTime}, #{businessGroupId}, #{gpsTime}, #{streamIdentification})") - int add(DeviceChannel channel); - - @Update(value = {" "}) - int update(DeviceChannel channel); - - @Select(value = {" "}) - List queryChannels(@Param("deviceId") String deviceId, @Param("parentChannelId") String parentChannelId, @Param("query") String query, @Param("hasSubChannel") Boolean hasSubChannel, @Param("online") Boolean online, @Param("channelIds") List channelIds); - - @Select(value = {" "}) - List queryChannelsWithDeviceInfo(@Param("deviceId") String deviceId, @Param("parentChannelId") String parentChannelId, @Param("query") String query, @Param("hasSubChannel") Boolean hasSubChannel, @Param("online") Boolean online, @Param("channelIds") List channelIds); - - - @Select(value = {" "}) - List queryChannelsByDeviceIdWithStartAndLimit(@Param("deviceId") String deviceId, @Param("channelIds") List channelIds, @Param("parentChannelId") String parentChannelId, @Param("query") String query, @Param("hasSubChannel") Boolean hasSubChannel, @Param("online") Boolean online, @Param("start") int start, @Param("limit") int limit); - - @Select("SELECT * FROM wvp_device_channel WHERE device_id=#{deviceId} AND channel_id=#{channelId}") - DeviceChannel queryChannel(@Param("deviceId") String deviceId,@Param("channelId") String channelId); - - @Delete("DELETE FROM wvp_device_channel WHERE device_id=#{deviceId}") - int cleanChannelsByDeviceId(@Param("deviceId") String deviceId); - - @Delete("DELETE FROM wvp_device_channel WHERE device_id=#{deviceId} AND channel_id=#{channelId}") - int del(@Param("deviceId") String deviceId, @Param("channelId") String channelId); - - @Update(value = {"UPDATE wvp_device_channel SET stream_id=null WHERE device_id=#{deviceId} AND channel_id=#{channelId}"}) - void stopPlay(@Param("deviceId") String deviceId, @Param("channelId") String channelId); - - @Update(value = {"UPDATE wvp_device_channel SET stream_id=#{streamId} WHERE device_id=#{deviceId} AND channel_id=#{channelId}"}) - void startPlay(@Param("deviceId") String deviceId, @Param("channelId") String channelId, @Param("streamId") String streamId); - - - @Select(value = {" "}) - List queryChannelListInAll(@Param("query") String query, @Param("online") Boolean online, @Param("hasSubChannel") Boolean hasSubChannel, @Param("platformId") String platformId, @Param("catalogId") String catalogId); - - @Select(value = {" "}) - List queryChannelByPlatformId(String platformId); - - - @Select("SELECT * FROM wvp_device_channel WHERE channel_id=#{channelId}") - List queryChannelByChannelId( String channelId); - - @Update(value = {"UPDATE wvp_device_channel SET status=false WHERE device_id=#{deviceId} AND channel_id=#{channelId}"}) - void offline(@Param("deviceId") String deviceId, @Param("channelId") String channelId); - - @Update(value = {"UPDATE wvp_device_channel SET status=false WHERE device_id=#{deviceId}"}) - void offlineByDeviceId(String deviceId); - - @Insert("") - int batchAdd(@Param("addChannels") List addChannels); - - - @Insert("") - int batchAddOrUpdate(List addChannels); - - @Update(value = {"UPDATE wvp_device_channel SET status=true WHERE device_id=#{deviceId} AND channel_id=#{channelId}"}) - void online(@Param("deviceId") String deviceId, @Param("channelId") String channelId); - - @Update({""}) - int batchUpdate(List updateChannels); - - - @Select("SELECT * FROM wvp_device_channel WHERE device_id=#{deviceId} AND status=true") - List queryOnlineChannelsByDeviceId(String deviceId); - - @Delete(value = {" "}) - int cleanChannelsNotInList(@Param("deviceId") String deviceId, @Param("channels") List channels); - - @Update(" update wvp_device_channel" + - " set sub_count = (select *" + - " from (select count(0)" + - " from wvp_device_channel" + - " where device_id = #{deviceId} and parent_id = #{channelId}) as temp)" + - " where device_id = #{deviceId} " + - " and channel_id = #{channelId}") - int updateChannelSubCount(@Param("deviceId") String deviceId, @Param("channelId") String channelId); - - @Update(value = {" "}) - int updatePosition(DeviceChannel deviceChannel); - - @Select("SELECT * FROM wvp_device_channel WHERE length(trim(stream_id)) > 0") - List getAllChannelInPlay(); - - @Select("select * from wvp_device_channel where longitude*latitude > 0 and device_id = #{deviceId}") - List getAllChannelWithCoordinate(String deviceId); - - - @Select(value = {" "}) - List getChannelsWithCivilCodeAndLength(@Param("deviceId") String deviceId, @Param("parentId") String parentId, @Param("length") Integer length); - - @Select(value = {" "}) - List getChannelsByCivilCode(@Param("deviceId") String deviceId, @Param("parentId") String parentId); - - @Select("select min(length(channel_id)) as minLength " + - "from wvp_device_channel " + - "where device_id=#{deviceId}") - Integer getChannelMinLength(String deviceId); - - @Select("select * from wvp_device_channel where device_id=#{deviceId} and civil_code not in " + - "(select civil_code from wvp_device_channel where device_id=#{deviceId} group by civil_code)") - List getChannelWithoutCivilCode(String deviceId); - - @Select("select * from wvp_device_channel where device_id=#{deviceId} and SUBSTRING(channel_id, 11, 3)=#{typeCode}") - List getBusinessGroups(@Param("deviceId") String deviceId, @Param("typeCode") String typeCode); - - @Select("select dc.id, dc.channel_id, dc.device_id, COALESCE(dc.custom_name, dc.name) AS name, dc.manufacture,dc.model,dc.owner, pc.civil_code,dc.block, " + - " dc.address, '0' as parental,'0' as channel_type, pc.id as parent_id, dc.safety_way, dc.register_way,dc.cert_num, dc.certifiable, " + - " dc.err_code,dc.end_time, dc.secrecy, dc.ip_address, dc.port, COALESCE(dc.custom_ptz_type, dc.ptz_type) AS ptz_type, dc.password, dc.status, " + - " COALESCE(dc.custom_longitude, dc.longitude) AS longitude, COALESCE(dc.custom_latitude, dc.latitude) AS latitude, pc.business_group_id " + - " from wvp_device_channel dc" + - " LEFT JOIN wvp_platform_gb_channel pgc on dc.id = pgc.device_channel_id" + - " LEFT JOIN wvp_platform_catalog pc on pgc.catalog_id = pc.id and pgc.platform_id = pc.platform_id" + - " where pgc.platform_id=#{serverGBId}") - List queryChannelWithCatalog(String serverGBId); - - @Select("select * from wvp_device_channel where device_id = #{deviceId}") - List queryAllChannels(String deviceId); - - - @Select("select channelId" + - ", device_id" + - ", latitude" + - ", longitude"+ - ",latitude_wgs84"+ - ",longitude_wgs84"+ - ",latitude_gcj02"+ - ",longitude_gcj02"+ - "from wvp_device_channel where device_id = #{deviceId} " + - "and latitude != 0 " + - "and longitude != 0 " + - "and(latitude_gcj02=0 or latitude_wgs84=0 or longitude_wgs84= 0 or longitude_gcj02 = 0)") - List getChannelsWithoutTransform(String deviceId); - - @Select("select de.* from wvp_device de left join wvp_device_channel dc on de.device_id = dc.deviceId where dc.channel_id=#{channelId}") - List getDeviceByChannelId(String channelId); - - - @Delete({""}) - int batchDel(@Param("deleteChannelList") List deleteChannelList); - - @Update({""}) - int batchOnline(@Param("channels") List channels); - - @Update({""}) - int batchOffline(List channels); - - - @Select("select count(1) from wvp_device_channel where status = true") - int getOnlineCount(); - - @Select("select count(1) from wvp_device_channel") - int getAllChannelCount(); - - // 设备主子码流逻辑START - @Update(value = {"UPDATE wvp_device_channel SET stream_id=null WHERE device_id=#{deviceId}"}) - void clearPlay(String deviceId); - // 设备主子码流逻辑END - @Select(value = {" "}) - List getSubChannelsByDeviceId(@Param("deviceId") String deviceId, @Param("parentId") String parentId, @Param("onlyCatalog") boolean onlyCatalog); - - @Update("") - void updateChannelStreamIdentification(DeviceChannel channel); - - - @Update({""}) - void batchUpdatePosition(List channelList); - -} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java deleted file mode 100755 index 3790bdabb..000000000 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.genersoft.iot.vmp.storager.dao; - -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; -import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; -import org.apache.ibatis.annotations.*; -import org.apache.ibatis.annotations.Param; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Map; - -@Mapper -@Repository -public interface GbStreamMapper { - - @Insert("INSERT INTO wvp_gb_stream (app, stream, gb_id, name, " + - "longitude, latitude, stream_type,media_server_id,create_time) VALUES" + - "(#{app}, #{stream}, #{gbId}, #{name}, " + - "#{longitude}, #{latitude}, #{streamType}, " + - "#{mediaServerId}, #{createTime})") - @Options(useGeneratedKeys = true, keyProperty = "gbStreamId", keyColumn = "gb_stream_id") - int add(GbStream gbStream); - - @Update("UPDATE wvp_gb_stream " + - "SET app=#{app}," + - "stream=#{stream}," + - "gb_id=#{gbId}," + - "name=#{name}," + - "stream_type=#{streamType}," + - "longitude=#{longitude}, " + - "latitude=#{latitude}," + - "media_server_id=#{mediaServerId}" + - "WHERE app=#{app} AND stream=#{stream}") - int updateByAppAndStream(GbStream gbStream); - - @Update("UPDATE wvp_gb_stream " + - "SET app=#{app}," + - "stream=#{stream}," + - "gb_id=#{gbId}," + - "name=#{name}," + - "stream_type=#{streamType}," + - "longitude=#{longitude}, " + - "latitude=#{latitude}," + - "media_server_id=#{mediaServerId}" + - "WHERE gb_stream_id=#{gbStreamId}") - int update(GbStream gbStream); - - @Delete("DELETE FROM wvp_gb_stream WHERE app=#{app} AND stream=#{stream}") - int del(@Param("app") String app, @Param("stream") String stream); - - @Select("") - List selectAll(@Param("platformId") String platformId, @Param("catalogId") String catalogId, @Param("query") String query, @Param("mediaServerId") String mediaServerId); - - @Select("SELECT * FROM wvp_gb_stream WHERE app=#{app} AND stream=#{stream}") - GbStream selectOne(@Param("app") String app, @Param("stream") String stream); - - @Select("SELECT * FROM wvp_gb_stream WHERE gb_id=#{gbId}") - List selectByGBId(String gbId); - - @Select("SELECT gs.*, pgs.platform_id as platform_id, pgs.catalog_id as catalog_id FROM wvp_gb_stream gs " + - "LEFT JOIN wvp_platform_gb_stream pgs ON gs.gb_stream_id = pgs.gb_stream_id " + - "WHERE gs.gb_id = #{gbId} AND pgs.platform_id = #{platformId}") - GbStream queryStreamInPlatform(@Param("platformId") String platformId, @Param("gbId") String gbId); - - @Select("") - List queryGbStreamListInPlatform(String platformId, @Param("usPushingAsStatus") boolean usPushingAsStatus); - - - @Select("SELECT gs.* FROM wvp_gb_stream gs left join wvp_platform_gb_stream pgs " + - "ON gs.gb_stream_id = pgs.gb_stream_id WHERE pgs.gb_stream_id is NULL") - List queryStreamNotInPlatform(); - - @Delete("DELETE FROM wvp_gb_stream WHERE stream_type=#{type} AND gb_id=NULL AND media_server_id=#{mediaServerId}") - void deleteWithoutGBId(@Param("type") String type, @Param("mediaServerId") String mediaServerId); - - @Delete("") - void batchDel(List streamProxyItemList); - - @Delete("") - void batchDelForGbStream(List gbStreams); - - @Insert("") - @Options(useGeneratedKeys = true, keyProperty = "gbStreamId", keyColumn = "gb_stream_id") - void batchAdd(@Param("subList") List subList); - - @Update({""}) - int updateStreamGPS(List gpsMsgInfos); - - @Select("") - List selectAllForAppAndStream(List streamPushItems); - - @Update("UPDATE wvp_gb_stream " + - "SET media_server_id=#{mediaServerId}" + - "WHERE app=#{app} AND stream=#{stream}") - void updateMediaServer(String app, String stream, String mediaServerId); - - @Update("") - int updateGbIdOrName(List streamPushItemForUpdate); - - @Select("SELECT status FROM wvp_stream_proxy WHERE app=#{app} AND stream=#{stream}") - Boolean selectStatusForProxy(@Param("app") String app, @Param("stream") String stream); - - @Select("SELECT status FROM wvp_stream_push WHERE app=#{app} AND stream=#{stream}") - Boolean selectStatusForPush(@Param("app") String app, @Param("stream") String stream); - - @MapKey("gbId") - @Select("SELECT * from wvp_gb_stream") - Map getAllGBId(); -} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java deleted file mode 100755 index 63b19bb47..000000000 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.genersoft.iot.vmp.storager.dao; - -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo; -import org.apache.ibatis.annotations.*; -import org.apache.ibatis.annotations.Param; -import org.springframework.stereotype.Repository; - -import java.util.List; - -/** - * 用于存储上级平台 - */ -@Mapper -@Repository -public interface ParentPlatformMapper { - - @Insert("INSERT INTO wvp_platform (enable, name, server_gb_id, server_gb_domain, server_ip, server_port,device_gb_id,device_ip,"+ - "device_port,username,password,expires,keep_timeout,transport,character_set,ptz,rtcp,as_message_channel,auto_push_channel,"+ - "status,start_offline_push,catalog_id,administrative_division,catalog_group,create_time,update_time,send_stream_ip) " + - " VALUES (#{enable}, #{name}, #{serverGBId}, #{serverGBDomain}, #{serverIP}, #{serverPort}, #{deviceGBId}, #{deviceIp}, " + - " #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, #{asMessageChannel}, #{autoPushChannel}, " + - " #{status}, #{startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime}, #{sendStreamIp})") - int addParentPlatform(ParentPlatform parentPlatform); - - @Update("UPDATE wvp_platform " + - "SET enable=#{enable}, " + - "name=#{name}," + - "device_gb_id=#{deviceGBId}," + - "server_gb_id=#{serverGBId}, " + - "server_gb_domain=#{serverGBDomain}, " + - "server_ip=#{serverIP}," + - "server_port=#{serverPort}, " + - "device_ip=#{deviceIp}, " + - "device_port=#{devicePort}, " + - "username=#{username}, " + - "password=#{password}, " + - "expires=#{expires}, " + - "keep_timeout=#{keepTimeout}, " + - "transport=#{transport}, " + - "character_set=#{characterSet}, " + - "ptz=#{ptz}, " + - "rtcp=#{rtcp}, " + - "as_message_channel=#{asMessageChannel}, " + - "auto_push_channel=#{autoPushChannel}, " + - "status=#{status}, " + - "start_offline_push=#{startOfflinePush}, " + - "catalog_group=#{catalogGroup}, " + - "administrative_division=#{administrativeDivision}, " + - "create_time=#{createTime}, " + - "update_time=#{updateTime}, " + - "send_stream_ip=#{sendStreamIp}, " + - "catalog_id=#{catalogId} " + - "WHERE id=#{id}") - int updateParentPlatform(ParentPlatform parentPlatform); - - @Delete("DELETE FROM wvp_platform WHERE server_gb_id=#{serverGBId}") - int delParentPlatform(ParentPlatform parentPlatform); - - @Select("SELECT *, ((SELECT count(0)\n" + - " FROM wvp_platform_gb_channel pc\n" + - " WHERE pc.platform_id = pp.server_gb_id)\n" + - " +\n" + - " (SELECT count(0)\n" + - " FROM wvp_platform_gb_stream pgs\n" + - " WHERE pgs.platform_id = pp.server_gb_id)\n" + - " +\n" + - " (SELECT count(0)\n" + - " FROM wvp_platform_catalog pgc\n" + - " WHERE pgc.platform_id = pp.server_gb_id)) as channel_count\n" + - "FROM wvp_platform pp ") - List getParentPlatformList(); - - @Select("SELECT * FROM wvp_platform WHERE enable=#{enable} ") - List getEnableParentPlatformList(boolean enable); - - @Select("SELECT * FROM wvp_platform WHERE enable=true and as_message_channel=true") - List queryEnablePlatformListWithAsMessageChannel(); - - @Select("SELECT * FROM wvp_platform WHERE server_gb_id=#{platformGbId}") - ParentPlatform getParentPlatByServerGBId(String platformGbId); - - @Select("SELECT * FROM wvp_platform WHERE id=#{id}") - ParentPlatform getParentPlatById(int id); - - @Update("UPDATE wvp_platform SET status=false" ) - int outlineForAllParentPlatform(); - - @Update("UPDATE wvp_platform SET status=#{online} WHERE server_gb_id=#{platformGbID}" ) - int updateParentPlatformStatus(@Param("platformGbID") String platformGbID, @Param("online") boolean online); - - @Update(value = {" "}) - int setDefaultCatalog(@Param("platformId") String platformId, @Param("catalogId") String catalogId, @Param("updateTime") String updateTime); - - @Select("select 'channel' as name, count(pgc.platform_id) count from wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id where pgc.platform_id=#{platform_id} and dc.channel_id =#{gbId} " + - "union " + - "select 'stream' as name, count(pgs.platform_id) count from wvp_platform_gb_stream pgs left join wvp_gb_stream gs on pgs.gb_stream_id = gs.gb_stream_id where pgs.platform_id=#{platform_id} and gs.gb_id =#{gbId}") - List getChannelSource(@Param("platform_id") String platform_id, @Param("gbId") String gbId); -} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformCatalogMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformCatalogMapper.java deleted file mode 100755 index 62d610190..000000000 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformCatalogMapper.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.genersoft.iot.vmp.storager.dao; - -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog; -import org.apache.ibatis.annotations.*; -import org.apache.ibatis.annotations.Param; -import org.springframework.stereotype.Repository; - -import java.util.List; - - -@Mapper -@Repository -public interface PlatformCatalogMapper { - - @Insert("INSERT INTO wvp_platform_catalog (id, name, platform_id, parent_id, civil_code, business_group_id) VALUES" + - "(#{id}, #{name}, #{platformId}, #{parentId}, #{civilCode}, #{businessGroupId})") - int add(PlatformCatalog platformCatalog); - - @Delete("DELETE from wvp_platform_catalog WHERE platform_id=#{platformId} and id=#{id}") - int del(@Param("platformId") String platformId, @Param("id") String id); - - @Delete("DELETE from wvp_platform_catalog WHERE platform_id=#{platformId}") - int delByPlatformId(@Param("platformId") String platformId); - - @Select("SELECT pc.*, count(pc2.id) as children_count from wvp_platform_catalog pc " + - "left join wvp_platform_catalog pc2 on pc.id = pc2.parent_id " + - "WHERE pc.parent_id=#{parentId} AND pc.platform_id=#{platformId} " + - "group by pc.id, pc.name, pc.platform_id, pc.business_group_id, pc.civil_code, pc.parent_id") - List selectByParentId(@Param("platformId") String platformId, @Param("parentId") String parentId); - - @Update(value = {" "}) - int update(@Param("platformCatalog") PlatformCatalog platformCatalog); - - @Select("SELECT *, (SELECT COUNT(1) from wvp_platform_catalog where parent_id = pc.id) as children_count from wvp_platform_catalog pc WHERE pc.platform_id=#{platformId}") - List selectByPlatForm(@Param("platformId") String platformId); - - @Select("SELECT pc.* FROM wvp_platform_catalog pc WHERE pc.id = (SELECT pp.catalog_id from wvp_platform pp WHERE pp.server_gb_id=#{platformId})") - PlatformCatalog selectDefaultByPlatFormId(@Param("platformId") String platformId); - - @Select("SELECT pc.id as channel_id, pc.name, pc.civil_code, pc.business_group_id,'1' as parental, pc.parent_id " + - " from wvp_platform_catalog pc WHERE pc.platform_id=#{platformId}") - List queryCatalogInPlatform(@Param("platformId") String platformId); - - @Select("SELECT *, " + - "(SELECT COUNT(1) from wvp_platform_catalog where parent_id = pc.id) as children_count " + - " from wvp_platform_catalog pc " + - " WHERE pc.id=#{id} and pc.platform_id=#{platformId}") - PlatformCatalog selectByPlatFormAndCatalogId(@Param("platformId") String platformId, @Param("id") String id); - - - @Delete("") - int deleteAll(String platformId, List ids); - - @Select("SELECT id from wvp_platform_catalog WHERE platform_id=#{platformId} and parent_id = #{id}") - List queryCatalogFromParent(@Param("id") String id, @Param("platformId") String platformId); -} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java deleted file mode 100755 index f5ce3f0a0..000000000 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.genersoft.iot.vmp.storager.dao; - -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog; -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; -import org.apache.ibatis.annotations.Delete; -import org.apache.ibatis.annotations.Insert; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; -import org.springframework.stereotype.Repository; - -import java.util.List; - -@Mapper -@Repository -public interface PlatformChannelMapper { - - /** - * 查询列表里已经关联的 - */ - @Select("") - List findChannelRelatedPlatform(@Param("platformId") String platformId, @Param("channelReduces") List channelReduces); - - @Insert("") - int addChannels(@Param("platformId") String platformId, @Param("channelReducesToAdd") List channelReducesToAdd); - - @Delete("") - int delChannelForGB(@Param("platformId") String platformId, @Param("channelReducesToDel") List channelReducesToDel); - - @Delete("") - int delChannelForDeviceId(String deviceId); - - @Delete("") - int cleanChannelForGB(String platformId); - - @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 queryChannelInParentPlatform(@Param("platformId") String platformId, @Param("channelId") String channelId); - - @Select("") - List 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 " + - " from wvp_device_channel dc left join wvp_platform_gb_channel pgc on dc.id = pgc.device_channel_id " + - " where pgc.platform_id=#{platformId} and pgc.catalog_id=#{catalogId}") - List queryChannelInParentPlatformAndCatalog(@Param("platformId") String platformId, @Param("catalogId") String catalogId); - - @Select("select d.*\n" + - "from wvp_platform_gb_channel pgc\n" + - " left join wvp_device_channel dc on dc.id = pgc.device_channel_id\n" + - " left join wvp_device d on dc.device_id = d.device_id\n" + - "where dc.channel_id = #{channelId} and pgc.platform_id=#{platformId}") - List queryVideoDeviceByPlatformIdAndChannelId(@Param("platformId") String platformId, @Param("channelId") String channelId); - - @Delete("") - int delByCatalogId(@Param("platformId") String platformId, @Param("id") String id); - - @Delete("") - int delByCatalogIdAndChannelIdAndPlatformId(PlatformCatalog platformCatalog); - - @Select(" ") - List queryPlatFormListForGBWithGBId(@Param("channelId") String channelId, @Param("platforms") List platforms); - - @Delete("") - void delByPlatformId(String serverGBId); - - @Delete("") - int delChannelForGBByCatalogId(@Param("platformId") String platformId, @Param("catalogId") String catalogId); - - @Select("select dc.channel_id dc.device_id,dc.name,d.manufacturer,d.model,d.firmware\n" + - "from wvp_platform_gb_channel pgc\n" + - " left join wvp_device_channel dc on dc.id = pgc.device_channel_id\n" + - " left join wvp_device d on dc.device_id = d.device_id\n" + - "where dc.channel_id = #{channelId} and pgc.platform_id=#{platformId}") - List queryDeviceInfoByPlatformIdAndChannelId(@Param("platformId") String platformId, @Param("channelId") String channelId); - - @Select("SELECT pgc.platform_id from wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id WHERE dc.channel_id=#{channelId}") - List queryParentPlatformByChannelId(@Param("channelId") String channelId); -} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformGbStreamMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformGbStreamMapper.java deleted file mode 100755 index e4fc3568a..000000000 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformGbStreamMapper.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.genersoft.iot.vmp.storager.dao; - -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog; -import com.genersoft.iot.vmp.gb28181.bean.PlatformGbStream; -import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; -import org.apache.ibatis.annotations.*; -import org.springframework.stereotype.Repository; - -import java.util.List; - - -@Mapper -@Repository -public interface PlatformGbStreamMapper { - - @Insert("INSERT INTO wvp_platform_gb_stream (gb_stream_id, platform_id, catalog_id) VALUES" + - "( #{gbStreamId}, #{platformId}, #{catalogId})") - int add(PlatformGbStream platformGbStream); - - - @Insert("") - int batchAdd(List streamPushItems); - - @Delete("DELETE from wvp_platform_gb_stream WHERE gb_stream_id = (select gb_stream_id from wvp_gb_stream where app=#{app} AND stream=#{stream})") - int delByAppAndStream(@Param("app") String app, @Param("stream") String stream); - - @Delete("DELETE from wvp_platform_gb_stream WHERE platform_id=#{platformId}") - int delByPlatformId(String platformId); - - @Select("SELECT " + - "pp.* " + - "FROM " + - "wvp_platform_gb_stream pgs " + - "LEFT JOIN wvp_platform pp ON pp.server_gb_id = pgs.platform_id " + - "LEFT join wvp_gb_stream gs ON gs.gb_stream_id = pgs.gb_stream_id " + - "WHERE " + - "gs.app =#{app} " + - "AND gs.stream =#{stream} ") - List selectByAppAndStream(@Param("app") String app, @Param("stream") String stream); - - @Select("SELECT pgs.*, gs.gb_id from wvp_platform_gb_stream pgs " + - "LEFT join wvp_gb_stream gs ON pgs.gb_stream_id = gs.gb_stream_id " + - "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("") - List 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" + - "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}") - List queryChannelInParentPlatformAndCatalogForCatalog(@Param("platformId") String platformId, @Param("catalogId") String catalogId); - - @Select(" ") - List queryPlatFormListForGBWithGBId(@Param("app") String app, @Param("stream") String stream, @Param("platforms") List platforms); - - @Delete("DELETE from wvp_platform_gb_stream WHERE gb_stream_id = (select id from wvp_gb_stream where app=#{app} AND stream=#{stream}) AND platform_id=#{platformId}") - int delByAppAndStreamAndPlatform(String app, String stream, String platformId); - - @Delete("") - void delByGbStreams(List gbStreams); - - @Delete("") - void delByAppAndStreamsByPlatformId(@Param("gbStreams") List gbStreams, @Param("platformId") String platformId); - - @Delete("") - int delByPlatformAndCatalogId(@Param("platformId") String platformId, @Param("catalogId") String catalogId); -} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java deleted file mode 100755 index 6ad36cef6..000000000 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.genersoft.iot.vmp.storager.dao; - -import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; -import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; -import org.apache.ibatis.annotations.*; -import org.springframework.stereotype.Repository; - -import java.util.List; - -@Mapper -@Repository -public interface StreamProxyMapper { - - @Insert("INSERT INTO wvp_stream_proxy (type, name, app, stream,media_server_id, url, src_url, dst_url, " + - "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_audio, enable_mp4, enable, status, stream_key, enable_remove_none_reader, enable_disable_none_reader, create_time) VALUES" + - "(#{type}, #{name}, #{app}, #{stream}, #{mediaServerId}, #{url}, #{srcUrl}, #{dstUrl}, " + - "#{timeoutMs}, #{ffmpegCmdKey}, #{rtpType}, #{enableAudio}, #{enableMp4}, #{enable}, #{status}, #{streamKey}, " + - "#{enableRemoveNoneReader}, #{enableDisableNoneReader}, #{createTime} )") - int add(StreamProxyItem streamProxyDto); - - @Update("UPDATE wvp_stream_proxy " + - "SET type=#{type}, " + - "name=#{name}," + - "app=#{app}," + - "stream=#{stream}," + - "url=#{url}, " + - "media_server_id=#{mediaServerId}, " + - "src_url=#{srcUrl}," + - "dst_url=#{dstUrl}, " + - "timeout_ms=#{timeoutMs}, " + - "ffmpeg_cmd_key=#{ffmpegCmdKey}, " + - "rtp_type=#{rtpType}, " + - "enable_audio=#{enableAudio}, " + - "enable=#{enable}, " + - "status=#{status}, " + - "stream_key=#{streamKey}, " + - "enable_remove_none_reader=#{enableRemoveNoneReader}, " + - "enable_disable_none_reader=#{enableDisableNoneReader}, " + - "enable_mp4=#{enableMp4} " + - "WHERE app=#{app} AND stream=#{stream}") - int update(StreamProxyItem streamProxyDto); - - @Delete("DELETE FROM wvp_stream_proxy WHERE app=#{app} AND stream=#{stream}") - int del(String app, String stream); - - @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream order by st.create_time desc") - List selectAll(); - - @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude, 'proxy' as streamType FROM wvp_stream_proxy st LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable=#{enable} order by st.create_time desc") - List selectForEnable(boolean enable); - - @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.app=#{app} AND st.stream=#{stream} order by st.create_time desc") - StreamProxyItem selectOne(@Param("app") String app, @Param("stream") String stream); - - @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st " + - "LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream " + - "WHERE st.enable=#{enable} and st.media_server_id= #{id} order by st.create_time desc") - List selectForEnableInMediaServer( @Param("id") String id, @Param("enable") boolean enable); - - @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st " + - "LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream " + - "WHERE st.media_server_id= #{id} order by st.create_time desc") - List selectInMediaServer(String id); - - @Update("UPDATE wvp_stream_proxy " + - "SET status=#{status} " + - "WHERE media_server_id=#{mediaServerId}") - void updateStatusByMediaServerId(@Param("mediaServerId") String mediaServerId, @Param("status") boolean status); - - @Update("UPDATE wvp_stream_proxy " + - "SET status=#{status} " + - "WHERE app=#{app} AND stream=#{stream}") - int updateStatus(@Param("app") String app, @Param("stream") String stream, @Param("status") boolean status); - - @Delete("DELETE FROM wvp_stream_proxy WHERE enable_remove_none_reader=true AND media_server_id=#{mediaServerId}") - void deleteAutoRemoveItemByMediaServerId(String mediaServerId); - - @Select("SELECT st.*, pgs.gb_id, pgs.name, pgs.longitude, pgs.latitude FROM wvp_stream_proxy st LEFT join wvp_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable_remove_none_reader=true AND st.media_server_id=#{mediaServerId} order by st.create_time desc") - List selectAutoRemoveItemByMediaServerId(String mediaServerId); - - @Select("select count(1) as total, sum(status) as online from wvp_stream_proxy") - ResourceBaseInfo getOverview(); - - @Select("select count(1) from wvp_stream_proxy") - - int getAllCount(); - - @Select("select count(1) from wvp_stream_proxy where status = true") - int getOnline(); -} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java deleted file mode 100755 index daf21eff6..000000000 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java +++ /dev/null @@ -1,207 +0,0 @@ -package com.genersoft.iot.vmp.storager.dao; - -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; -import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis; -import org.apache.ibatis.annotations.*; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Map; - -@Mapper -@Repository -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, server_id, update_time, create_time, push_ing, self) VALUES" + - "(#{app}, #{stream}, #{totalReaderCount}, #{originType}, #{originTypeStr}, " + - "#{pushTime}, #{aliveSecond}, #{mediaServerId} , #{serverId} , #{updateTime} , #{createTime}, " + - "#{pushIng}, #{self} )") - int add(StreamPushItem streamPushItem); - - - @Update(value = {" "}) - int update(StreamPushItem streamPushItem); - - @Delete("DELETE FROM wvp_stream_push WHERE app=#{app} AND stream=#{stream}") - int del(String app, String stream); - - @Delete("") - int delAllWithoutGBId(List streamPushItems); - - @Delete("") - int delAll(List streamPushItems); - - @Delete("") - int delAllForGbStream(List gbStreams); - - - @Select(value = {" "}) - List selectAllForList(@Param("query") String query, @Param("pushing") Boolean pushing, @Param("mediaServerId") String mediaServerId); - - @Select("SELECT st.*, gs.gb_id, gs.name, gs.longitude, gs.latitude FROM wvp_stream_push st LEFT join wvp_gb_stream gs on st.app = gs.app AND st.stream = gs.stream order by st.create_time desc") - List selectAll(); - - @Select("SELECT st.*, gs.gb_id, gs.name, gs.longitude, gs.latitude FROM wvp_stream_push st LEFT join wvp_gb_stream gs on st.app = gs.app AND st.stream = gs.stream WHERE st.app=#{app} AND st.stream=#{stream}") - StreamPushItem selectOne(@Param("app") String app, @Param("stream") String stream); - - @Insert("") - @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") - int addAll(List streamPushItems); - - @Delete("DELETE FROM wvp_stream_push") - void clear(); - - @Delete("delete" + - " from wvp_stream_push " + - " where id in " + - " (select temp.id from " + - " (select wgs.gb_stream_id as id " + - " from wvp_gb_stream wgs" + - " left join wvp_stream_push sp on sp.id = wgs.gb_stream_id" + - " where wgs.gb_id is null and wgs.media_server_id = #{mediaServerId}) temp)" - ) - void deleteWithoutGBId(String mediaServerId); - - @Select("SELECT * FROM wvp_stream_push WHERE media_server_id=#{mediaServerId}") - List selectAllByMediaServerId(String mediaServerId); - - @Select("SELECT sp.* FROM wvp_stream_push sp left join wvp_gb_stream gs on gs.app = sp.app and gs.stream= sp.stream WHERE sp.media_server_id=#{mediaServerId} and gs.gb_id is null") - List selectAllByMediaServerIdWithOutGbID(String mediaServerId); - - @Update("UPDATE wvp_stream_push " + - "SET status=#{status} " + - "WHERE app=#{app} AND stream=#{stream}") - int updateStatus(@Param("app") String app, @Param("stream") String stream, @Param("status") boolean status); - - @Update("UPDATE wvp_stream_push " + - "SET push_ing=#{pushIng} " + - "WHERE app=#{app} AND stream=#{stream}") - int updatePushStatus(@Param("app") String app, @Param("stream") String stream, @Param("pushIng") boolean pushIng); - - @Update("UPDATE wvp_stream_push " + - "SET status=#{status} " + - "WHERE media_server_id=#{mediaServerId}") - void updateStatusByMediaServerId(@Param("mediaServerId") String mediaServerId, @Param("status") boolean status); - - - @Select("") - List getOnlinePusherForGbInList(List offlineStreams); - - @Update("") - void offline(List offlineStreams); - - @Select("") - List getOfflinePusherForGbInList(List onlineStreams); - - @Update("") - void online(List onlineStreams); - - @Select("SELECT gs.* FROM wvp_stream_push sp left join wvp_gb_stream gs on sp.app = gs.app AND sp.stream = gs.stream where sp.status = true") - List getOnlinePusherForGb(); - - @Update("UPDATE wvp_stream_push SET status=0") - void setAllStreamOffline(); - - @Select("SELECT CONCAT(app,stream) from wvp_gb_stream") - List getAllAppAndStream(); - - @Select("select count(1) from wvp_stream_push ") - int getAllCount(); - - @Select(value = {" "}) - int getAllOnline(Boolean usePushingAsStatus); - - @Select("") - List getListIn(List streamPushItems); - - @MapKey("vhost") - @Select("SELECT CONCAT(wsp.app, wsp.stream) as vhost, wsp.app, wsp.stream, wgs.gb_id, wgs.name " + - " from wvp_stream_push wsp " + - " left join wvp_gb_stream wgs on wgs.app = wsp.app and wgs.stream = wsp.stream") - Map getAllAppAndStreamMap(); - - -} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java index 168a5778f..8bfeab789 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java @@ -29,7 +29,7 @@ public interface UserMapper { @Delete("DELETE from wvp_user WHERE id != 1 and id=#{id}") int delete(int id); - @Select("select u.*, r.id as role_id, r.name as roleName, r.authority as roleAuthority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u, wvp_user_role r WHERE u.role_id=r.id and u.username=#{username} AND u.password=#{password}") + @Select("select u.*, r.name as roleName, r.authority as roleAuthority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u, wvp_user_role r WHERE u.role_id=r.id and u.username=#{username} AND u.password=#{password}") @Results(id = "roleMap", value = { @Result(column = "role_id", property = "role.id"), @Result(column = "role_name", property = "role.name"), @@ -39,19 +39,19 @@ public interface UserMapper { }) User select(@Param("username") String username, @Param("password") String password); - @Select("select u.*, r.id as role_id, r.name as role_name, r.authority as role_authority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u, wvp_user_role r WHERE u.role_id=r.id and u.id=#{id}") + @Select("select u.*, r.name as role_name, r.authority as role_authority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u, wvp_user_role r WHERE u.role_id=r.id and u.id=#{id}") @ResultMap(value="roleMap") User selectById(int id); - @Select("select u.*, r.id as role_id, r.name as role_name, r.authority as role_authority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u, wvp_user_role r WHERE u.role_id=r.id and u.username=#{username}") + @Select("select u.*, r.name as role_name, r.authority as role_authority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u, wvp_user_role r WHERE u.role_id=r.id and u.username=#{username}") @ResultMap(value="roleMap") User getUserByUsername(String username); - @Select("select u.*, r.id as role_id, r.name as role_name, r.authority as role_authority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u, wvp_user_role r WHERE u.role_id=r.id") + @Select("select u.*, r.name as role_name, r.authority as role_authority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u, wvp_user_role r WHERE u.role_id=r.id") @ResultMap(value="roleMap") List selectAll(); - @Select("select u.id, u.username,u.push_key,u.role_id, r.id as role_id, r.name as role_name, r.authority as role_authority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u join wvp_user_role r on u.role_id=r.id") + @Select("select u.id, u.username,u.push_key,u.role_id, r.name as role_name, r.authority as role_authority , r.create_time as role_create_time , r.update_time as role_update_time from wvp_user u join wvp_user_role r on u.role_id=r.id") @ResultMap(value="roleMap") List getUsers(); diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java index e70e195c7..81c8b3dad 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java @@ -6,24 +6,20 @@ import com.genersoft.iot.vmp.common.SystemAllInfo; import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.dao.DeviceChannelMapper; +import com.genersoft.iot.vmp.gb28181.dao.DeviceMapper; import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent; import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; -import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; -import com.genersoft.iot.vmp.storager.dao.DeviceMapper; import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo; import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.JsonUtil; import com.genersoft.iot.vmp.utils.SystemInfoUtils; import com.genersoft.iot.vmp.utils.redis.RedisUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; @@ -33,10 +29,10 @@ import java.time.Duration; import java.util.*; @SuppressWarnings("rawtypes") +@Slf4j @Component public class RedisCatchStorageImpl implements IRedisCatchStorage { - private final Logger logger = LoggerFactory.getLogger(RedisCatchStorageImpl.class); @Autowired private DeviceChannelMapper deviceChannelMapper; @@ -53,6 +49,11 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Autowired private StringRedisTemplate stringRedisTemplate; + @Override + public List queryAllSendRTPServer() { + return Collections.emptyList(); + } + @Override public Long getCSEQ() { String key = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetting.getServerId(); @@ -94,14 +95,14 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { } @Override - public void updatePlatformCatchInfo(ParentPlatformCatch parentPlatformCatch) { + public void updatePlatformCatchInfo(PlatformCatch parentPlatformCatch) { String key = VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + parentPlatformCatch.getId(); redisTemplate.opsForValue().set(key, parentPlatformCatch); } @Override - public ParentPlatformCatch queryPlatformCatchInfo(String platformGbId) { - return (ParentPlatformCatch)redisTemplate.opsForValue().get(VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + platformGbId); + public PlatformCatch queryPlatformCatchInfo(String platformGbId) { + return (PlatformCatch)redisTemplate.opsForValue().get(VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + platformGbId); } @Override @@ -138,181 +139,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { redisTemplate.delete(VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + callId); } - @Override - public void updateSendRTPSever(SendRtpItem sendRtpItem) { - redisTemplate.opsForValue().set(sendRtpItem.getRedisKey(), sendRtpItem); - } - @Override - public List querySendRTPServer(String platformGbId, String channelId, String streamId) { - String scanKey = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX - + userSetting.getServerId() + "_*_" - + platformGbId + "_" - + channelId + "_" - + streamId + "_" - + "*"; - List result = new ArrayList<>(); - List scan = RedisUtil.scan(redisTemplate, scanKey); - if (!scan.isEmpty()) { - for (Object o : scan) { - String key = (String) o; - result.add(JsonUtil.redisJsonToObject(redisTemplate, key, SendRtpItem.class)); - } - } - return result; - } - - @Override - public SendRtpItem querySendRTPServer(String platformGbId, String channelId, String streamId, String callId) { - if (platformGbId == null) { - platformGbId = "*"; - } - if (channelId == null) { - channelId = "*"; - } - if (streamId == null) { - streamId = "*"; - } - if (callId == null) { - callId = "*"; - } - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX - + "*_*_" - + platformGbId + "_" - + channelId + "_" - + streamId + "_" - + callId; - List scan = RedisUtil.scan(redisTemplate, key); - if (scan.size() > 0) { - return (SendRtpItem)redisTemplate.opsForValue().get(scan.get(0)); - }else { - return null; - } - } - - @Override - public List querySendRTPServerByChannelId(String channelId) { - if (channelId == null) { - return null; - } - String platformGbId = "*"; - String callId = "*"; - String streamId = "*"; - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX - + userSetting.getServerId() + "_*_" - + platformGbId + "_" - + channelId + "_" - + streamId + "_" - + callId; - List scan = RedisUtil.scan(redisTemplate, key); - List result = new ArrayList<>(); - for (Object o : scan) { - result.add((SendRtpItem) redisTemplate.opsForValue().get(o)); - } - return result; - } - - @Override - public List querySendRTPServerByStream(String stream) { - if (stream == null) { - return null; - } - String platformGbId = "*"; - String callId = "*"; - String channelId = "*"; - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX - + userSetting.getServerId() + "_*_" - + platformGbId + "_" - + channelId + "_" - + stream + "_" - + callId; - List scan = RedisUtil.scan(redisTemplate, key); - List result = new ArrayList<>(); - for (Object o : scan) { - result.add((SendRtpItem) redisTemplate.opsForValue().get(o)); - } - return result; - } - - @Override - public List querySendRTPServer(String platformGbId) { - if (platformGbId == null) { - platformGbId = "*"; - } - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX - + userSetting.getServerId() + "_*_" - + platformGbId + "_*" + "_*" + "_*"; - List queryResult = RedisUtil.scan(redisTemplate, key); - List result= new ArrayList<>(); - - for (Object o : queryResult) { - String keyItem = (String) o; - result.add((SendRtpItem) redisTemplate.opsForValue().get(keyItem)); - } - - return result; - } - - /** - * 删除RTP推送信息缓存 - */ - @Override - public void deleteSendRTPServer(String platformGbId, String channelId, String callId, String streamId) { - if (streamId == null) { - streamId = "*"; - } - if (callId == null) { - callId = "*"; - } - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX - + userSetting.getServerId() + "_*_" - + platformGbId + "_" - + channelId + "_" - + streamId + "_" - + callId; - List scan = RedisUtil.scan(redisTemplate, key); - if (scan.size() > 0) { - for (Object keyStr : scan) { - logger.info("[删除 redis的SendRTP]: {}", keyStr.toString()); - redisTemplate.delete(keyStr); - } - } - } - - /** - * 删除RTP推送信息缓存 - */ - @Override - public void deleteSendRTPServer(SendRtpItem sendRtpItem) { - deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(),sendRtpItem.getCallId(), sendRtpItem.getStream()); - } - - @Override - public List queryAllSendRTPServer() { - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX - + userSetting.getServerId() + "_*"; - List queryResult = RedisUtil.scan(redisTemplate, key); - List result= new ArrayList<>(); - - for (Object o : queryResult) { - String keyItem = (String) o; - result.add((SendRtpItem) redisTemplate.opsForValue().get(keyItem)); - } - - return result; - } - - /** - * 查询某个通道是否存在上级点播(RTP推送) - */ - @Override - public boolean isChannelSendingRTP(String channelId) { - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX - + userSetting.getServerId() + "_*_*_" - + channelId + "*_" + "*_"; - List RtpStreams = RedisUtil.scan(redisTemplate, key); - return RtpStreams.size() > 0; - } @Override public void updateWVPInfo(JSONObject jsonObject, int time) { @@ -324,7 +151,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Override public void sendStreamChangeMsg(String type, JSONObject jsonObject) { String key = VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + type; - logger.info("[redis 流变化事件] 发送 {}: {}", key, jsonObject.toString()); + log.info("[redis 流变化事件] 发送 {}: {}", key, jsonObject.toString()); redisTemplate.convertAndSend(key, jsonObject); } @@ -426,8 +253,8 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { } @Override - public GPSMsgInfo getGpsMsgInfo(String gbId) { - String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + gbId; + public GPSMsgInfo getGpsMsgInfo(String channelId) { + String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + channelId; return JsonUtil.redisJsonToObject(redisTemplate, key, GPSMsgInfo.class); } @@ -583,14 +410,14 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Override public void sendMobilePositionMsg(JSONObject jsonObject) { String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_MOBILE_POSITION; - logger.debug("[redis发送通知] 发送 移动位置 {}: {}", key, jsonObject.toString()); + log.debug("[redis发送通知] 发送 移动位置 {}: {}", key, jsonObject.toString()); redisTemplate.convertAndSend(key, jsonObject); } @Override public void sendStreamPushRequestedMsg(MessageForPushChannel msg) { String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED; - logger.info("[redis发送通知] 发送 推流被请求 {}: {}/{}", key, msg.getApp(), msg.getStream()); + log.info("[redis发送通知] 发送 推流被请求 {}: {}/{}", key, msg.getApp(), msg.getStream()); redisTemplate.convertAndSend(key, JSON.toJSON(msg)); } @@ -598,7 +425,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { public void sendAlarmMsg(AlarmChannelMessage msg) { // 此消息用于对接第三方服务下级来的消息内容 String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM; - logger.info("[redis发送通知] 发送 报警{}: {}", key, JSON.toJSON(msg)); + log.info("[redis发送通知] 发送 报警{}: {}", key, JSON.toJSON(msg)); redisTemplate.convertAndSend(key, JSON.toJSON(msg)); } @@ -611,7 +438,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Override public void sendStreamPushRequestedMsgForStatus() { String key = VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED; - logger.info("[redis通知] 发送 获取所有推流设备的状态"); + log.info("[redis通知] 发送 获取所有推流设备的状态"); JSONObject jsonObject = new JSONObject(); jsonObject.put(key, key); redisTemplate.convertAndSend(key, jsonObject); @@ -631,7 +458,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Override public int getGbSendCount(String id) { - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + String key = VideoManagerConstants.SEND_RTP_INFO + userSetting.getServerId() + "_*_" + id + "_*"; return RedisUtil.scan(redisTemplate, key).size(); } @@ -645,7 +472,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { msg.append(":").append(channelId); } msg.append(" ").append(online? "ON":"OFF"); - logger.info("[redis通知] 推送设备/通道状态-> {} ", msg); + log.info("[redis通知] 推送设备/通道状态-> {} ", msg); // 使用 RedisTemplate 发送字符串消息会导致发送的消息多带了双引号 stringRedisTemplate.convertAndSend(key, msg.toString()); } @@ -661,89 +488,88 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { msg.append(":").append(channelId); } msg.append(" ").append(add? "ADD":"DELETE"); - logger.info("[redis通知] 推送通道-> {}", msg); + log.info("[redis通知] 推送通道-> {}", msg); // 使用 RedisTemplate 发送字符串消息会导致发送的消息多带了双引号 stringRedisTemplate.convertAndSend(key, msg.toString()); } @Override - public void sendPlatformStartPlayMsg(SendRtpItem sendRtpItem, ParentPlatform platform) { + public void sendPlatformStartPlayMsg(SendRtpInfo sendRtpItem, DeviceChannel channel, Platform platform) { if (sendRtpItem.getPlayType() == InviteStreamType.PUSH && platform != null) { MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, sendRtpItem.getApp(), sendRtpItem.getStream(), - sendRtpItem.getChannelId(), platform.getServerGBId(), platform.getName(), userSetting.getServerId(), + channel.getDeviceId(), platform.getServerGBId(), platform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId()); messageForPushChannel.setPlatFormIndex(platform.getId()); String key = VideoManagerConstants.VM_MSG_STREAM_START_PLAY_NOTIFY; - logger.info("[redis发送通知] 发送 推流被上级平台观看 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), platform.getServerGBId()); + log.info("[redis发送通知] 发送 推流被上级平台观看 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), platform.getServerGBId()); redisTemplate.convertAndSend(key, JSON.toJSON(messageForPushChannel)); } } @Override - public void sendPlatformStopPlayMsg(SendRtpItem sendRtpItem, ParentPlatform platform) { + public void sendPlatformStopPlayMsg(SendRtpInfo sendRtpItem, Platform platform, CommonGBChannel channel) { MessageForPushChannel msg = MessageForPushChannel.getInstance(0, - sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(), - sendRtpItem.getPlatformId(), platform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId()); + sendRtpItem.getApp(), sendRtpItem.getStream(), channel.getGbDeviceId(), + sendRtpItem.getTargetId(), platform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId()); msg.setPlatFormIndex(platform.getId()); String key = VideoManagerConstants.VM_MSG_STREAM_STOP_PLAY_NOTIFY; - logger.info("[redis发送通知] 发送 上级平台停止观看 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), platform.getServerGBId()); + log.info("[redis发送通知] 发送 上级平台停止观看 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), platform.getServerGBId()); redisTemplate.convertAndSend(key, JSON.toJSON(msg)); } @Override - public void addPushListItem(String app, String stream, MediaArrivalEvent event) { + public void addPushListItem(String app, String stream, MediaInfo mediaInfo) { String key = VideoManagerConstants.PUSH_STREAM_LIST + app + "_" + stream; - redisTemplate.opsForValue().set(key, event.getHookParam()); + redisTemplate.opsForValue().set(key, mediaInfo); } @Override - public OnStreamChangedHookParam getPushListItem(String app, String stream) { + public MediaInfo getPushListItem(String app, String stream) { String key = VideoManagerConstants.PUSH_STREAM_LIST + app + "_" + stream; - return (OnStreamChangedHookParam)redisTemplate.opsForValue().get(key); + return (MediaInfo)redisTemplate.opsForValue().get(key); } @Override public void removePushListItem(String app, String stream, String mediaServerId) { String key = VideoManagerConstants.PUSH_STREAM_LIST + app + "_" + stream; - StreamPushItem param = (StreamPushItem)redisTemplate.opsForValue().get(key); - if (param != null && param.getMediaServerId().equalsIgnoreCase(mediaServerId)) { + MediaInfo param = (MediaInfo)redisTemplate.opsForValue().get(key); + if (param != null && param.getMediaServer().getId().equalsIgnoreCase(mediaServerId)) { redisTemplate.delete(key); } - } @Override public void sendPushStreamClose(MessageForPushChannel msg) { String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE_REQUESTED; - logger.info("[redis发送通知] 发送 停止向上级推流 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId()); + log.info("[redis发送通知] 发送 停止向上级推流 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId()); redisTemplate.convertAndSend(key, JSON.toJSON(msg)); } @Override - public void addWaiteSendRtpItem(SendRtpItem sendRtpItem, int platformPlayTimeout) { + public void addWaiteSendRtpItem(SendRtpInfo sendRtpItem, int platformPlayTimeout) { String key = VideoManagerConstants.WAITE_SEND_PUSH_STREAM + sendRtpItem.getApp() + "_" + sendRtpItem.getStream(); redisTemplate.opsForValue().set(key, sendRtpItem); } @Override - public SendRtpItem getWaiteSendRtpItem(String app, String stream) { + public SendRtpInfo getWaiteSendRtpItem(String app, String stream) { String key = VideoManagerConstants.WAITE_SEND_PUSH_STREAM + app + "_" + stream; - return JsonUtil.redisJsonToObject(redisTemplate, key, SendRtpItem.class); + return JsonUtil.redisJsonToObject(redisTemplate, key, SendRtpInfo.class); } @Override - public void sendStartSendRtp(SendRtpItem sendRtpItem) { + public void sendStartSendRtp(SendRtpInfo sendRtpItem) { String key = VideoManagerConstants.START_SEND_PUSH_STREAM + sendRtpItem.getApp() + "_" + sendRtpItem.getStream(); - logger.info("[redis发送通知] 通知其他WVP推流 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getPlatformId()); + log.info("[redis发送通知] 通知其他WVP推流 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getTargetId()); redisTemplate.convertAndSend(key, JSON.toJSON(sendRtpItem)); } @Override - public void sendPushStreamOnline(SendRtpItem sendRtpItem) { + public void sendPushStreamOnline(SendRtpInfo sendRtpItem) { String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE_REQUESTED; - logger.info("[redis发送通知] 流上线 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getPlatformId()); + log.info("[redis发送通知] 流上线 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getTargetId()); redisTemplate.convertAndSend(key, JSON.toJSON(sendRtpItem)); } } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java deleted file mode 100755 index b1eef31e4..000000000 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java +++ /dev/null @@ -1,1099 +0,0 @@ -package com.genersoft.iot.vmp.storager.impl; - -import com.baomidou.dynamic.datasource.annotation.DS; -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.conf.UserSetting; -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; -import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; -import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; -import com.genersoft.iot.vmp.storager.dao.*; -import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo; -import com.genersoft.iot.vmp.utils.DateUtil; -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; -import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; -import com.github.pagehelper.PageHelper; -import com.github.pagehelper.PageInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.datasource.DataSourceTransactionManager; -import org.springframework.stereotype.Component; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.CollectionUtils; -import org.springframework.util.ObjectUtils; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 视频设备数据存储-jdbc实现 - * swwheihei - * 2020年5月6日 下午2:31:42 - */ -@SuppressWarnings("rawtypes") -@Component -@DS("master") -public class VideoManagerStorageImpl implements IVideoManagerStorage { - - private final Logger logger = LoggerFactory.getLogger(VideoManagerStorageImpl.class); - - @Autowired - EventPublisher eventPublisher; - - @Autowired - SipConfig sipConfig; - - - @Autowired - TransactionDefinition transactionDefinition; - - @Autowired - DataSourceTransactionManager dataSourceTransactionManager; - - @Autowired - private DeviceMapper deviceMapper; - - @Autowired - private DeviceChannelMapper deviceChannelMapper; - - @Autowired - private DeviceMobilePositionMapper deviceMobilePositionMapper; - - @Autowired - private ParentPlatformMapper platformMapper; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - private PlatformChannelMapper platformChannelMapper; - - @Autowired - private PlatformCatalogMapper platformCatalogMapper; - - @Autowired - private StreamProxyMapper streamProxyMapper; - - @Autowired - private StreamPushMapper streamPushMapper; - - @Autowired - private GbStreamMapper gbStreamMapper; - - @Autowired - private UserSetting userSetting; - - @Autowired - private PlatformCatalogMapper catalogMapper; - - @Autowired - private PlatformGbStreamMapper platformGbStreamMapper; - - /** - * 根据设备ID判断设备是否存在 - * - * @param deviceId 设备ID - * @return true:存在 false:不存在 - */ - @Override - public boolean exists(String deviceId) { - return deviceMapper.getDeviceByDeviceId(deviceId) != null; - } - - @Override - public boolean resetChannels(String deviceId, List deviceChannelList) { - if (CollectionUtils.isEmpty(deviceChannelList)) { - return false; - } - List allChannels = deviceChannelMapper.queryAllChannels(deviceId); - Map allChannelMap = new ConcurrentHashMap<>(); - if (allChannels.size() > 0) { - for (DeviceChannel deviceChannel : allChannels) { - allChannelMap.put(deviceChannel.getChannelId(), deviceChannel); - } - } - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); - // 数据去重 - List channels = new ArrayList<>(); - - List updateChannels = new ArrayList<>(); - List addChannels = new ArrayList<>(); - List deleteChannels = new ArrayList<>(); - StringBuilder stringBuilder = new StringBuilder(); - Map subContMap = new HashMap<>(); - - // 数据去重 - Set gbIdSet = new HashSet<>(); - for (DeviceChannel deviceChannel : deviceChannelList) { - if (gbIdSet.contains(deviceChannel.getChannelId())) { - stringBuilder.append(deviceChannel.getChannelId()).append(","); - continue; - } - gbIdSet.add(deviceChannel.getChannelId()); - if (allChannelMap.containsKey(deviceChannel.getChannelId())) { - deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId()); - deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).getHasAudio()); - if (allChannelMap.get(deviceChannel.getChannelId()).isStatus() !=deviceChannel.isStatus()){ - List strings = platformChannelMapper.queryParentPlatformByChannelId(deviceChannel.getChannelId()); - if (!CollectionUtils.isEmpty(strings)){ - strings.forEach(platformId->{ - eventPublisher.catalogEventPublish(platformId, deviceChannel, deviceChannel.isStatus()?CatalogEvent.ON:CatalogEvent.OFF); - }); - } - - } - deviceChannel.setUpdateTime(DateUtil.getNow()); - updateChannels.add(deviceChannel); - }else { - deviceChannel.setCreateTime(DateUtil.getNow()); - deviceChannel.setUpdateTime(DateUtil.getNow()); - addChannels.add(deviceChannel); - } - allChannelMap.remove(deviceChannel.getChannelId()); - channels.add(deviceChannel); - if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) { - if (subContMap.get(deviceChannel.getParentId()) == null) { - subContMap.put(deviceChannel.getParentId(), 1); - }else { - Integer count = subContMap.get(deviceChannel.getParentId()); - subContMap.put(deviceChannel.getParentId(), count++); - } - } - } - deleteChannels.addAll(allChannelMap.values()); - if (!channels.isEmpty()) { - for (DeviceChannel channel : channels) { - if (subContMap.get(channel.getChannelId()) != null){ - Integer count = subContMap.get(channel.getChannelId()); - if (count > 0) { - channel.setSubCount(count); - channel.setParental(1); - } - } - } - } - - if (stringBuilder.length() > 0) { - logger.info("[目录查询]收到的数据存在重复: {}" , stringBuilder); - } - if(CollectionUtils.isEmpty(channels)){ - logger.info("通道重设,数据为空={}" , deviceChannelList); - return false; - } - try { - int limitCount = 50; - boolean result = false; - if (!result && !addChannels.isEmpty()) { - if (addChannels.size() > limitCount) { - for (int i = 0; i < addChannels.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > addChannels.size()) { - toIndex = addChannels.size(); - } - result = result || deviceChannelMapper.batchAdd(addChannels.subList(i, toIndex)) < 0; - } - }else { - result = result || deviceChannelMapper.batchAdd(addChannels) < 0; - } - } - if (!result && !updateChannels.isEmpty()) { - if (updateChannels.size() > limitCount) { - for (int i = 0; i < updateChannels.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > updateChannels.size()) { - toIndex = updateChannels.size(); - } - result = result || deviceChannelMapper.batchUpdate(updateChannels.subList(i, toIndex)) < 0; - } - }else { - result = result || deviceChannelMapper.batchUpdate(updateChannels) < 0; - } - } - if (!result && !deleteChannels.isEmpty()) { - System.out.println("删除: " + deleteChannels.size()); - if (deleteChannels.size() > limitCount) { - for (int i = 0; i < deleteChannels.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > deleteChannels.size()) { - toIndex = deleteChannels.size(); - } - result = result || deviceChannelMapper.batchDel(deleteChannels.subList(i, toIndex)) < 0; - } - }else { - result = result || deviceChannelMapper.batchDel(deleteChannels) < 0; - } - } - - if (result) { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - } - dataSourceTransactionManager.commit(transactionStatus); //手动提交 - return true; - }catch (Exception e) { - logger.error("未处理的异常 ", e); - dataSourceTransactionManager.rollback(transactionStatus); - return false; - } - - } - - - @Override - public boolean updateChannels(String deviceId, List deviceChannelList) { - if (CollectionUtils.isEmpty(deviceChannelList)) { - return false; - } - List allChannels = deviceChannelMapper.queryAllChannels(deviceId); - Map allChannelMap = new ConcurrentHashMap<>(); - if (allChannels.size() > 0) { - for (DeviceChannel deviceChannel : allChannels) { - allChannelMap.put(deviceChannel.getChannelId(), deviceChannel); - } - } - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); - // 数据去重 - List channels = new ArrayList<>(); - - List updateChannels = new ArrayList<>(); - List addChannels = new ArrayList<>(); - StringBuilder stringBuilder = new StringBuilder(); - Map subContMap = new HashMap<>(); - if (deviceChannelList.size() > 0) { - // 数据去重 - Set gbIdSet = new HashSet<>(); - for (DeviceChannel deviceChannel : deviceChannelList) { - if (!gbIdSet.contains(deviceChannel.getChannelId())) { - gbIdSet.add(deviceChannel.getChannelId()); - deviceChannel.setUpdateTime(DateUtil.getNow()); - if (allChannelMap.containsKey(deviceChannel.getChannelId())) { - deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId()); - deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).getHasAudio()); - if (allChannelMap.get(deviceChannel.getChannelId()).isStatus() !=deviceChannel.isStatus()){ - List strings = platformChannelMapper.queryParentPlatformByChannelId(deviceChannel.getChannelId()); - if (!CollectionUtils.isEmpty(strings)){ - strings.forEach(platformId->{ - eventPublisher.catalogEventPublish(platformId, deviceChannel, deviceChannel.isStatus()?CatalogEvent.ON:CatalogEvent.OFF); - }); - } - } - updateChannels.add(deviceChannel); - }else { - deviceChannel.setCreateTime(DateUtil.getNow()); - addChannels.add(deviceChannel); - } - channels.add(deviceChannel); - if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) { - if (subContMap.get(deviceChannel.getParentId()) == null) { - subContMap.put(deviceChannel.getParentId(), 1); - }else { - Integer count = subContMap.get(deviceChannel.getParentId()); - subContMap.put(deviceChannel.getParentId(), count++); - } - } - }else { - stringBuilder.append(deviceChannel.getChannelId()).append(","); - } - } - if (channels.size() > 0) { - for (DeviceChannel channel : channels) { - if (subContMap.get(channel.getChannelId()) != null){ - channel.setSubCount(subContMap.get(channel.getChannelId())); - } - } - } - - } - if (stringBuilder.length() > 0) { - logger.info("[目录查询]收到的数据存在重复: {}" , stringBuilder); - } - if(CollectionUtils.isEmpty(channels)){ - logger.info("通道重设,数据为空={}" , deviceChannelList); - return false; - } - try { - int limitCount = 50; - boolean result = false; - if (addChannels.size() > 0) { - if (addChannels.size() > limitCount) { - for (int i = 0; i < addChannels.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > addChannels.size()) { - toIndex = addChannels.size(); - } - result = result || deviceChannelMapper.batchAdd(addChannels.subList(i, toIndex)) < 0; - } - }else { - result = result || deviceChannelMapper.batchAdd(addChannels) < 0; - } - } - if (updateChannels.size() > 0) { - if (updateChannels.size() > limitCount) { - for (int i = 0; i < updateChannels.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > updateChannels.size()) { - toIndex = updateChannels.size(); - } - result = result || deviceChannelMapper.batchUpdate(updateChannels.subList(i, toIndex)) < 0; - } - }else { - result = result || deviceChannelMapper.batchUpdate(updateChannels) < 0; - } - } - - if (result) { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - } - dataSourceTransactionManager.commit(transactionStatus); //手动提交 - return true; - }catch (Exception e) { - logger.error("未处理的异常 ", e); - dataSourceTransactionManager.rollback(transactionStatus); - return false; - } - } - - @Override - public void deviceChannelOnline(String deviceId, String channelId) { - deviceChannelMapper.online(deviceId, channelId); - } - - @Override - public void deviceChannelOffline(String deviceId, String channelId) { - deviceChannelMapper.offline(deviceId, channelId); - } - - @Override - public void startPlay(String deviceId, String channelId, String streamId) { - deviceChannelMapper.startPlay(deviceId, channelId, streamId); - } - - @Override - public void stopPlay(String deviceId, String channelId) { - deviceChannelMapper.stopPlay(deviceId, channelId); - } - - /** - * 获取设备 - * - * @param deviceId 设备ID - * @return Device 设备对象 - */ - @Override - public Device queryVideoDevice(String deviceId) { - return deviceMapper.getDeviceByDeviceId(deviceId); - } - - @Override - public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count) { - // 获取到所有正在播放的流 - PageHelper.startPage(page, count); - List all; - if (catalogUnderDevice != null && catalogUnderDevice) { - all = deviceChannelMapper.queryChannels(deviceId, deviceId, query, hasSubChannel, online,null); - // 海康设备的parentId是SIP id - List deviceChannels = deviceChannelMapper.queryChannels(deviceId, sipConfig.getId(), query, hasSubChannel, online,null); - all.addAll(deviceChannels); - }else { - all = deviceChannelMapper.queryChannels(deviceId, null, query, hasSubChannel, online,null); - } - return new PageInfo<>(all); - } - - @Override - public List queryChannelsByDeviceIdWithStartAndLimit(String deviceId, List channelIds, String query, Boolean hasSubChannel, Boolean online, int start, int limit) { - return deviceChannelMapper.queryChannelsByDeviceIdWithStartAndLimit(deviceId, channelIds, null, query, hasSubChannel, online, start, limit); - } - - - @Override - public List queryChannelsByDeviceId(String deviceId,Boolean online,List channelIds) { - return deviceChannelMapper.queryChannels(deviceId, null,null, null, online,channelIds); - } - - @Override - public List queryChannelsByDeviceId(String deviceId, List channelIds, Boolean online) { - return deviceChannelMapper.queryChannelsWithDeviceInfo(deviceId, null,null, null, online,channelIds); - } - - @Override - public PageInfo querySubChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, int page, int count) { - PageHelper.startPage(page, count); - List all = deviceChannelMapper.queryChannels(deviceId, parentChannelId, query, hasSubChannel, online,null); - return new PageInfo<>(all); - } - - @Override - public DeviceChannel queryChannel(String deviceId, String channelId) { - return deviceChannelMapper.queryChannel(deviceId, channelId); - } - - - @Override - public int delChannel(String deviceId, String channelId) { - return deviceChannelMapper.del(deviceId, channelId); - } - - /** - * 获取多个设备 - * - * @param page 当前页数 - * @param count 每页数量 - * @return PageInfo 分页设备对象数组 - */ - @Override - public PageInfo queryVideoDeviceList(int page, int count,Boolean online) { - PageHelper.startPage(page, count); - List all = deviceMapper.getDevices(online); - return new PageInfo<>(all); - } - - /** - * 获取多个设备 - * - * @return List 设备对象数组 - */ - @Override - public List queryVideoDeviceList(Boolean online) { - - List deviceList = deviceMapper.getDevices(online); - return deviceList; - } - - /** - * 清空通道 - * @param deviceId - */ - @Override - public void cleanChannelsForDevice(String deviceId) { - deviceChannelMapper.cleanChannelsByDeviceId(deviceId); - } - - /** - * 添加Mobile Position设备移动位置 - * @param mobilePosition - */ - @Override - public synchronized boolean insertMobilePosition(MobilePosition mobilePosition) { - return deviceMobilePositionMapper.insertNewPosition(mobilePosition) > 0; - } - - /** - * 查询移动位置轨迹 - * @param deviceId - * @param startTime - * @param endTime - */ - @Override - public synchronized List queryMobilePositions(String deviceId, String channelId, String startTime, String endTime) { - return deviceMobilePositionMapper.queryPositionByDeviceIdAndTime(deviceId, channelId, startTime, endTime); - } - - @Override - public boolean addParentPlatform(ParentPlatform parentPlatform) { - if (parentPlatform.getCatalogId() == null) { - parentPlatform.setCatalogId(parentPlatform.getServerGBId()); - } - int result = platformMapper.addParentPlatform(parentPlatform); - return result > 0; - } - - @Override - public boolean updateParentPlatform(ParentPlatform parentPlatform) { - int result = 0; - if (parentPlatform.getCatalogGroup() == 0) { - parentPlatform.setCatalogGroup(1); - } - if (parentPlatform.getAdministrativeDivision() == null) { - parentPlatform.setAdministrativeDivision(parentPlatform.getAdministrativeDivision()); - } - ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); // .getDeviceGBId()); - if (parentPlatform.getId() == null ) { - if (parentPlatform.getCatalogId() == null) { - parentPlatform.setCatalogId(parentPlatform.getServerGBId()); - } - result = platformMapper.addParentPlatform(parentPlatform); - if (parentPlatformCatch == null) { - parentPlatformCatch = new ParentPlatformCatch(); - parentPlatformCatch.setParentPlatform(parentPlatform); - parentPlatformCatch.setId(parentPlatform.getServerGBId()); - } - }else { - if (parentPlatformCatch == null) { // serverGBId 已变化 - ParentPlatform parentPlatById = platformMapper.getParentPlatById(parentPlatform.getId()); - // 使用旧的查出缓存ID - parentPlatformCatch = new ParentPlatformCatch(); - parentPlatformCatch.setId(parentPlatform.getServerGBId()); - redisCatchStorage.delPlatformCatchInfo(parentPlatById.getServerGBId()); - } - - result = platformMapper.updateParentPlatform(parentPlatform); - } - // 更新缓存 - parentPlatformCatch.setParentPlatform(parentPlatform); - redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); - - return result > 0; - } - - @Transactional - @Override - public boolean deleteParentPlatform(ParentPlatform parentPlatform) { - int result = platformMapper.delParentPlatform(parentPlatform); - // 删除关联的通道 - platformChannelMapper.cleanChannelForGB(parentPlatform.getServerGBId()); - return result > 0; - } - - @Override - public ParentPlatform queryParentPlatByServerGBId(String platformGbId) { - return platformMapper.getParentPlatByServerGBId(platformGbId); - } - - @Override - public List queryEnableParentPlatformList(boolean enable) { - return platformMapper.getEnableParentPlatformList(enable); - } - - @Override - public List queryEnablePlatformListWithAsMessageChannel() { - return platformMapper.queryEnablePlatformListWithAsMessageChannel(); - } - - @Override - public List queryDeviceWithAsMessageChannel() { - return deviceMapper.queryDeviceWithAsMessageChannel(); - } - - @Override - public void outlineForAllParentPlatform() { - platformMapper.outlineForAllParentPlatform(); - } - - - @Override - public PageInfo queryAllChannelList(int page, int count, String query, Boolean online, - Boolean channelType, String platformId, String catalogId) { - PageHelper.startPage(page, count); - List all = deviceChannelMapper.queryChannelListInAll(query, online, channelType, platformId, catalogId); - return new PageInfo<>(all); - } - - @Override - public List queryChannelListInParentPlatform(String platformId) { - - return deviceChannelMapper.queryChannelByPlatformId(platformId); - } - - - @Override - public int delChannelForGB(String platformId, List channelReduces) { - - int result = platformChannelMapper.delChannelForGB(platformId, channelReduces); - List deviceChannelList = new ArrayList<>(); - for (ChannelReduce channelReduce : channelReduces) { - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setChannelId(channelReduce.getChannelId()); - deviceChannelList.add(deviceChannel); - } - eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL); - return result; - } - - @Override - public DeviceChannel queryChannelInParentPlatform(String platformId, String channelId) { - List channels = platformChannelMapper.queryChannelInParentPlatform(platformId, channelId); - if (channels.size() > 1) { - // 出现长度大于0的时候肯定是国标通道的ID重复了 - logger.warn("国标ID存在重复:{}", channelId); - } - if (channels.size() == 0) { - return null; - }else { - return channels.get(0); - } - } - - @Override - public List queryChannelInParentPlatformAndCatalog(String platformId, String catalogId) { - List catalogs = platformChannelMapper.queryChannelInParentPlatformAndCatalog(platformId, catalogId); - return catalogs; - } - - @Override - public List queryStreamInParentPlatformAndCatalog(String platformId, String catalogId) { - List catalogs = platformGbStreamMapper.queryChannelInParentPlatformAndCatalogForCatalog(platformId, catalogId); - return catalogs; - } - - @Override - public Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId) { - List devices = platformChannelMapper.queryVideoDeviceByPlatformIdAndChannelId(platformId, channelId); - if (devices.size() > 1) { - // 出现长度大于0的时候肯定是国标通道的ID重复了 - logger.warn("国标ID存在重复:{}", channelId); - } - if (devices.size() == 0) { - return null; - }else { - return devices.get(0); - } - - - } - - @Override - public Device queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId) { - List devices = platformChannelMapper.queryDeviceInfoByPlatformIdAndChannelId(platformId, channelId); - if (devices.size() > 1) { - // 出现长度大于0的时候肯定是国标通道的ID重复了 - logger.warn("国标ID存在重复:{}", channelId); - } - if (devices.size() == 0) { - return null; - }else { - return devices.get(0); - } - } - - /** - * 查询最新移动位置 - * @param deviceId - */ - @Override - public MobilePosition queryLatestPosition(String deviceId) { - return deviceMobilePositionMapper.queryLatestPositionByDevice(deviceId); - } - - /** - * 删除指定设备的所有移动位置 - * @param deviceId - */ - @Override - public int clearMobilePositionsByDeviceId(String deviceId) { - return deviceMobilePositionMapper.clearMobilePositionsByDeviceId(deviceId); - } - - - /** - * 移除代理流 - * @param app - * @param stream - * @return - */ - @Override - public int deleteStreamProxy(String app, String stream) { - return streamProxyMapper.del(app, stream); - } - - /** - * 根据是否启用获取代理流列表 - * @param enable - * @return - */ - @Override - public List getStreamProxyListForEnable(boolean enable) { - return streamProxyMapper.selectForEnable(enable); - } - - /** - * 分页查询代理流列表 - * @param page - * @param count - * @return - */ - @Override - public PageInfo queryStreamProxyList(Integer page, Integer count) { - PageHelper.startPage(page, count); - List all = streamProxyMapper.selectAll(); - return new PageInfo<>(all); - } - - /** - * 根据国标ID获取平台关联的直播流 - * @param platformId - * @param gbId - * @return - */ - @Override - public GbStream queryStreamInParentPlatform(String platformId, String gbId) { - return gbStreamMapper.queryStreamInPlatform(platformId, gbId); - } - - /** - * 获取平台关联的直播流 - * @param platformId - * @return - */ - @Override - public List queryGbStreamListInPlatform(String platformId) { - return gbStreamMapper.queryGbStreamListInPlatform(platformId, userSetting.isUsePushingAsStatus()); - } - - /** - * 按照是app和stream获取代理流 - * @param app - * @param stream - * @return - */ - @Override - public StreamProxyItem queryStreamProxy(String app, String stream){ - return streamProxyMapper.selectOne(app, stream); - } - - @Override - public int removeMedia(String app, String stream) { - return streamPushMapper.del(app, stream); - } - - @Override - public int mediaOffline(String app, String stream) { - GbStream gbStream = gbStreamMapper.selectOne(app, stream); - int result; - if ("proxy".equals(gbStream.getStreamType())) { - result = streamProxyMapper.updateStatus(app, stream, false); - }else { - result = streamPushMapper.updatePushStatus(app, stream, false); - } - return result; - } - - @Override - public int mediaOnline(String app, String stream) { - GbStream gbStream = gbStreamMapper.selectOne(app, stream); - int result; - if ("proxy".equals(gbStream.getStreamType())) { - result = streamProxyMapper.updateStatus(app, stream, true); - }else { - result = streamPushMapper.updatePushStatus(app, stream, true); - } - return result; - } - - @Override - public void updateParentPlatformStatus(String platformGbID, boolean online) { - platformMapper.updateParentPlatformStatus(platformGbID, online); - } - - @Override - public List getStreamProxyListForEnableInMediaServer(String id, boolean enable) { - return streamProxyMapper.selectForEnableInMediaServer(id, enable); - } - - - @Override - public Device queryVideoDeviceByChannelId( String channelId) { - Device result = null; - List channelList = deviceChannelMapper.queryChannelByChannelId(channelId); - if (channelList.size() == 1) { - result = deviceMapper.getDeviceByDeviceId(channelList.get(0).getDeviceId()); - } - return result; - } - - @Override - public StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId) { - return streamProxyMapper.selectOne(app, streamId); - } - - @Override - public List getChildrenCatalogByPlatform(String platformId, String parentId) { - return catalogMapper.selectByParentId(platformId, parentId); - } - - @Override - public int addCatalog(PlatformCatalog platformCatalog) { - ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformCatalog.getPlatformId()); - if (platform == null) { - return 0; - } - if (platformCatalog.getId().length() <= 8) { - platformCatalog.setCivilCode(platformCatalog.getParentId()); - }else { - if (platformCatalog.getId().length() != 20) { - return 0; - } - if (platformCatalog.getParentId() != null) { - switch (Integer.parseInt(platformCatalog.getId().substring(10, 13))){ - case 200: - case 215: - if (platformCatalog.getParentId().length() <= 8) { - platformCatalog.setCivilCode(platformCatalog.getParentId()); - }else { - PlatformCatalog catalog = catalogMapper.selectByPlatFormAndCatalogId(platformCatalog.getPlatformId(), platformCatalog.getParentId()); - if (catalog != null) { - platformCatalog.setCivilCode(catalog.getCivilCode()); - } - } - break; - case 216: - if (platformCatalog.getParentId().length() <= 8) { - platformCatalog.setCivilCode(platformCatalog.getParentId()); - }else { - PlatformCatalog catalog = catalogMapper.selectByPlatFormAndCatalogId(platformCatalog.getPlatformId(),platformCatalog.getParentId()); - if (catalog == null) { - logger.warn("[添加目录] 无法获取目录{}的CivilCode和BusinessGroupId", platformCatalog.getPlatformId()); - break; - } - platformCatalog.setCivilCode(catalog.getCivilCode()); - if (Integer.parseInt(platformCatalog.getParentId().substring(10, 13)) == 215) { - platformCatalog.setBusinessGroupId(platformCatalog.getParentId()); - }else { - if (Integer.parseInt(platformCatalog.getParentId().substring(10, 13)) == 216) { - platformCatalog.setBusinessGroupId(catalog.getBusinessGroupId()); - } - } - } - break; - default: - break; - } - } - } - int result = catalogMapper.add(platformCatalog); - if (result > 0) { - DeviceChannel deviceChannel = getDeviceChannelByCatalog(platformCatalog); - eventPublisher.catalogEventPublish(platformCatalog.getPlatformId(), deviceChannel, CatalogEvent.ADD); - } - return result; - } - - private PlatformCatalog getTopCatalog(String id, String platformId) { - PlatformCatalog catalog = catalogMapper.selectByPlatFormAndCatalogId(platformId, id); - if (catalog.getParentId().equals(platformId)) { - return catalog; - }else { - return getTopCatalog(catalog.getParentId(), platformId); - } - } - - @Override - public PlatformCatalog getCatalog(String platformId, String id) { - return catalogMapper.selectByPlatFormAndCatalogId(platformId, id); - } - - @Override - public int delCatalog(String platformId, String id) { - return delCatalogExecute(id, platformId); - } - private int delCatalogExecute(String id, String platformId) { - int delresult = catalogMapper.del(platformId, id); - DeviceChannel deviceChannelForCatalog = new DeviceChannel(); - if (delresult > 0){ - deviceChannelForCatalog.setChannelId(id); - eventPublisher.catalogEventPublish(platformId, deviceChannelForCatalog, CatalogEvent.DEL); - } - - List gbStreams = platformGbStreamMapper.queryChannelInParentPlatformAndCatalog(platformId, id); - if (gbStreams.size() > 0){ - List deviceChannelList = new ArrayList<>(); - for (GbStream gbStream : gbStreams) { - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setChannelId(gbStream.getGbId()); - deviceChannelList.add(deviceChannel); - } - eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL); - } - int delStreamresult = platformGbStreamMapper.delByPlatformAndCatalogId(platformId,id); - List platformCatalogs = platformChannelMapper.queryChannelInParentPlatformAndCatalog(platformId, id); - if (platformCatalogs.size() > 0){ - List deviceChannelList = new ArrayList<>(); - for (PlatformCatalog platformCatalog : platformCatalogs) { - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setChannelId(platformCatalog.getId()); - deviceChannelList.add(deviceChannel); - } - eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL); - } - int delChannelresult = platformChannelMapper.delByCatalogId(platformId, id); - // 查看是否存在子目录,如果存在一并删除 - List allChildCatalog = getAllChildCatalog(id, platformId); - if (!allChildCatalog.isEmpty()) { - int limitCount = 50; - if (allChildCatalog.size() > limitCount) { - for (int i = 0; i < allChildCatalog.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > allChildCatalog.size()) { - toIndex = allChildCatalog.size(); - } - delChannelresult += platformCatalogMapper.deleteAll(platformId, allChildCatalog.subList(i, toIndex)); - } - }else { - delChannelresult += platformCatalogMapper.deleteAll(platformId, allChildCatalog); - } - } - return delresult + delChannelresult + delStreamresult; - } - - private List getAllChildCatalog(String id, String platformId) { - List catalogList = platformCatalogMapper.queryCatalogFromParent(id, platformId); - List catalogListChild = new ArrayList<>(); - if (catalogList != null && !catalogList.isEmpty()) { - for (String childId : catalogList) { - List allChildCatalog = getAllChildCatalog(childId, platformId); - if (allChildCatalog != null && !allChildCatalog.isEmpty()) { - catalogListChild.addAll(allChildCatalog); - } - - } - } - if (!catalogListChild.isEmpty()) { - catalogList.addAll(catalogListChild); - } - return catalogList; - } - - - @Override - public int updateCatalog(PlatformCatalog platformCatalog) { - int result = catalogMapper.update(platformCatalog); - if (result > 0) { - DeviceChannel deviceChannel = getDeviceChannelByCatalog(platformCatalog); - eventPublisher.catalogEventPublish(platformCatalog.getPlatformId(), deviceChannel, CatalogEvent.UPDATE); - } - return result; - } - - @Override - public int setDefaultCatalog(String platformId, String catalogId) { - return platformMapper.setDefaultCatalog(platformId, catalogId, DateUtil.getNow()); - } - - @Override - public List queryCatalogInPlatform(String platformId) { - return catalogMapper.queryCatalogInPlatform(platformId); - } - - @Override - public int delRelation(PlatformCatalog platformCatalog) { - if (platformCatalog.getType() == 1) { - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setChannelId(platformCatalog.getId()); - eventPublisher.catalogEventPublish(platformCatalog.getPlatformId(), deviceChannel, CatalogEvent.DEL); - return platformChannelMapper.delByCatalogIdAndChannelIdAndPlatformId(platformCatalog); - }else if (platformCatalog.getType() == 2) { - List gbStreams = platformGbStreamMapper.queryChannelInParentPlatformAndCatalog(platformCatalog.getPlatformId(), platformCatalog.getParentId()); - for (GbStream gbStream : gbStreams) { - if (gbStream.getGbId().equals(platformCatalog.getId())) { - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setChannelId(gbStream.getGbId()); - eventPublisher.catalogEventPublish(platformCatalog.getPlatformId(), deviceChannel, CatalogEvent.DEL); - return platformGbStreamMapper.delByAppAndStream(gbStream.getApp(), gbStream.getStream()); - } - } - } - return 0; - } - - @Override - public int updateStreamGPS(List gpsMsgInfos) { - return gbStreamMapper.updateStreamGPS(gpsMsgInfos); - } - - - private DeviceChannel getDeviceChannelByCatalog(PlatformCatalog catalog) { - ParentPlatform platform = platformMapper.getParentPlatByServerGBId(catalog.getPlatformId()); - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setChannelId(catalog.getId()); - deviceChannel.setName(catalog.getName()); - deviceChannel.setDeviceId(platform.getDeviceGBId()); - deviceChannel.setManufacture("wvp-pro"); - deviceChannel.setStatus(true); - deviceChannel.setParental(1); - - deviceChannel.setRegisterWay(1); - deviceChannel.setParentId(catalog.getParentId()); - deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); - - deviceChannel.setModel("live"); - deviceChannel.setOwner("wvp-pro"); - deviceChannel.setSecrecy("0"); - return deviceChannel; - } - - @Override - public List queryOnlineChannelsByDeviceId(String deviceId) { - return deviceChannelMapper.queryOnlineChannelsByDeviceId(deviceId); - } - - @Override - public List queryPlatFormListForGBWithGBId(String channelId, List platforms) { - return platformChannelMapper.queryPlatFormListForGBWithGBId(channelId, platforms); - } - - @Override - public List queryPlatFormListForStreamWithGBId(String app, String stream, List platforms) { - if (platforms == null || platforms.size() == 0) { - return new ArrayList<>(); - } - return platformGbStreamMapper.queryPlatFormListForGBWithGBId(app, stream, platforms); - } - - @Override - public GbStream getGbStream(String app, String streamId) { - return gbStreamMapper.selectOne(app, streamId); - } - - @Override - public void delCatalogByPlatformId(String serverGBId) { - catalogMapper.delByPlatformId(serverGBId); - } - - @Override - public void delRelationByPlatformId(String serverGBId) { - platformGbStreamMapper.delByPlatformId(serverGBId); - platformChannelMapper.delByPlatformId(serverGBId); - } - - @Override - public PlatformCatalog queryDefaultCatalogInPlatform(String platformId) { - return catalogMapper.selectDefaultByPlatFormId(platformId); - } - - @Override - public List getChannelSource(String platformId, String gbId) { - return platformMapper.getChannelSource(platformId, gbId); - } - - @Override - public void updateChannelPosition(DeviceChannel deviceChannel) { - if (deviceChannel.getChannelId().equals(deviceChannel.getDeviceId())) { - deviceChannel.setChannelId(null); - } - if (deviceChannel.getGpsTime() == null) { - deviceChannel.setGpsTime(DateUtil.getNow()); - } - - deviceChannelMapper.updatePosition(deviceChannel); - } - - @Override - public void cleanContentForPlatform(String serverGBId) { -// List catalogList = catalogMapper.selectByPlatForm(serverGBId); -// if (catalogList.size() > 0) { -// int result = catalogMapper.delByPlatformId(serverGBId); -// if (result > 0) { -// List deviceChannels = new ArrayList<>(); -// for (PlatformCatalog catalog : catalogList) { -// deviceChannels.add(getDeviceChannelByCatalog(catalog)); -// } -// eventPublisher.catalogEventPublish(serverGBId, deviceChannels, CatalogEvent.DEL); -// } -// } - catalogMapper.delByPlatformId(serverGBId); - platformChannelMapper.delByPlatformId(serverGBId); - platformGbStreamMapper.delByPlatformId(serverGBId); - } - - @Override - public List queryChannelWithCatalog(String serverGBId) { - return deviceChannelMapper.queryChannelWithCatalog(serverGBId); - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/bean/StreamProxy.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/bean/StreamProxy.java new file mode 100755 index 000000000..cdb2470f6 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/bean/StreamProxy.java @@ -0,0 +1,78 @@ +package com.genersoft.iot.vmp.streamProxy.bean; + +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.util.ObjectUtils; + +/** + * @author lin + */ +@Data +@Schema(description = "拉流代理的信息") +@EqualsAndHashCode(callSuper = true) +public class StreamProxy extends CommonGBChannel { + + /** + * 数据库自增ID + */ + @Schema(description = "数据库自增ID") + private int id; + + @Schema(description = "类型,取值,default: 流媒体直接拉流(默认),ffmpeg: ffmpeg实现拉流") + private String type; + + @Schema(description = "应用名") + private String app; + + @Schema(description = "流ID") + private String stream; + + @Schema(description = "流媒体服务ID") + private String mediaServerId; + + @Schema(description = "拉流地址") + private String srcUrl; + + @Schema(description = "超时时间:秒") + private int timeout; + + @Schema(description = "ffmpeg模板KEY") + private String ffmpegCmdKey; + + @Schema(description = "rtsp拉流时,拉流方式,0:tcp,1:udp,2:组播") + private String rtspType; + + @Schema(description = "是否启用") + private boolean enable; + + @Schema(description = "是否启用音频") + private boolean enableAudio; + + @Schema(description = "是否启用MP4") + private boolean enableMp4; + + @Schema(description = "是否 无人观看时删除") + private boolean enableRemoveNoneReader; + + @Schema(description = "是否 无人观看时自动停用") + private boolean enableDisableNoneReader; + + @Schema(description = "拉流代理时zlm返回的key,用于停止拉流代理") + private String streamKey; + + @Schema(description = "拉流状态") + private Boolean pulling; + + public CommonGBChannel buildCommonGBChannel() { + if (ObjectUtils.isEmpty(this.getGbDeviceId())) { + return null; + } + if (ObjectUtils.isEmpty(this.getGbName())) { + this.setGbName( app+ "-" +stream); + } + this.setStreamProxyId(this.getId()); + return this; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/bean/StreamProxyParam.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/bean/StreamProxyParam.java new file mode 100755 index 000000000..632ca998a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/bean/StreamProxyParam.java @@ -0,0 +1,74 @@ +package com.genersoft.iot.vmp.streamProxy.bean; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * @author lin + */ +@Data +@Schema(description = "拉流代理的信息") +public class StreamProxyParam { + + @Schema(description = "类型,取值,default: 流媒体直接拉流(默认),ffmpeg: ffmpeg实现拉流") + private String type; + + @Schema(description = "应用名") + private String app; + + @Schema(description = "名称") + private String name; + + @Schema(description = "流ID") + private String stream; + + @Schema(description = "流媒体服务ID") + private String mediaServerId; + + @Schema(description = "拉流地址") + private String url; + + @Schema(description = "超时时间:秒") + private int timeoutMs; + + @Schema(description = "ffmpeg模板KEY") + private String ffmpegCmdKey; + + @Schema(description = "rtsp拉流时,拉流方式,0:tcp,1:udp,2:组播") + private String rtpType; + + @Schema(description = "是否启用") + private boolean enable; + + @Schema(description = "是否启用音频") + private boolean enableAudio; + + @Schema(description = "是否启用MP4") + private boolean enableMp4; + + @Schema(description = "是否 无人观看时删除") + private boolean enableRemoveNoneReader; + + @Schema(description = "是否 无人观看时自动停用") + private boolean enableDisableNoneReader; + + + public StreamProxy buildStreamProxy() { + StreamProxy streamProxy = new StreamProxy(); + streamProxy.setApp(app); + streamProxy.setStream(stream); + streamProxy.setMediaServerId(mediaServerId); + streamProxy.setSrcUrl(url); + streamProxy.setTimeout(timeoutMs/1000); + streamProxy.setRtspType(rtpType); + streamProxy.setEnable(enable); + streamProxy.setEnableAudio(enableAudio); + streamProxy.setEnableMp4(enableMp4); + streamProxy.setEnableRemoveNoneReader(enableRemoveNoneReader); + streamProxy.setEnableDisableNoneReader(enableDisableNoneReader); + streamProxy.setFfmpegCmdKey(ffmpegCmdKey); + streamProxy.setGbName(name); + return streamProxy; + + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/controller/StreamProxyController.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/controller/StreamProxyController.java new file mode 100755 index 000000000..861dea303 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/controller/StreamProxyController.java @@ -0,0 +1,204 @@ +package com.genersoft.iot.vmp.streamProxy.controller; + +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.conf.security.JwtUtils; +import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.media.service.IMediaServerService; +import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy; +import com.genersoft.iot.vmp.streamProxy.bean.StreamProxyParam; +import com.genersoft.iot.vmp.streamProxy.service.IStreamProxyPlayService; +import com.genersoft.iot.vmp.streamProxy.service.IStreamProxyService; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.StreamContent; +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; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@SuppressWarnings("rawtypes") +/** + * 拉流代理接口 + */ +@Tag(name = "拉流代理", description = "") +@Controller +@Slf4j +@RequestMapping(value = "/api/proxy") +public class StreamProxyController { + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private IStreamProxyService streamProxyService; + + @Autowired + private IStreamProxyPlayService streamProxyPlayService; + + + @Operation(summary = "分页查询流代理", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "page", description = "当前页") + @Parameter(name = "count", description = "每页查询数量") + @Parameter(name = "query", description = "查询内容") + @Parameter(name = "pulling", description = "是否正在拉流") + @Parameter(name = "mediaServerId", description = "流媒体ID") + @GetMapping(value = "/list") + @ResponseBody + public PageInfo list(@RequestParam(required = false)Integer page, + @RequestParam(required = false)Integer count, + @RequestParam(required = false)String query, + @RequestParam(required = false)Boolean pulling, + @RequestParam(required = false)String mediaServerId){ + + if (ObjectUtils.isEmpty(mediaServerId)) { + mediaServerId = null; + } + if (ObjectUtils.isEmpty(query)) { + query = null; + } + return streamProxyService.getAll(page, count, query, pulling, mediaServerId); + } + + @Operation(summary = "查询流代理", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "app", description = "应用名") + @Parameter(name = "stream", description = "流Id") + @GetMapping(value = "/one") + @ResponseBody + public StreamProxy one(String app, String stream){ + + return streamProxyService.getStreamProxyByAppAndStream(app, stream); + } + + @Operation(summary = "保存代理(已存在会覆盖)", security = @SecurityRequirement(name = JwtUtils.HEADER), parameters = { + @Parameter(name = "param", description = "代理参数", required = true), + }) + @PostMapping(value = "/save") + @ResponseBody + public StreamContent save(@RequestBody StreamProxyParam param){ + log.info("添加代理: " + JSONObject.toJSONString(param)); + if (ObjectUtils.isEmpty(param.getMediaServerId())) { + param.setMediaServerId("auto"); + } + if (ObjectUtils.isEmpty(param.getType())) { + param.setType("default"); + } + + StreamInfo streamInfo = streamProxyService.save(param); + if (param.isEnable()) { + if (streamInfo == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), ErrorCode.ERROR100.getMsg()); + }else { + return new StreamContent(streamInfo); + } + }else { + return null; + } + + } + + @Operation(summary = "新增代理", security = @SecurityRequirement(name = JwtUtils.HEADER), parameters = { + @Parameter(name = "param", description = "代理参数", required = true), + }) + @PostMapping(value = "/add") + @ResponseBody + public StreamProxy add(@RequestBody StreamProxy param){ + log.info("添加代理: " + JSONObject.toJSONString(param)); + if (ObjectUtils.isEmpty(param.getMediaServerId())) { + param.setMediaServerId(null); + } + if (ObjectUtils.isEmpty(param.getType())) { + param.setType("default"); + } + if (ObjectUtils.isEmpty(param.getGbId())) { + param.setGbDeviceId(null); + } + streamProxyService.add(param); + return param; + } + + @Operation(summary = "更新代理", security = @SecurityRequirement(name = JwtUtils.HEADER), parameters = { + @Parameter(name = "param", description = "代理参数", required = true), + }) + @PostMapping(value = "/update") + @ResponseBody + public StreamProxy update(@RequestBody StreamProxy param){ + log.info("更新代理: " + JSONObject.toJSONString(param)); + if (param.getId() == 0) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "缺少代理信息的ID"); + } + if (ObjectUtils.isEmpty(param.getGbId())) { + param.setGbDeviceId(null); + } + streamProxyService.update(param); + return param; + } + + @GetMapping(value = "/ffmpeg_cmd/list") + @ResponseBody + @Operation(summary = "获取ffmpeg.cmd模板", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "mediaServerId", description = "流媒体ID", required = true) + public Map getFFmpegCMDs(@RequestParam String mediaServerId){ + log.debug("获取节点[ {} ]ffmpeg.cmd模板", mediaServerId ); + + MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId); + if (mediaServerItem == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "流媒体: " + mediaServerId + "未找到"); + } + return streamProxyService.getFFmpegCMDs(mediaServerItem); + } + + @DeleteMapping(value = "/del") + @ResponseBody + @Operation(summary = "移除代理", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "app", description = "应用名", required = true) + @Parameter(name = "stream", description = "流id", required = true) + public void del(@RequestParam String app, @RequestParam String stream){ + log.info("移除代理: " + app + "/" + stream); + if (app == null || stream == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), app == null ?"app不能为null":"stream不能为null"); + }else { + streamProxyService.delteByAppAndStream(app, stream); + } + } + + @DeleteMapping(value = "/delete") + @ResponseBody + @Operation(summary = "移除代理", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "id", description = "代理ID", required = true) + public void delte(int id){ + log.info("移除代理: {}", id); + streamProxyService.delete(id); + } + + @GetMapping(value = "/start") + @ResponseBody + @Operation(summary = "启用代理", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "id", description = "代理Id", required = true) + public StreamContent start(int id){ + log.info("播放代理: {}", id); + StreamInfo streamInfo = streamProxyPlayService.start(id); + if (streamInfo == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), ErrorCode.ERROR100.getMsg()); + }else { + return new StreamContent(streamInfo); + } + } + + @GetMapping(value = "/stop") + @ResponseBody + @Operation(summary = "停用代理", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "id", description = "代理Id", required = true) + public void stop(int id){ + log.info("停用代理: {}", id); + streamProxyPlayService.stop(id); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/dao/StreamProxyMapper.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/dao/StreamProxyMapper.java new file mode 100755 index 000000000..551b6e3c2 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/dao/StreamProxyMapper.java @@ -0,0 +1,91 @@ +package com.genersoft.iot.vmp.streamProxy.dao; + +import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy; +import com.genersoft.iot.vmp.streamProxy.dao.provider.StreamProxyProvider; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Mapper +@Repository +public interface StreamProxyMapper { + + @Insert("INSERT INTO wvp_stream_proxy (type, app, stream,media_server_id, src_url, " + + "timeout, ffmpeg_cmd_key, rtsp_type, enable_audio, enable_mp4, enable, pulling, stream_key, " + + "enable_remove_none_reader, enable_disable_none_reader, create_time) VALUES" + + "(#{type}, #{app}, #{stream}, #{mediaServerId}, #{srcUrl}, " + + "#{timeout}, #{ffmpegCmdKey}, #{rtspType}, #{enableAudio}, #{enableMp4}, #{enable}, #{pulling}, #{streamKey}, " + + "#{enableRemoveNoneReader}, #{enableDisableNoneReader}, #{createTime} )") + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") + int add(StreamProxy streamProxyDto); + + @Update("UPDATE wvp_stream_proxy " + + "SET type=#{type}, " + + "app=#{app}," + + "stream=#{stream}," + + "media_server_id=#{mediaServerId}, " + + "src_url=#{srcUrl}," + + "timeout=#{timeout}, " + + "ffmpeg_cmd_key=#{ffmpegCmdKey}, " + + "rtsp_type=#{rtspType}, " + + "enable_audio=#{enableAudio}, " + + "enable=#{enable}, " + + "pulling=#{pulling}, " + + "stream_key=#{streamKey}, " + + "enable_remove_none_reader=#{enableRemoveNoneReader}, " + + "enable_disable_none_reader=#{enableDisableNoneReader}, " + + "enable_mp4=#{enableMp4} " + + "WHERE id=#{id}") + int update(StreamProxy streamProxyDto); + + @Delete("DELETE FROM wvp_stream_proxy WHERE app=#{app} AND stream=#{stream}") + int delByAppAndStream(String app, String stream); + + @SelectProvider(type = StreamProxyProvider.class, method = "selectAll") + List selectAll(@Param("query") String query, @Param("pulling") Boolean pulling, @Param("mediaServerId") String mediaServerId); + + @SelectProvider(type = StreamProxyProvider.class, method = "selectOneByAppAndStream") + StreamProxy selectOneByAppAndStream(@Param("app") String app, @Param("stream") String stream); + + @SelectProvider(type = StreamProxyProvider.class, method = "selectForEnableInMediaServer") + List selectForEnableInMediaServer(@Param("mediaServerId") String mediaServerId, @Param("enable") boolean enable); + + + @Select("select count(1) from wvp_stream_proxy") + int getAllCount(); + + @Select("select count(1) from wvp_stream_proxy where pulling = true") + int getOnline(); + + @Delete("DELETE FROM wvp_stream_proxy WHERE id=#{id}") + int delete(@Param("id") int id); + + @Delete(value = "") + void deleteByList(List streamProxiesForRemove); + + @Update("UPDATE wvp_stream_proxy " + + "SET pulling=true " + + "WHERE id=#{id}") + int online(@Param("id") int id); + + @Update("UPDATE wvp_stream_proxy " + + "SET pulling=false " + + "WHERE id=#{id}") + int offline(@Param("id") int id); + + @SelectProvider(type = StreamProxyProvider.class, method = "select") + StreamProxy select(@Param("id") int id); + + @Update("UPDATE wvp_stream_proxy " + + "SET pulling=false, " + + "stream_key = null " + + "WHERE id=#{id}") + void removeStream(@Param("id")int id); +} diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/dao/provider/StreamProxyProvider.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/dao/provider/StreamProxyProvider.java new file mode 100644 index 000000000..653426f67 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/dao/provider/StreamProxyProvider.java @@ -0,0 +1,63 @@ +package com.genersoft.iot.vmp.streamProxy.dao.provider; + +import java.util.Map; + +public class StreamProxyProvider { + + public String getBaseSelectSql(){ + return "SELECT " + + " st.*, " + + " st.id as stream_proxy_id, " + + " wdc.*, " + + " wdc.id as gb_id" + + " FROM wvp_stream_proxy st " + + " LEFT join wvp_device_channel wdc " + + " on st.id = wdc.stream_proxy_id "; + } + + public String select(Map params ){ + return getBaseSelectSql() + " WHERE st.id = " + params.get("id"); + } + + public String selectForEnableInMediaServer(Map params ){ + return getBaseSelectSql() + String.format(" WHERE st.enable=%s and st.media_server_id= '%s' order by st.create_time desc", + params.get("enable"), params.get("mediaServerId")); + } + + public String selectOneByAppAndStream(Map params ){ + return getBaseSelectSql() + String.format(" WHERE st.app='%s' AND st.stream='%s' order by st.create_time desc", + params.get("app"), params.get("stream")); + } + + public String selectAll(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(getBaseSelectSql()); + sqlBuild.append(" WHERE 1=1 "); + if (params.get("query") != null) { + sqlBuild.append(" AND ") + .append(" (") + .append(" st.app LIKE ").append("'%").append(params.get("query")).append("%'") + .append(" OR") + .append(" st.stream LIKE ").append("'%").append(params.get("query")).append("%'") + .append(" OR") + .append(" wdc.gb_device_id LIKE ").append("'%").append(params.get("query")).append("%'") + .append(" OR") + .append(" wdc.gb_name LIKE ").append("'%").append(params.get("query")).append("%'") + .append(" )") + ; + } + Object pulling = params.get("pulling"); + if (pulling != null) { + if ((Boolean) pulling) { + sqlBuild.append(" AND st.pulling=1 "); + }else { + sqlBuild.append(" AND st.pulling=0 "); + } + } + if (params.get("mediaServerId") != null) { + sqlBuild.append(" AND st.media_server_id='").append(params.get("mediaServerId")).append("'"); + } + sqlBuild.append(" order by st.create_time desc"); + return sqlBuild.toString(); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/service/IStreamProxyPlayService.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/IStreamProxyPlayService.java new file mode 100755 index 000000000..9f9ebf6f8 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/IStreamProxyPlayService.java @@ -0,0 +1,15 @@ +package com.genersoft.iot.vmp.streamProxy.service; + +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy; + +public interface IStreamProxyPlayService { + + StreamInfo start(int id); + + StreamInfo startProxy(StreamProxy streamProxy); + + void stop(int id); + + void stopProxy(StreamProxy streamProxy); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/IStreamProxyService.java similarity index 53% rename from src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java rename to src/main/java/com/genersoft/iot/vmp/streamProxy/service/IStreamProxyService.java index f3b516286..f612abd94 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/IStreamProxyService.java @@ -1,11 +1,10 @@ -package com.genersoft.iot.vmp.service; +package com.genersoft.iot.vmp.streamProxy.service; -import com.genersoft.iot.vmp.common.GeneralCallback; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; +import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy; +import com.genersoft.iot.vmp.streamProxy.bean.StreamProxyParam; import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import com.github.pagehelper.PageInfo; import java.util.Map; @@ -16,23 +15,7 @@ public interface IStreamProxyService { * 保存视频代理 * @param param */ - void save(StreamProxyItem param, GeneralCallback callback); - - /** - * 添加视频代理到zlm - * - * @param param - * @return - */ - WVPResult addStreamProxyToZlm(StreamProxyItem param); - - /** - * 从zlm移除视频代理 - * - * @param param - * @return - */ - Boolean removeStreamProxyFromZlm(StreamProxyItem param); + StreamInfo save(StreamProxyParam param); /** * 分页查询 @@ -40,14 +23,14 @@ public interface IStreamProxyService { * @param count * @return */ - PageInfo getAll(Integer page, Integer count); + PageInfo getAll(Integer page, Integer count, String query, Boolean pulling,String mediaServerId); /** * 删除视频代理 * @param app * @param stream */ - void del(String app, String stream); + void delteByAppAndStream(String app, String stream); /** * 启用视频代理 @@ -55,17 +38,7 @@ public interface IStreamProxyService { * @param stream * @return */ - boolean start(String app, String stream); - - /** - * 更新状态 - * @param status 状态 - * @param app - * @param stream - */ - int updateStatus(boolean status, String app, String stream); - - + boolean startByAppAndStream(String app, String stream); /** * 停用用视频代理 @@ -73,7 +46,7 @@ public interface IStreamProxyService { * @param stream * @return */ - boolean stop(String app, String stream); + void stopByAppAndStream(String app, String stream); /** * 获取ffmpeg.cmd模板 @@ -86,7 +59,7 @@ public interface IStreamProxyService { * 根据app与stream获取streamProxy * @return */ - StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId); + StreamProxy getStreamProxyByAppAndStream(String app, String streamId); /** @@ -103,12 +76,10 @@ public interface IStreamProxyService { */ void zlmServerOffline(String mediaServerId); - void clean(); - /** * 更新代理流 */ - boolean updateStreamProxy(StreamProxyItem streamProxyItem); + boolean update(StreamProxy streamProxyItem); /** * 获取统计信息 @@ -116,4 +87,9 @@ public interface IStreamProxyService { */ ResourceBaseInfo getOverview(); + void add(StreamProxy streamProxy); + + StreamProxy getStreamProxy(int id); + + void delete(int id); } diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyPlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyPlayServiceImpl.java new file mode 100755 index 000000000..170ed6fdc --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyPlayServiceImpl.java @@ -0,0 +1,97 @@ +package com.genersoft.iot.vmp.streamProxy.service.impl; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.media.service.IMediaServerService; +import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy; +import com.genersoft.iot.vmp.streamProxy.dao.StreamProxyMapper; +import com.genersoft.iot.vmp.streamProxy.service.IStreamProxyPlayService; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +/** + * 视频代理业务 + */ +@Slf4j +@Service +@DS("master") +public class StreamProxyPlayServiceImpl implements IStreamProxyPlayService { + + @Autowired + private StreamProxyMapper streamProxyMapper; + + @Autowired + private IMediaServerService mediaServerService; + + @Override + public StreamInfo start(int id) { + StreamProxy streamProxy = streamProxyMapper.select(id); + if (streamProxy == null) { + throw new ControllerException(ErrorCode.ERROR404.getCode(), "代理信息未找到"); + } + return startProxy(streamProxy); + } + + @Override + public StreamInfo startProxy(StreamProxy streamProxy){ + if (!streamProxy.isEnable()) { + return null; + } + MediaServer mediaServer; + String mediaServerId = streamProxy.getMediaServerId(); + if (mediaServerId == null) { + mediaServer = mediaServerService.getMediaServerForMinimumLoad(null); + }else { + mediaServer = mediaServerService.getOne(mediaServerId); + if (mediaServer == null) { + mediaServer = mediaServerService.getMediaServerForMinimumLoad(null); + } + } + if (mediaServer == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的媒体节点"); + } + StreamInfo streamInfo = mediaServerService.startProxy(mediaServer, streamProxy); + if (mediaServerId == null || !mediaServerId.equals(mediaServer.getId())) { + streamProxy.setMediaServerId(mediaServer.getId()); + streamProxyMapper.update(streamProxy); + } + return streamInfo; + } + + @Override + public void stop(int id) { + StreamProxy streamProxy = streamProxyMapper.select(id); + if (streamProxy == null) { + throw new ControllerException(ErrorCode.ERROR404.getCode(), "代理信息未找到"); + } + stopProxy(streamProxy); + } + + @Override + public void stopProxy(StreamProxy streamProxy){ + + String mediaServerId = streamProxy.getMediaServerId(); + Assert.notNull(mediaServerId, "代理节点不存在"); + + MediaServer mediaServer = mediaServerService.getOne(mediaServerId); + if (mediaServer == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体节点不存在"); + } + if (ObjectUtils.isEmpty(streamProxy.getStreamKey())) { + mediaServerService.closeStreams(mediaServer, streamProxy.getApp(), streamProxy.getStream()); + }else { + mediaServerService.stopProxy(mediaServer, streamProxy.getStreamKey()); + } + streamProxy.setMediaServerId(mediaServer.getId()); + streamProxy.setStreamKey(null); + streamProxy.setPulling(false); + streamProxyMapper.removeStream(streamProxy.getId()); + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyServiceImpl.java new file mode 100755 index 000000000..c5909c0bf --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyServiceImpl.java @@ -0,0 +1,441 @@ +package com.genersoft.iot.vmp.streamProxy.service.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; +import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent; +import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent; +import com.genersoft.iot.vmp.media.event.media.MediaNotFoundEvent; +import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOfflineEvent; +import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOnlineEvent; +import com.genersoft.iot.vmp.media.service.IMediaServerService; +import com.genersoft.iot.vmp.media.zlm.dto.hook.OriginType; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy; +import com.genersoft.iot.vmp.streamProxy.bean.StreamProxyParam; +import com.genersoft.iot.vmp.streamProxy.dao.StreamProxyMapper; +import com.genersoft.iot.vmp.streamProxy.service.IStreamProxyPlayService; +import com.genersoft.iot.vmp.streamProxy.service.IStreamProxyService; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; +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.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 视频代理业务 + */ +@Slf4j +@Service +@DS("master") +public class StreamProxyServiceImpl implements IStreamProxyService { + + @Autowired + private StreamProxyMapper streamProxyMapper; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IStreamProxyPlayService playService; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private IGbChannelService gbChannelService; + + @Autowired + DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + TransactionDefinition transactionDefinition; + + /** + * 流到来的处理 + */ + @Async("taskExecutor") + @Transactional + @org.springframework.context.event.EventListener + public void onApplicationEvent(MediaArrivalEvent event) { + if ("rtsp".equals(event.getSchema())) { + streamChangeHandler(event.getApp(), event.getStream(), event.getMediaServer().getId(), true); + } + } + + /** + * 流离开的处理 + */ + @Async("taskExecutor") + @EventListener + @Transactional + public void onApplicationEvent(MediaDepartureEvent event) { + if ("rtsp".equals(event.getSchema())) { + streamChangeHandler(event.getApp(), event.getStream(), event.getMediaServer().getId(), false); + } + } + + /** + * 流未找到的处理 + */ + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(MediaNotFoundEvent event) { + if ("rtp".equals(event.getApp())) { + return; + } + // 拉流代理 + StreamProxy streamProxyByAppAndStream = getStreamProxyByAppAndStream(event.getApp(), event.getStream()); + if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnableDisableNoneReader()) { + startByAppAndStream(event.getApp(), event.getStream()); + } + } + + /** + * 流媒体节点上线 + */ + @Async("taskExecutor") + @EventListener + @Transactional + public void onApplicationEvent(MediaServerOnlineEvent event) { + zlmServerOnline(event.getMediaServerId()); + } + + /** + * 流媒体节点离线 + */ + @Async("taskExecutor") + @EventListener + @Transactional + public void onApplicationEvent(MediaServerOfflineEvent event) { + zlmServerOffline(event.getMediaServerId()); + } + + + @Override + @Transactional + public StreamInfo save(StreamProxyParam param) { + // 兼容旧接口 + StreamProxy streamProxyInDb = getStreamProxyByAppAndStream(param.getApp(), param.getStream()); + if (streamProxyInDb != null && streamProxyInDb.getPulling() != null && streamProxyInDb.getPulling()) { + playService.stopProxy(streamProxyInDb); + } + if (param.getMediaServerId().equals("auto")) { + param.setMediaServerId(null); + } + StreamProxy streamProxy = param.buildStreamProxy(); + + if (streamProxyInDb == null) { + add(streamProxy); + } else { + try { + playService.stopProxy(streamProxyInDb); + } catch (ControllerException ignored) { + } + streamProxyMapper.delete(streamProxyInDb.getId()); + add(streamProxy); + } + + if (param.isEnable()) { + return playService.startProxy(streamProxy); + } else { + return null; + } + } + + @Override + @Transactional + public void add(StreamProxy streamProxy) { + StreamProxy streamProxyInDb = streamProxyMapper.selectOneByAppAndStream(streamProxy.getApp(), streamProxy.getStream()); + if (streamProxyInDb != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "APP+STREAM已经存在"); + } + if (streamProxy.getGbDeviceId() != null) { + gbChannelService.add(streamProxy.buildCommonGBChannel()); + } + streamProxy.setCreateTime(DateUtil.getNow()); + streamProxy.setUpdateTime(DateUtil.getNow()); + streamProxyMapper.add(streamProxy); + streamProxy.setStreamProxyId(streamProxy.getId()); + } + + @Override + public void delete(int id) { + StreamProxy streamProxy = getStreamProxy(id); + if (streamProxy == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "代理不存在"); + } + delete(streamProxy); + } + + private void delete(StreamProxy streamProxy) { + Assert.notNull(streamProxy, "代理不可为NULL"); + if (streamProxy.getPulling() != null && streamProxy.getPulling()) { + playService.stopProxy(streamProxy); + } + if (streamProxy.getGbId() > 0) { + gbChannelService.delete(streamProxy.getGbId()); + } + streamProxyMapper.delete(streamProxy.getId()); + } + + @Override + @Transactional + public void delteByAppAndStream(String app, String stream) { + StreamProxy streamProxy = streamProxyMapper.selectOneByAppAndStream(app, stream); + if (streamProxy == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "代理不存在"); + } + delete(streamProxy); + } + + /** + * 更新代理流 + */ + @Override + public boolean update(StreamProxy streamProxy) { + streamProxy.setUpdateTime(DateUtil.getNow()); + StreamProxy streamProxyInDb = streamProxyMapper.select(streamProxy.getId()); + if (streamProxyInDb == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "代理不存在"); + } + int updateResult = streamProxyMapper.update(streamProxy); + if (updateResult > 0 && !ObjectUtils.isEmpty(streamProxy.getGbDeviceId())) { + if (streamProxy.getGbId() > 0) { + gbChannelService.update(streamProxy.buildCommonGBChannel()); + } else { + gbChannelService.add(streamProxy.buildCommonGBChannel()); + } + } + // 判断是否需要重启代理 + if (!streamProxyInDb.getApp().equals(streamProxy.getApp()) + || !streamProxyInDb.getStream().equals(streamProxy.getStream()) + || (streamProxyInDb.getMediaServerId() != null && streamProxyInDb.getMediaServerId().equals(streamProxy.getMediaServerId())) + || (streamProxyInDb.getMediaServerId() == null && streamProxy.getMediaServerId() != null) + ) { + // 变化则重启代理 + playService.stopProxy(streamProxyInDb); + playService.startProxy(streamProxy); + } + return true; + } + + @Override + public PageInfo getAll(Integer page, Integer count, String query, Boolean pulling, String mediaServerId) { + PageHelper.startPage(page, count); + List all = streamProxyMapper.selectAll(query, pulling, mediaServerId); + return new PageInfo<>(all); + } + + + @Override + public boolean startByAppAndStream(String app, String stream) { + StreamProxy streamProxy = streamProxyMapper.selectOneByAppAndStream(app, stream); + if (streamProxy == null) { + throw new ControllerException(ErrorCode.ERROR404.getCode(), "代理信息未找到"); + } + StreamInfo streamInfo = playService.startProxy(streamProxy); + return streamInfo != null; + } + + @Override + public void stopByAppAndStream(String app, String stream) { + StreamProxy streamProxy = streamProxyMapper.selectOneByAppAndStream(app, stream); + if (streamProxy == null) { + throw new ControllerException(ErrorCode.ERROR404.getCode(), "代理信息未找到"); + } + playService.stopProxy(streamProxy); + } + + + @Override + public Map getFFmpegCMDs(MediaServer mediaServer) { + return mediaServerService.getFFmpegCMDs(mediaServer); + } + + + @Override + public StreamProxy getStreamProxyByAppAndStream(String app, String stream) { + return streamProxyMapper.selectOneByAppAndStream(app, stream); + } + + @Override + @Transactional + public void zlmServerOnline(String mediaServerId) { + MediaServer mediaServer = mediaServerService.getOne(mediaServerId); + if (mediaServer == null) { + return; + } + // 这里主要是控制数据库/redis缓存/以及zlm中存在的代理流 三者状态一致。以数据库中数据为根本 + redisCatchStorage.removeStream(mediaServerId, "pull"); + + List streamProxies = streamProxyMapper.selectForEnableInMediaServer(mediaServerId, true); + if (streamProxies.isEmpty()) { + return; + } + Map streamProxyMapForDb = new HashMap<>(); + for (StreamProxy streamProxy : streamProxies) { + streamProxyMapForDb.put(streamProxy.getApp() + "_" + streamProxy.getStream(), streamProxy); + } + + List streamInfoList = mediaServerService.getMediaList(mediaServer, null, null, null); + + List channelListForOnline = new ArrayList<>(); + for (StreamInfo streamInfo : streamInfoList) { + String key = streamInfo.getApp() + streamInfo.getStream(); + StreamProxy streamProxy = streamProxyMapForDb.get(key); + if (streamProxy == null) { + // 流媒体存在,数据库中不存在 + continue; + } + if (streamInfo.getOriginType() == OriginType.PULL.ordinal() + || streamInfo.getOriginType() == OriginType.FFMPEG_PULL.ordinal()) { + if (streamProxyMapForDb.get(key) != null) { + redisCatchStorage.addStream(mediaServer, "pull", streamInfo.getApp(), streamInfo.getStream(), streamInfo.getMediaInfo()); + if ("OFF".equalsIgnoreCase(streamProxy.getGbStatus()) && streamProxy.getGbId() > 0) { + streamProxy.setGbStatus("ON"); + channelListForOnline.add(streamProxy.buildCommonGBChannel()); + } + streamProxyMapForDb.remove(key); + } + } + } + + if (!channelListForOnline.isEmpty()) { + gbChannelService.online(channelListForOnline); + } + List channelListForOffline = new ArrayList<>(); + List streamProxiesForRemove = new ArrayList<>(); + if (!streamProxyMapForDb.isEmpty()) { + for (StreamProxy streamProxy : streamProxyMapForDb.values()) { + if ("ON".equalsIgnoreCase(streamProxy.getGbStatus()) && streamProxy.getGbId() > 0) { + streamProxy.setGbStatus("OFF"); + channelListForOffline.add(streamProxy.buildCommonGBChannel()); + } + // 移除开启了无人观看自动移除的流 + if (streamProxy.getGbDeviceId() == null && streamProxy.isEnableRemoveNoneReader()) { + streamProxiesForRemove.add(streamProxy); + streamProxyMapForDb.remove(streamProxy.getApp() + streamProxy.getStream()); + } + } + } + if (!channelListForOffline.isEmpty()) { + gbChannelService.offline(channelListForOffline); + } + if (!streamProxiesForRemove.isEmpty()) { + streamProxyMapper.deleteByList(streamProxiesForRemove); + } + + + if (!streamProxyMapForDb.isEmpty()) { + for (StreamProxy streamProxy : streamProxyMapForDb.values()) { + log.info("恢复流代理," + streamProxy.getApp() + "/" + streamProxy.getStream()); + mediaServerService.startProxy(mediaServer, streamProxy); + } + } + } + + @Override + public void zlmServerOffline(String mediaServerId) { + List streamProxies = streamProxyMapper.selectForEnableInMediaServer(mediaServerId, true); + + // 清理redis相关的缓存 + redisCatchStorage.removeStream(mediaServerId, "pull"); + + if (streamProxies.isEmpty()) { + return; + } + List streamProxiesForRemove = new ArrayList<>(); + List streamProxiesForSendMessage = new ArrayList<>(); + List channelListForOffline = new ArrayList<>(); + + for (StreamProxy streamProxy : streamProxies) { + if (streamProxy.getGbId() > 0 && "ON".equalsIgnoreCase(streamProxy.getGbStatus())) { + channelListForOffline.add(streamProxy.buildCommonGBChannel()); + } + if (streamProxy.getGbId() == 0 && streamProxy.isEnableRemoveNoneReader()) { + streamProxiesForRemove.add(streamProxy); + } + if ("ON".equalsIgnoreCase(streamProxy.getGbStatus())) { + streamProxiesForSendMessage.add(streamProxy); + } + } + // 移除开启了无人观看自动移除的流 + streamProxyMapper.deleteByList(streamProxiesForRemove); + // 修改国标关联的国标通道的状态 + gbChannelService.offline(channelListForOffline); + + if (!streamProxiesForSendMessage.isEmpty()) { + for (StreamProxy streamProxy : streamProxiesForSendMessage) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("serverId", userSetting.getServerId()); + jsonObject.put("app", streamProxy.getApp()); + jsonObject.put("stream", streamProxy.getStream()); + jsonObject.put("register", false); + jsonObject.put("mediaServerId", mediaServerId); + redisCatchStorage.sendStreamChangeMsg("pull", jsonObject); + } + } + } + + @Transactional + public void streamChangeHandler(String app, String stream, String mediaServerId, boolean status) { + // 状态变化时推送到国标上级 + StreamProxy streamProxy = streamProxyMapper.selectOneByAppAndStream(app, stream); + if (streamProxy == null) { + return; + } + streamProxy.setPulling(status); + if (!mediaServerId.equals(streamProxy.getMediaServerId())) { + streamProxy.setMediaServerId(mediaServerId); + } + streamProxy.setUpdateTime(DateUtil.getNow()); + streamProxyMapper.update(streamProxy); + streamProxy.setGbStatus(status ? "ON" : "OFF"); + if (streamProxy.getGbId() > 0) { + if (status) { + gbChannelService.online(streamProxy.buildCommonGBChannel()); + } else { + gbChannelService.offline(streamProxy.buildCommonGBChannel()); + } + } + } + + @Override + public ResourceBaseInfo getOverview() { + + int total = streamProxyMapper.getAllCount(); + int online = streamProxyMapper.getOnline(); + + return new ResourceBaseInfo(total, online); + } + + @Override + public StreamProxy getStreamProxy(int id) { + return streamProxyMapper.select(id); + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/streamPush/bean/BatchRemoveParam.java b/src/main/java/com/genersoft/iot/vmp/streamPush/bean/BatchRemoveParam.java new file mode 100644 index 000000000..307ef547c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamPush/bean/BatchRemoveParam.java @@ -0,0 +1,10 @@ +package com.genersoft.iot.vmp.streamPush.bean; + +import lombok.Data; + +import java.util.Set; + +@Data +public class BatchRemoveParam { + private Set ids; +} diff --git a/src/main/java/com/genersoft/iot/vmp/streamPush/bean/RedisPushStreamMessage.java b/src/main/java/com/genersoft/iot/vmp/streamPush/bean/RedisPushStreamMessage.java new file mode 100644 index 000000000..6d12fb489 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamPush/bean/RedisPushStreamMessage.java @@ -0,0 +1,24 @@ +package com.genersoft.iot.vmp.streamPush.bean; + +import lombok.Data; + +@Data +public class RedisPushStreamMessage { + + private String gbId; + private String app; + private String stream; + private String name; + private boolean status; + + public StreamPush buildstreamPush() { + StreamPush push = new StreamPush(); + push.setApp(app); + push.setStream(stream); + push.setGbName(name); + push.setGbDeviceId(gbId); + push.setStartOfflinePush(true); + push.setGbStatus(status?"ON":"OFF"); + return push; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/streamPush/bean/StreamPush.java b/src/main/java/com/genersoft/iot/vmp/streamPush/bean/StreamPush.java new file mode 100755 index 000000000..9a9d5e983 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamPush/bean/StreamPush.java @@ -0,0 +1,124 @@ +package com.genersoft.iot.vmp.streamPush.bean; + +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent; +import com.genersoft.iot.vmp.utils.DateUtil; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.jetbrains.annotations.NotNull; +import org.springframework.util.ObjectUtils; + + +@Data +@Schema(description = "推流信息") +@EqualsAndHashCode(callSuper = true) +public class StreamPush extends CommonGBChannel implements Comparable{ + + /** + * id + */ + @Schema(description = "id") + private Integer id; + + /** + * 应用名 + */ + @Schema(description = "应用名") + private String app; + + /** + * 流id + */ + @Schema(description = "流id") + private String stream; + + /** + * 使用的流媒体ID + */ + @Schema(description = "使用的流媒体ID") + private String mediaServerId; + + /** + * 使用的服务ID + */ + @Schema(description = "使用的服务ID") + private String serverId; + + /** + * 推流时间 + */ + @Schema(description = "推流时间") + private String pushTime; + + /** + * 更新时间 + */ + @Schema(description = "更新时间") + private String updateTime; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + private String createTime; + + /** + * 是否正在推流 + */ + @Schema(description = "是否正在推流") + private boolean pushing; + + /** + * 拉起离线推流 + */ + @Schema(description = "拉起离线推流") + private boolean startOfflinePush; + + private String uniqueKey; + + @Override + public int compareTo(@NotNull StreamPush streamPushItem) { + return Long.valueOf(DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(this.createTime) + - DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(streamPushItem.getCreateTime())).intValue(); + } + + public StreamPush getInstance(StreamInfo streamInfo) { + StreamPush streamPush = new StreamPush(); + streamPush.setApp(streamInfo.getApp()); + if (streamInfo.getMediaServer() != null) { + streamPush.setMediaServerId(streamInfo.getMediaServer().getId()); + } + + streamPush.setStream(streamInfo.getStream()); + streamPush.setCreateTime(DateUtil.getNow()); + streamPush.setServerId(streamInfo.getServerId()); + return streamPush; + + } + + public static StreamPush getInstance(MediaArrivalEvent event, String serverId){ + StreamPush streamPushItem = new StreamPush(); + streamPushItem.setApp(event.getApp()); + streamPushItem.setMediaServerId(event.getMediaServer().getId()); + streamPushItem.setStream(event.getStream()); + streamPushItem.setCreateTime(DateUtil.getNow()); + streamPushItem.setServerId(serverId); + return streamPushItem; + } + + public CommonGBChannel buildCommonGBChannel() { + if (ObjectUtils.isEmpty(this.getGbDeviceId())) { + return null; + } + if (ObjectUtils.isEmpty(this.getGbName())) { + this.setGbName( app+ "-" +stream); + } + this.setStreamPushId(this.getId()); + return this; + } + + +} + diff --git a/src/main/java/com/genersoft/iot/vmp/streamPush/bean/StreamPushExcelDto.java b/src/main/java/com/genersoft/iot/vmp/streamPush/bean/StreamPushExcelDto.java new file mode 100755 index 000000000..4ccc7514a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamPush/bean/StreamPushExcelDto.java @@ -0,0 +1,30 @@ +package com.genersoft.iot.vmp.streamPush.bean; + +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class StreamPushExcelDto { + + @ExcelProperty("名称") + private String name; + + @ExcelProperty("应用名") + private String app; + + @ExcelProperty("流ID") + private String stream; + + @ExcelProperty("国标ID") + private String gbDeviceId; + + @ExcelProperty("在线状态") + private boolean status; + + @Schema(description = "经度 WGS-84坐标系") + private Double longitude; + + @Schema(description = "纬度 WGS-84坐标系") + private Double latitude; +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java b/src/main/java/com/genersoft/iot/vmp/streamPush/controller/StreamPushController.java similarity index 60% rename from src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java rename to src/main/java/com/genersoft/iot/vmp/streamPush/controller/StreamPushController.java index 3717e69f4..3af1f2941 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java +++ b/src/main/java/com/genersoft/iot/vmp/streamPush/controller/StreamPushController.java @@ -1,34 +1,35 @@ -package com.genersoft.iot.vmp.vmanager.streamPush; +package com.genersoft.iot.vmp.streamPush.controller; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelReader; import com.alibaba.excel.read.metadata.ReadSheet; -import com.genersoft.iot.vmp.common.StreamInfo; 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.conf.security.SecurityUtils; -import com.genersoft.iot.vmp.conf.security.dto.LoginUser; -import com.genersoft.iot.vmp.gb28181.bean.GbStream; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; import com.genersoft.iot.vmp.media.service.IMediaServerService; import com.genersoft.iot.vmp.service.IMediaService; -import com.genersoft.iot.vmp.service.IStreamPushService; -import com.genersoft.iot.vmp.service.impl.StreamPushUploadFileHandler; -import com.genersoft.iot.vmp.vmanager.bean.*; +import com.genersoft.iot.vmp.streamPush.bean.BatchRemoveParam; +import com.genersoft.iot.vmp.streamPush.bean.StreamPush; +import com.genersoft.iot.vmp.streamPush.bean.StreamPushExcelDto; +import com.genersoft.iot.vmp.streamPush.enent.StreamPushUploadFileHandler; +import com.genersoft.iot.vmp.streamPush.service.IStreamPushPlayService; +import com.genersoft.iot.vmp.streamPush.service.IStreamPushService; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.StreamContent; +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; 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; import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; +import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; @@ -43,15 +44,16 @@ import java.util.UUID; @Tag(name = "推流信息管理") @Controller - +@Slf4j @RequestMapping(value = "/api/push") public class StreamPushController { - private final static Logger logger = LoggerFactory.getLogger(StreamPushController.class); - @Autowired private IStreamPushService streamPushService; + @Autowired + private IStreamPushPlayService streamPushPlayService; + @Autowired private IMediaServerService mediaServerService; @@ -72,11 +74,11 @@ public class StreamPushController { @Parameter(name = "query", description = "查询内容") @Parameter(name = "pushing", description = "是否正在推流") @Parameter(name = "mediaServerId", description = "流媒体ID") - public PageInfo list(@RequestParam(required = false)Integer page, - @RequestParam(required = false)Integer count, - @RequestParam(required = false)String query, - @RequestParam(required = false)Boolean pushing, - @RequestParam(required = false)String mediaServerId ){ + public PageInfo list(@RequestParam(required = false)Integer page, + @RequestParam(required = false)Integer count, + @RequestParam(required = false)String query, + @RequestParam(required = false)Boolean pushing, + @RequestParam(required = false)String mediaServerId ){ if (ObjectUtils.isEmpty(query)) { query = null; @@ -84,49 +86,17 @@ public class StreamPushController { if (ObjectUtils.isEmpty(mediaServerId)) { mediaServerId = null; } - PageInfo pushList = streamPushService.getPushList(page, count, query, pushing, mediaServerId); + PageInfo pushList = streamPushService.getPushList(page, count, query, pushing, mediaServerId); return pushList; } - @PostMapping(value = "/save_to_gb") + + @PostMapping(value = "/remove") @ResponseBody - @Operation(summary = "将推流添加到国标", security = @SecurityRequirement(name = JwtUtils.HEADER)) - public void saveToGB(@RequestBody GbStream stream){ - if (!streamPushService.saveToGB(stream)){ - throw new ControllerException(ErrorCode.ERROR100); - } - } - - - @DeleteMapping(value = "/remove_form_gb") - @ResponseBody - @Operation(summary = "将推流移出到国标", security = @SecurityRequirement(name = JwtUtils.HEADER)) - public void removeFormGB(@RequestBody GbStream stream){ - if (!streamPushService.removeFromGB(stream)){ - throw new ControllerException(ErrorCode.ERROR100); - } - } - - - @PostMapping(value = "/stop") - @ResponseBody - @Operation(summary = "中止一个推流", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "app", description = "应用名", required = true) - @Parameter(name = "stream", description = "流id", required = true) - public void stop(String app, String stream){ - if (!streamPushService.stop(app, stream)){ - throw new ControllerException(ErrorCode.ERROR100); - } - } - - @DeleteMapping(value = "/batchStop") - @ResponseBody - @Operation(summary = "中止多个推流", security = @SecurityRequirement(name = JwtUtils.HEADER)) - public void batchStop(@RequestBody BatchGBStreamParam batchGBStreamParam){ - if (batchGBStreamParam.getGbStreams().size() == 0) { - throw new ControllerException(ErrorCode.ERROR100); - } - if (!streamPushService.batchStop(batchGBStreamParam.getGbStreams())){ + @Operation(summary = "删除", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "id", description = "应用名", required = true) + public void delete(int id){ + if (streamPushService.delete(id) > 0){ throw new ControllerException(ErrorCode.ERROR100); } } @@ -140,9 +110,9 @@ public class StreamPushController { // 录像查询以channelId作为deviceId查询 String key = DeferredResultHolder.UPLOAD_FILE_CHANNEL; String uuid = UUID.randomUUID().toString(); - logger.info("通道导入文件类型: {}",file.getContentType() ); + log.info("通道导入文件类型: {}",file.getContentType() ); if (file.isEmpty()) { - logger.warn("通道导入文件为空"); + log.warn("通道导入文件为空"); WVPResult wvpResult = new WVPResult<>(); wvpResult.setCode(-1); wvpResult.setMsg("文件为空"); @@ -158,7 +128,7 @@ public class StreamPushController { } // 同时只处理一个文件 if (resultHolder.exist(key, null)) { - logger.warn("已有导入任务正在执行"); + log.warn("已有导入任务正在执行"); WVPResult wvpResult = new WVPResult<>(); wvpResult.setCode(-1); wvpResult.setMsg("已有导入任务正在执行"); @@ -168,7 +138,7 @@ public class StreamPushController { resultHolder.put(key, uuid, result); result.onTimeout(()->{ - logger.warn("通道导入超时,可能文件过大"); + log.warn("通道导入超时,可能文件过大"); RequestMessage msg = new RequestMessage(); msg.setKey(key); WVPResult wvpResult = new WVPResult<>(); @@ -183,17 +153,17 @@ public class StreamPushController { String name = file.getName(); inputStream = file.getInputStream(); } catch (IOException e) { - logger.error("未处理的异常 ", e); + log.error("未处理的异常 ", e); } try { //传入参数 ExcelReader excelReader = EasyExcel.read(inputStream, StreamPushExcelDto.class, new StreamPushUploadFileHandler(streamPushService, mediaServerService.getDefaultMediaServer().getId(), (errorStreams, errorGBs)->{ - logger.info("通道导入成功,存在重复App+Stream为{}个,存在国标ID为{}个", errorStreams.size(), errorGBs.size()); + log.info("通道导入成功,存在重复App+Stream为{}个,存在国标ID为{}个", errorStreams.size(), errorGBs.size()); RequestMessage msg = new RequestMessage(); msg.setKey(key); WVPResult>> wvpResult = new WVPResult<>(); - if (errorStreams.size() == 0 && errorGBs.size() == 0) { + if (errorStreams.isEmpty() && errorGBs.isEmpty()) { wvpResult.setCode(0); wvpResult.setMsg("成功"); }else { @@ -211,7 +181,7 @@ public class StreamPushController { excelReader.read(readSheet); excelReader.finish(); }catch (Exception e) { - logger.warn("通道导入失败:", e); + log.warn("通道导入失败:", e); RequestMessage msg = new RequestMessage(); msg.setKey(key); WVPResult wvpResult = new WVPResult<>(); @@ -225,37 +195,6 @@ public class StreamPushController { return result; } - /** - * 获取推流播放地址 - * @param app 应用名 - * @param stream 流id - * @return - */ - @GetMapping(value = "/getPlayUrl") - @ResponseBody - @Operation(summary = "获取推流播放地址", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "app", description = "应用名", required = true) - @Parameter(name = "stream", description = "流id", required = true) - @Parameter(name = "mediaServerId", description = "媒体服务器id") - public StreamContent getPlayUrl(@RequestParam String app, @RequestParam String stream, - @RequestParam(required = false) String mediaServerId){ - boolean authority = false; - // 是否登陆用户, 登陆用户返回完整信息 - LoginUser userInfo = SecurityUtils.getUserInfo(); - if (userInfo!= null) { - authority = true; - } - StreamPushItem push = streamPushService.getPush(app, stream); - if (push != null && !push.isSelf()) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "来自其他平台的推流信息"); - } - StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority); - if (streamInfo == null){ - throw new ControllerException(ErrorCode.ERROR100.getCode(), "获取播放地址失败"); - } - return new StreamContent(streamInfo); - } - /** * 添加推流信息 * @param stream 推流信息 @@ -264,19 +203,60 @@ public class StreamPushController { @PostMapping(value = "/add") @ResponseBody @Operation(summary = "添加推流信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) - public void add(@RequestBody StreamPushItem stream){ + public StreamPush add(@RequestBody StreamPush stream){ if (ObjectUtils.isEmpty(stream.getGbId())) { throw new ControllerException(ErrorCode.ERROR400.getCode(), "国标ID不可为空"); } if (ObjectUtils.isEmpty(stream.getApp()) && ObjectUtils.isEmpty(stream.getStream())) { throw new ControllerException(ErrorCode.ERROR400.getCode(), "app或stream不可为空"); } - stream.setStatus(false); - stream.setPushIng(false); - stream.setAliveSecond(0L); - stream.setTotalReaderCount(0); + stream.setGbStatus("OFF"); + stream.setPushing(false); if (!streamPushService.add(stream)) { throw new ControllerException(ErrorCode.ERROR100); } + stream.setStreamPushId(stream.getId()); + return stream; + } + + @PostMapping(value = "/update") + @ResponseBody + @Operation(summary = "更新推流信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) + public void update(@RequestBody StreamPush stream){ + if (ObjectUtils.isEmpty(stream.getId())) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "ID不可为空"); + } + if (!streamPushService.update(stream)) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @DeleteMapping(value = "/batchRemove") + @ResponseBody + @Operation(summary = "删除多个推流", security = @SecurityRequirement(name = JwtUtils.HEADER)) + public void batchStop(@RequestBody BatchRemoveParam ids){ + if(ids.getIds().isEmpty()) { + return; + } + streamPushService.batchRemove(ids.getIds()); + } + + @GetMapping(value = "/start") + @ResponseBody + @Operation(summary = "开始播放", security = @SecurityRequirement(name = JwtUtils.HEADER)) + public DeferredResult> batchStop(Integer id){ + Assert.notNull(id, "推流ID不可为NULL"); + DeferredResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); + result.onTimeout(()->{ + WVPResult fail = WVPResult.fail(ErrorCode.ERROR100.getCode(), "等待推流超时"); + result.setResult(fail); + }); + streamPushPlayService.start(id, (code, msg, streamInfo) -> { + if (code == 0 && streamInfo != null) { + WVPResult success = WVPResult.success(new StreamContent(streamInfo)); + result.setResult(success); + } + }, null, null); + return result; } } diff --git a/src/main/java/com/genersoft/iot/vmp/streamPush/dao/StreamPushMapper.java b/src/main/java/com/genersoft/iot/vmp/streamPush/dao/StreamPushMapper.java new file mode 100755 index 000000000..b34414358 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamPush/dao/StreamPushMapper.java @@ -0,0 +1,157 @@ +package com.genersoft.iot.vmp.streamPush.dao; + +import com.genersoft.iot.vmp.streamPush.bean.StreamPush; +import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Mapper +@Repository +public interface StreamPushMapper { + + @Insert("INSERT INTO wvp_stream_push (app, stream, media_server_id, server_id, push_time, update_time, create_time, pushing, start_offline_push) VALUES" + + "(#{app}, #{stream}, #{mediaServerId} , #{serverId} , #{pushTime} ,#{updateTime}, #{createTime}, #{pushing}, #{startOfflinePush})") + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") + int add(StreamPush streamPushItem); + + + @Update(value = {" "}) + int update(StreamPush streamPushItem); + + @Delete("DELETE FROM wvp_stream_push WHERE id=#{id}") + int del(@Param("id") int id); + + @Select(value = {" "}) + List selectAll(@Param("query") String query, @Param("pushing") Boolean pushing, @Param("mediaServerId") String mediaServerId); + + @Select("SELECT st.*, st.id as stream_push_id, wdc.*, wdc.id as gb_id FROM wvp_stream_push st LEFT join wvp_device_channel wdc on st.id = wdc.stream_push_id WHERE st.app=#{app} AND st.stream=#{stream}") + StreamPush selectByAppAndStream(@Param("app") String app, @Param("stream") String stream); + + @Insert("") + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") + int addAll(List streamPushItems); + + @Select("SELECT st.*, st.id as stream_push_id, wdc.*, wdc.id as gb_id FROM wvp_stream_push st LEFT join wvp_device_channel wdc on st.id = wdc.stream_push_id WHERE st.media_server_id=#{mediaServerId}") + List selectAllByMediaServerId(String mediaServerId); + + @Select("SELECT st.*, st.id as stream_push_id, wdc.*, wdc.id as gb_id FROM wvp_stream_push st LEFT join wvp_device_channel wdc on st.id = wdc.stream_push_id WHERE st.media_server_id=#{mediaServerId} and wdc.gb_device_id is null") + List selectAllByMediaServerIdWithOutGbID(String mediaServerId); + + @Update("UPDATE wvp_stream_push " + + "SET pushing=#{pushing} " + + "WHERE id=#{id}") + int updatePushStatus(@Param("id") int id, @Param("pushing") boolean pushing); + + + @Select("") + List getListFromRedis(List offlineStreams); + + + @Select("SELECT CONCAT(app,stream) from wvp_stream_push") + List getAllAppAndStream(); + + @Select("select count(1) from wvp_stream_push ") + int getAllCount(); + + @Select(value = {" "}) + int getAllPushing(Boolean usePushingAsStatus); + + @MapKey("uniqueKey") + @Select("SELECT CONCAT(wsp.app, wsp.stream) as unique_key, wsp.*, wsp.* , wdc.id as gb_id " + + " from wvp_stream_push wsp " + + " LEFT join wvp_device_channel wdc on wsp.id = wdc.stream_push_id") + Map getAllAppAndStreamMap(); + + + @MapKey("gbDeviceId") + @Select("SELECT wdc.gb_device_id, wsp.id as stream_push_id, wsp.*, wsp.* , wdc.id as gb_id " + + " from wvp_stream_push wsp " + + " LEFT join wvp_device_channel wdc on wsp.id = wdc.stream_push_id") + Map getAllGBId(); + + @Select("SELECT st.*, st.id as stream_push_id, wdc.*, wdc.id as gb_id FROM wvp_stream_push st LEFT join wvp_device_channel wdc on st.id = wdc.stream_push_id WHERE st.id=#{id}") + StreamPush queryOne(@Param("id") int id); + + @Select("") + List selectInSet(Set ids); + + @Delete("") + void batchDel(List streamPushList); + + + @Update({""}) + int batchUpdate(List streamPushItemForUpdate); +} diff --git a/src/main/java/com/genersoft/iot/vmp/streamPush/enent/StreamPushUploadFileHandler.java b/src/main/java/com/genersoft/iot/vmp/streamPush/enent/StreamPushUploadFileHandler.java new file mode 100755 index 000000000..9ccc8c8c0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamPush/enent/StreamPushUploadFileHandler.java @@ -0,0 +1,140 @@ +package com.genersoft.iot.vmp.streamPush.enent; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.genersoft.iot.vmp.streamPush.bean.StreamPush; +import com.genersoft.iot.vmp.streamPush.bean.StreamPushExcelDto; +import com.genersoft.iot.vmp.streamPush.service.IStreamPushService; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import org.springframework.util.ObjectUtils; + +import java.util.*; + +public class StreamPushUploadFileHandler extends AnalysisEventListener { + + /** + * 错误数据的回调,用于将错误数据发送给页面 + */ + private final ErrorDataHandler errorDataHandler; + + /** + * 推流的业务类用于存储数据 + */ + private final IStreamPushService pushService; + + /** + * 默认流媒体节点ID + */ + private final String defaultMediaServerId; + + /** + * 用于存储更具APP+Stream过滤后的数据,可以直接存入stream_push表与gb_stream表 + */ + private final Map streamPushItemForSave = new HashMap<>(); + + /** + * 用于存储APP+Stream->国标ID 的数据结构, 数据一一对应,全局判断APP+Stream->国标ID是否存在不对应 + */ + private final BiMap gBMap = HashBiMap.create(); + + /** + * 用于存储APP+Stream-> 在数据库中的数据 + */ + private final BiMap pushMapInDb = HashBiMap.create(); + + /** + * 记录错误的APP+Stream + */ + private final List errorStreamList = new ArrayList<>(); + + + /** + * 记录错误的国标ID + */ + private final List errorInfoList = new ArrayList<>(); + + /** + * 读取数量计数器 + */ + private int loadedSize = 0; + + public StreamPushUploadFileHandler(IStreamPushService pushService, String defaultMediaServerId, ErrorDataHandler errorDataHandler) { + this.pushService = pushService; + this.defaultMediaServerId = defaultMediaServerId; + this.errorDataHandler = errorDataHandler; + // 获取数据库已有的数据,已经存在的则忽略 + List allAppAndStreams = pushService.getAllAppAndStream(); + if (!allAppAndStreams.isEmpty()) { + for (String allAppAndStream : allAppAndStreams) { + pushMapInDb.put(allAppAndStream, allAppAndStream); + } + } + } + + public interface ErrorDataHandler{ + void handle(List streams, List gbId); + } + + @Override + public void invoke(StreamPushExcelDto streamPushExcelDto, AnalysisContext analysisContext) { + if (ObjectUtils.isEmpty(streamPushExcelDto.getApp()) + || ObjectUtils.isEmpty(streamPushExcelDto.getStream()) + || ObjectUtils.isEmpty(streamPushExcelDto.getGbDeviceId())) { + return; + } + Integer rowIndex = analysisContext.readRowHolder().getRowIndex(); + + if (gBMap.get(streamPushExcelDto.getApp() + streamPushExcelDto.getStream()) == null) { + try { + gBMap.put(streamPushExcelDto.getApp() + streamPushExcelDto.getStream(), streamPushExcelDto.getGbDeviceId()); + }catch (IllegalArgumentException e) { + errorInfoList.add("行:" + rowIndex + ", " + streamPushExcelDto.getGbDeviceId() + " 国标ID重复使用"); + return; + } + }else { + if (!gBMap.get(streamPushExcelDto.getApp() + streamPushExcelDto.getStream()).equals(streamPushExcelDto.getGbDeviceId())) { + errorInfoList.add("行:" + rowIndex + ", " + streamPushExcelDto.getGbDeviceId() + " 同样的应用名和流ID使用了不同的国标ID"); + return; + } + } + + StreamPush streamPush = new StreamPush(); + streamPush.setApp(streamPushExcelDto.getApp()); + streamPush.setStream(streamPushExcelDto.getStream()); + streamPush.setGbDeviceId(streamPushExcelDto.getGbDeviceId()); + streamPush.setGbStatus(streamPushExcelDto.isStatus()?"ON":"OFF"); + streamPush.setCreateTime(DateUtil.getNow()); + streamPush.setMediaServerId(defaultMediaServerId); + streamPush.setGbName(streamPushExcelDto.getName()); + streamPush.setGbLongitude(streamPushExcelDto.getLongitude()); + streamPush.setGbLatitude(streamPushExcelDto.getLatitude()); + streamPush.setUpdateTime(DateUtil.getNow()); + streamPushItemForSave.put(streamPush.getApp() + streamPush.getStream(), streamPush); + + loadedSize ++; + if (loadedSize > 1000) { + saveData(); + streamPushItemForSave.clear(); + loadedSize = 0; + } + + } + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) { + // 这里也要保存数据,确保最后遗留的数据也存储到数据库 + saveData(); + streamPushItemForSave.clear(); + gBMap.clear(); + errorDataHandler.handle(errorStreamList, errorInfoList); + } + + private void saveData(){ + if (!streamPushItemForSave.isEmpty()) { + // 向数据库查询是否存在重复的app + pushService.batchAdd(new ArrayList<>(streamPushItemForSave.values())); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/streamPush/service/IStreamPushPlayService.java b/src/main/java/com/genersoft/iot/vmp/streamPush/service/IStreamPushPlayService.java new file mode 100644 index 000000000..1881c19b9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamPush/service/IStreamPushPlayService.java @@ -0,0 +1,8 @@ +package com.genersoft.iot.vmp.streamPush.service; + +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.service.bean.ErrorCallback; + +public interface IStreamPushPlayService { + void start(Integer id, ErrorCallback callback, String platformDeviceId, String platformName ); +} diff --git a/src/main/java/com/genersoft/iot/vmp/streamPush/service/IStreamPushService.java b/src/main/java/com/genersoft/iot/vmp/streamPush/service/IStreamPushService.java new file mode 100755 index 000000000..23dae58c3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamPush/service/IStreamPushService.java @@ -0,0 +1,100 @@ +package com.genersoft.iot.vmp.streamPush.service; + +import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis; +import com.genersoft.iot.vmp.streamPush.bean.StreamPush; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; +import com.github.pagehelper.PageInfo; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author lin + */ +public interface IStreamPushService { + + /** + * 获取 + */ + PageInfo getPushList(Integer page, Integer count, String query, Boolean pushing, String mediaServerId); + + List getPushList(String mediaSererId); + + StreamPush getPush(String app, String streamId); + + boolean stop(StreamPush streamPush); + + /** + * 停止一路推流 + * @param app 应用名 + * @param stream 流ID + */ + boolean stopByAppAndStream(String app, String stream); + + /** + * 新的节点加入 + */ + void zlmServerOnline(String mediaServerId); + + /** + * 节点离线 + */ + void zlmServerOffline(String mediaServerId); + + /** + * 批量添加 + */ + void batchAdd(List streamPushExcelDtoList); + + + /** + * 全部离线 + */ + void allOffline(); + + /** + * 推流离线 + */ + void offline(List offlineStreams); + + /** + * 推流上线 + */ + void online(List onlineStreams); + + /** + * 增加推流 + */ + boolean add(StreamPush stream); + + boolean update(StreamPush stream); + + /** + * 获取全部的app+Streanm 用于判断推流列表是新增还是修改 + * @return + */ + List getAllAppAndStream(); + + /** + * 获取统计信息 + * @return + */ + ResourceBaseInfo getOverview(); + + Map getAllAppAndStreamMap(); + + Map getAllGBId(); + + void updateStatus(StreamPush push); + + void deleteByAppAndStream(String app, String stream); + + void updatePushStatus(StreamPush streamPush, boolean pushIng); + + void batchUpdate(List streamPushItemForUpdate); + + int delete(int id); + + void batchRemove(Set ids); +} diff --git a/src/main/java/com/genersoft/iot/vmp/streamPush/service/impl/StreamPushPlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/streamPush/service/impl/StreamPushPlayServiceImpl.java new file mode 100644 index 000000000..7268b28c1 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamPush/service/impl/StreamPushPlayServiceImpl.java @@ -0,0 +1,103 @@ +package com.genersoft.iot.vmp.streamPush.service.impl; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.DynamicTask; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.media.bean.MediaInfo; +import com.genersoft.iot.vmp.media.service.IMediaServerService; +import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; +import com.genersoft.iot.vmp.service.bean.ErrorCallback; +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; +import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService; +import com.genersoft.iot.vmp.service.redisMsg.RedisPushStreamResponseListener; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.streamPush.bean.StreamPush; +import com.genersoft.iot.vmp.streamPush.dao.StreamPushMapper; +import com.genersoft.iot.vmp.streamPush.service.IStreamPushPlayService; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +import java.util.UUID; + +@Service +@Slf4j +@DS("master") +public class StreamPushPlayServiceImpl implements IStreamPushPlayService { + + @Autowired + private StreamPushMapper streamPushMapper; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private UserSetting userSetting; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private IRedisRpcService redisRpcService; + + @Autowired + private RedisPushStreamResponseListener redisPushStreamResponseListener; + + @Override + public void start(Integer id, ErrorCallback callback, String platformDeviceId, String platformName ) { + StreamPush streamPush = streamPushMapper.queryOne(id); + Assert.notNull(streamPush, "推流信息未找到"); + MediaInfo mediaInfo = redisCatchStorage.getPushListItem(streamPush.getApp(), streamPush.getStream()); + if (mediaInfo != null) { + String callId = null; + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(streamPush.getApp(), streamPush.getStream()); + if (streamAuthorityInfo != null) { + callId = streamAuthorityInfo.getCallId(); + } + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), mediaServerService.getStreamInfoByAppAndStream(mediaInfo.getMediaServer(), + streamPush.getApp(), streamPush.getStream(), mediaInfo, callId)); + return; + } + Assert.isTrue(streamPush.isStartOfflinePush(), "通道未推流"); + // 发送redis消息以使设备上线,流上线后被 + log.info("[ app={}, stream={} ]通道未推流,发送redis信息控制设备开始推流", streamPush.getApp(), streamPush.getStream()); + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, + streamPush.getApp(), streamPush.getStream(), streamPush.getGbDeviceId(), platformDeviceId, + platformName, userSetting.getServerId(), null); + redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); + // 设置超时 + String timeOutTaskKey = UUID.randomUUID().toString(); + dynamicTask.startDelay(timeOutTaskKey, () -> { + redisRpcService.unPushStreamOnlineEvent(streamPush.getApp(), streamPush.getStream()); + log.info("[ app={}, stream={} ] 等待设备开始推流超时", streamPush.getApp(), streamPush.getStream()); + callback.run(ErrorCode.ERROR100.getCode(), "timeout", null); + + }, userSetting.getPlatformPlayTimeout()); + // + long key = redisRpcService.onStreamOnlineEvent(streamPush.getApp(), streamPush.getStream(), (streamInfo) -> { + dynamicTask.stop(timeOutTaskKey); + if (streamInfo == null) { + log.warn("等待推流得到结果未空: {}/{}", streamPush.getApp(), streamPush.getStream()); + callback.run(ErrorCode.ERROR100.getCode(), "fail", null); + }else { + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo); + } + }); + // 添加回复的拒绝或者错误的通知 + // redis消息例如: PUBLISH VM_MSG_STREAM_PUSH_RESPONSE '{"code":1,"msg":"失败","app":"1","stream":"2"}' + redisPushStreamResponseListener.addEvent(streamPush.getApp(), streamPush.getStream(), response -> { + if (response.getCode() != 0) { + dynamicTask.stop(timeOutTaskKey); + redisRpcService.unPushStreamOnlineEvent(streamPush.getApp(), streamPush.getStream()); + redisRpcService.removeCallback(key); + callback.run(response.getCode(), response.getMsg(), null); + } + }); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/streamPush/service/impl/StreamPushServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/streamPush/service/impl/StreamPushServiceImpl.java new file mode 100755 index 000000000..aeabcc839 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/streamPush/service/impl/StreamPushServiceImpl.java @@ -0,0 +1,586 @@ +package com.genersoft.iot.vmp.streamPush.service.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; +import com.genersoft.iot.vmp.media.bean.MediaInfo; +import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent; +import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent; +import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOfflineEvent; +import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOnlineEvent; +import com.genersoft.iot.vmp.media.service.IMediaServerService; +import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; +import com.genersoft.iot.vmp.media.zlm.dto.hook.OriginType; +import com.genersoft.iot.vmp.service.ISendRtpServerService; +import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.streamPush.bean.StreamPush; +import com.genersoft.iot.vmp.streamPush.dao.StreamPushMapper; +import com.genersoft.iot.vmp.streamPush.service.IStreamPushService; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; +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.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +import java.util.*; + +@Service +@Slf4j +@DS("master") +public class StreamPushServiceImpl implements IStreamPushService { + + @Autowired + private StreamPushMapper streamPushMapper; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private ISendRtpServerService sendRtpServerService; + + @Autowired + private IGbChannelService gbChannelService; + + /** + * 流到来的处理 + */ + @Async("taskExecutor") + @EventListener + @Transactional + public void onApplicationEvent(MediaArrivalEvent event) { + MediaInfo mediaInfo = event.getMediaInfo(); + if (mediaInfo == null) { + return; + } + if (mediaInfo.getOriginType() != OriginType.RTMP_PUSH.ordinal() + && mediaInfo.getOriginType() != OriginType.RTSP_PUSH.ordinal() + && mediaInfo.getOriginType() != OriginType.RTC_PUSH.ordinal()) { + return; + } + + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(event.getApp(), event.getStream()); + if (streamAuthorityInfo == null) { + streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(event); + } else { + streamAuthorityInfo.setOriginType(mediaInfo.getOriginType()); + } + redisCatchStorage.updateStreamAuthorityInfo(event.getApp(), event.getStream(), streamAuthorityInfo); + + StreamPush streamPushInDb = getPush(event.getApp(), event.getStream()); + if (streamPushInDb == null) { + StreamPush streamPush = StreamPush.getInstance(event, userSetting.getServerId()); + streamPush.setPushing(true); + streamPush.setUpdateTime(DateUtil.getNow()); + streamPush.setPushTime(DateUtil.getNow()); + add(streamPush); + }else { + updatePushStatus(streamPushInDb, true); + } + // 冗余数据,自己系统中自用 + if (!"broadcast".equals(event.getApp()) && !"talk".equals(event.getApp())) { + redisCatchStorage.addPushListItem(event.getApp(), event.getStream(), event.getMediaInfo()); + } + + // 发送流变化redis消息 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("serverId", userSetting.getServerId()); + jsonObject.put("app", event.getApp()); + jsonObject.put("stream", event.getStream()); + jsonObject.put("register", true); + jsonObject.put("mediaServerId", event.getMediaServer().getId()); + redisCatchStorage.sendStreamChangeMsg(OriginType.values()[event.getMediaInfo().getOriginType()].getType(), jsonObject); + } + + /** + * 流离开的处理 + */ + @Async("taskExecutor") + @EventListener + @Transactional + public void onApplicationEvent(MediaDepartureEvent event) { + + // 兼容流注销时类型从redis记录获取 + MediaInfo mediaInfo = redisCatchStorage.getStreamInfo( + event.getApp(), event.getStream(), event.getMediaServer().getId()); + if (mediaInfo != null) { + String type = OriginType.values()[mediaInfo.getOriginType()].getType(); + redisCatchStorage.removeStream(event.getMediaServer().getId(), type, event.getApp(), event.getStream()); + if ("PUSH".equalsIgnoreCase(type)) { + // 冗余数据,自己系统中自用 + redisCatchStorage.removePushListItem(event.getApp(), event.getStream(), event.getMediaServer().getId()); + } + if (type != null) { + // 发送流变化redis消息 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("serverId", userSetting.getServerId()); + jsonObject.put("app", event.getApp()); + jsonObject.put("stream", event.getStream()); + jsonObject.put("register", false); + jsonObject.put("mediaServerId", event.getMediaServer().getId()); + redisCatchStorage.sendStreamChangeMsg(type, jsonObject); + } + } + StreamPush streamPush = getPush(event.getApp(), event.getStream()); + if (streamPush == null) { + return; + } + if (streamPush.getGbDeviceId() != null) { + updatePushStatus(streamPush, false); + }else { + deleteByAppAndStream(event.getApp(), event.getStream()); + } + } + + /** + * 流媒体节点上线 + */ + @Async("taskExecutor") + @EventListener + @Transactional + public void onApplicationEvent(MediaServerOnlineEvent event) { + zlmServerOnline(event.getMediaServerId()); + } + + /** + * 流媒体节点离线 + */ + @Async("taskExecutor") + @EventListener + @Transactional + public void onApplicationEvent(MediaServerOfflineEvent event) { + zlmServerOffline(event.getMediaServerId()); + } + + @Override + public PageInfo getPushList(Integer page, Integer count, String query, Boolean pushing, String mediaServerId) { + PageHelper.startPage(page, count); + List all = streamPushMapper.selectAll(query, pushing, mediaServerId); + return new PageInfo<>(all); + } + + @Override + public List getPushList(String mediaServerId) { + return streamPushMapper.selectAllByMediaServerIdWithOutGbID(mediaServerId); + } + + + @Override + public StreamPush getPush(String app, String stream) { + return streamPushMapper.selectByAppAndStream(app, stream); + } + + @Override + @Transactional + public boolean add(StreamPush stream) { + log.info("[添加推流] app: {}, stream: {}, 国标编号: {}", stream.getApp(), stream.getStream(), stream.getGbDeviceId()); + StreamPush streamPushInDb = streamPushMapper.selectByAppAndStream(stream.getApp(), stream.getStream()); + if (streamPushInDb != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "应用名+流ID已存在"); + } + stream.setUpdateTime(DateUtil.getNow()); + stream.setCreateTime(DateUtil.getNow()); + int addResult = streamPushMapper.add(stream); + if (addResult <= 0) { + return false; + } + if (ObjectUtils.isEmpty(stream.getGbDeviceId())) { + return true; + } + CommonGBChannel channel = gbChannelService.queryByDeviceId(stream.getGbDeviceId()); + if (channel != null) { + log.info("[添加推流]失败,国标编号已存在: {} app: {}, stream: {}, ", stream.getGbDeviceId(), stream.getApp(), stream.getStream()); + } + int addChannelResult = gbChannelService.add(stream.buildCommonGBChannel()); + return addChannelResult > 0; + } + + @Override + @Transactional + public void deleteByAppAndStream(String app, String stream) { + log.info("[删除推流] app: {}, stream: {}, ", app, stream); + StreamPush streamPush = streamPushMapper.selectByAppAndStream(app, stream); + if (streamPush == null) { + log.info("[删除推流]失败, 不存在 app: {}, stream: {}, ", app, stream); + return; + } + if (streamPush.isPushing()) { + stop(streamPush); + } + if (streamPush.getGbId() > 0) { + gbChannelService.delete(streamPush.getGbId()); + } + streamPushMapper.del(streamPush.getId()); + } + @Override + @Transactional + public boolean update(StreamPush streamPush) { + Assert.notNull(streamPush, "推流信息不可为NULL"); + Assert.isTrue(streamPush.getId() > 0, "推流信息ID必须存在"); + log.info("[更新推流]:id: {}, app: {}, stream: {}, ", streamPush.getId(), streamPush.getApp(), streamPush.getStream()); + StreamPush streamPushInDb = streamPushMapper.queryOne(streamPush.getId()); + if (!streamPushInDb.getApp().equals(streamPush.getApp()) || !streamPushInDb.getStream().equals(streamPush.getStream())) { + // app或者stream变化 + StreamPush streamPushInDbForAppAndStream = streamPushMapper.selectByAppAndStream(streamPush.getApp(), streamPush.getStream()); + if (streamPushInDbForAppAndStream != null && !streamPushInDbForAppAndStream.getId().equals(streamPush.getId())) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "应用名+流ID已存在"); + } + } + streamPush.setUpdateTime(DateUtil.getNow()); + streamPushMapper.update(streamPush); + if (streamPush.getGbId() > 0) { + gbChannelService.update(streamPush.buildCommonGBChannel()); + } + return true; + } + + + @Override + @Transactional + public boolean stop(StreamPush streamPush) { + log.info("[主动停止推流] id: {}, app: {}, stream: {}, ", streamPush.getId(), streamPush.getApp(), streamPush.getStream()); + MediaServer mediaServer = null; + if (streamPush.getMediaServerId() == null) { + log.info("[主动停止推流]未找到使用MediaServer,开始自动检索 id: {}, app: {}, stream: {}, ", streamPush.getId(), streamPush.getApp(), streamPush.getStream()); + mediaServer = mediaServerService.getMediaServerByAppAndStream(streamPush.getApp(), streamPush.getStream()); + if (mediaServer != null) { + log.info("[主动停止推流] 检索到MediaServer为{}, id: {}, app: {}, stream: {}, ", mediaServer.getId(), streamPush.getId(), streamPush.getApp(), streamPush.getStream()); + }else { + log.info("[主动停止推流]未找到使用MediaServer id: {}, app: {}, stream: {}, ", streamPush.getId(), streamPush.getApp(), streamPush.getStream()); + } + }else { + mediaServer = mediaServerService.getOne(streamPush.getMediaServerId()); + if (mediaServer == null) { + log.info("[主动停止推流]未找到使用的MediaServer: {},开始自动检索 id: {}, app: {}, stream: {}, ",streamPush.getMediaServerId(), streamPush.getId(), streamPush.getApp(), streamPush.getStream()); + mediaServer = mediaServerService.getMediaServerByAppAndStream(streamPush.getApp(), streamPush.getStream()); + if (mediaServer != null) { + log.info("[主动停止推流] 检索到MediaServer为{}, id: {}, app: {}, stream: {}, ", mediaServer.getId(), streamPush.getId(), streamPush.getApp(), streamPush.getStream()); + }else { + log.info("[主动停止推流]未找到使用MediaServer id: {}, app: {}, stream: {}, ", streamPush.getId(), streamPush.getApp(), streamPush.getStream()); + } + } + } + if (mediaServer != null) { + mediaServerService.closeStreams(mediaServer, streamPush.getApp(), streamPush.getStream()); + } + streamPush.setPushing(false); + if (userSetting.isUsePushingAsStatus()) { + CommonGBChannel commonGBChannel = streamPush.buildCommonGBChannel(); + if (commonGBChannel != null) { + gbChannelService.offline(commonGBChannel); + } + } + sendRtpServerService.deleteByStream(streamPush.getStream()); + mediaServerService.stopSendRtp(mediaServer, streamPush.getApp(), streamPush.getStream(), null); + streamPush.setUpdateTime(DateUtil.getNow()); + streamPushMapper.update(streamPush); + return true; + } + + @Override + @Transactional + public boolean stopByAppAndStream(String app, String stream) { + log.info("[主动停止推流] : app: {}, stream: {}, ", app, stream); + StreamPush streamPushItem = streamPushMapper.selectByAppAndStream(app, stream); + if (streamPushItem != null) { + stop(streamPushItem); + } + return true; + } + + @Override + @Transactional + public void zlmServerOnline(String mediaServerId) { + // 同步zlm推流信息 + MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId); + if (mediaServerItem == null) { + return; + } + // 数据库记录 + List pushList = getPushList(mediaServerId); + Map pushItemMap = new HashMap<>(); + // redis记录 + List mediaInfoList = redisCatchStorage.getStreams(mediaServerId, "PUSH"); + Map streamInfoPushItemMap = new HashMap<>(); + if (!pushList.isEmpty()) { + for (StreamPush streamPushItem : pushList) { + if (ObjectUtils.isEmpty(streamPushItem.getGbId())) { + pushItemMap.put(streamPushItem.getApp() + streamPushItem.getStream(), streamPushItem); + } + } + } + if (!mediaInfoList.isEmpty()) { + for (MediaInfo mediaInfo : mediaInfoList) { + streamInfoPushItemMap.put(mediaInfo.getApp() + mediaInfo.getStream(), mediaInfo); + } + } + // 获取所有推流鉴权信息,清理过期的 + List allStreamAuthorityInfo = redisCatchStorage.getAllStreamAuthorityInfo(); + Map streamAuthorityInfoInfoMap = new HashMap<>(); + for (StreamAuthorityInfo streamAuthorityInfo : allStreamAuthorityInfo) { + streamAuthorityInfoInfoMap.put(streamAuthorityInfo.getApp() + streamAuthorityInfo.getStream(), streamAuthorityInfo); + } + List mediaList = mediaServerService.getMediaList(mediaServerItem, null, null, null); + if (mediaList == null) { + return; + } + List streamPushItems = handleJSON(mediaList); + if (streamPushItems != null) { + for (StreamPush streamPushItem : streamPushItems) { + pushItemMap.remove(streamPushItem.getApp() + streamPushItem.getStream()); + streamInfoPushItemMap.remove(streamPushItem.getApp() + streamPushItem.getStream()); + streamAuthorityInfoInfoMap.remove(streamPushItem.getApp() + streamPushItem.getStream()); + } + } + List changedStreamPushList = new ArrayList<>(pushItemMap.values()); + if (!changedStreamPushList.isEmpty()) { + for (StreamPush streamPush : changedStreamPushList) { + stop(streamPush); + } + } + + Collection mediaInfos = streamInfoPushItemMap.values(); + if (!mediaInfos.isEmpty()) { + String type = "PUSH"; + for (MediaInfo mediaInfo : mediaInfos) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("serverId", userSetting.getServerId()); + jsonObject.put("app", mediaInfo.getApp()); + jsonObject.put("stream", mediaInfo.getStream()); + jsonObject.put("register", false); + jsonObject.put("mediaServerId", mediaServerId); + redisCatchStorage.sendStreamChangeMsg(type, jsonObject); + // 移除redis内流的信息 + redisCatchStorage.removeStream(mediaServerItem.getId(), "PUSH", mediaInfo.getApp(), mediaInfo.getStream()); + // 冗余数据,自己系统中自用 + redisCatchStorage.removePushListItem(mediaInfo.getApp(), mediaInfo.getStream(), mediaServerItem.getId()); + } + } + + Collection streamAuthorityInfos = streamAuthorityInfoInfoMap.values(); + if (!streamAuthorityInfos.isEmpty()) { + for (StreamAuthorityInfo streamAuthorityInfo : streamAuthorityInfos) { + // 移除redis内流的信息 + redisCatchStorage.removeStreamAuthorityInfo(streamAuthorityInfo.getApp(), streamAuthorityInfo.getStream()); + } + } + } + + @Override + @Transactional + public void zlmServerOffline(String mediaServerId) { + List streamPushItems = streamPushMapper.selectAllByMediaServerId(mediaServerId); + if (!streamPushItems.isEmpty()) { + for (StreamPush streamPushItem : streamPushItems) { + stop(streamPushItem); + } + } +// // 移除没有GBId的推流 +// streamPushMapper.deleteWithoutGBId(mediaServerId); +// // 其他的流设置未启用 +// streamPushMapper.updateStatusByMediaServerId(mediaServerId, false); +// streamProxyMapper.updateStatusByMediaServerId(mediaServerId, false); + // 发送流停止消息 + String type = "PUSH"; + // 发送redis消息 + List mediaInfoList = redisCatchStorage.getStreams(mediaServerId, type); + if (!mediaInfoList.isEmpty()) { + for (MediaInfo mediaInfo : mediaInfoList) { + // 移除redis内流的信息 + redisCatchStorage.removeStream(mediaServerId, type, mediaInfo.getApp(), mediaInfo.getStream()); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("serverId", userSetting.getServerId()); + jsonObject.put("app", mediaInfo.getApp()); + jsonObject.put("stream", mediaInfo.getStream()); + jsonObject.put("register", false); + jsonObject.put("mediaServerId", mediaServerId); + redisCatchStorage.sendStreamChangeMsg(type, jsonObject); + + // 冗余数据,自己系统中自用 + redisCatchStorage.removePushListItem(mediaInfo.getApp(), mediaInfo.getStream(), mediaServerId); + } + } + } + + @Override + @Transactional + public void batchAdd(List streamPushItems) { + streamPushMapper.addAll(streamPushItems); + List commonGBChannels = new ArrayList<>(); + for (StreamPush streamPush : streamPushItems) { + if (!ObjectUtils.isEmpty(streamPush.getGbDeviceId())) { + commonGBChannels.add(streamPush.buildCommonGBChannel()); + } + } + gbChannelService.batchAdd(commonGBChannels); + } + + @Override + public void allOffline() { + List streamPushList = streamPushMapper.selectAll(null, null, null); + if (streamPushList.isEmpty()) { + return; + } + List commonGBChannelList = new ArrayList<>(); + for (StreamPush streamPush : streamPushList) { + CommonGBChannel commonGBChannel = streamPush.buildCommonGBChannel(); + if (commonGBChannel != null) { + commonGBChannelList.add(streamPush.buildCommonGBChannel()); + } + } + gbChannelService.offline(commonGBChannelList); + } + + @Override + public void offline(List offlineStreams) { + // 更新部分设备离线 + List streamPushList = streamPushMapper.getListFromRedis(offlineStreams); + List commonGBChannelList = gbChannelService.queryListByStreamPushList(streamPushList); + gbChannelService.offline(commonGBChannelList); + } + + @Override + public void online(List onlineStreams) { + // 更新部分设备上线streamPushService + List streamPushList = streamPushMapper.getListFromRedis(onlineStreams); + List commonGBChannelList = gbChannelService.queryListByStreamPushList(streamPushList); + gbChannelService.online(commonGBChannelList); + } + + @Override + public List getAllAppAndStream() { + return streamPushMapper.getAllAppAndStream(); + } + + @Override + public ResourceBaseInfo getOverview() { + int total = streamPushMapper.getAllCount(); + int online = streamPushMapper.getAllPushing(userSetting.isUsePushingAsStatus()); + + return new ResourceBaseInfo(total, online); + } + + @Override + public Map getAllAppAndStreamMap() { + return streamPushMapper.getAllAppAndStreamMap(); + } + + @Override + public Map getAllGBId() { + return streamPushMapper.getAllGBId(); + } + + @Override + public void updateStatus(StreamPush push) { + + } + + + + @Override + @Transactional + public void updatePushStatus(StreamPush streamPush, boolean pushIng) { + streamPush.setPushing(pushIng); + if (userSetting.isUsePushingAsStatus()) { + streamPush.setGbStatus(pushIng?"ON":"OFF"); + } + streamPush.setPushTime(DateUtil.getNow()); + streamPushMapper.updatePushStatus(streamPush.getId(), pushIng); + if (ObjectUtils.isEmpty(streamPush.getGbDeviceId())) { + return; + } + if (userSetting.isUsePushingAsStatus()) { + if ("ON".equalsIgnoreCase(streamPush.getGbStatus()) ) { + gbChannelService.online(streamPush.buildCommonGBChannel()); + }else { + gbChannelService.offline(streamPush.buildCommonGBChannel()); + } + } + } + + private List handleJSON(List streamInfoList) { + if (streamInfoList == null || streamInfoList.isEmpty()) { + return null; + } + Map result = new HashMap<>(); + for (StreamInfo streamInfo : streamInfoList) { + // 不保存国标推理以及拉流代理的流 + if (streamInfo.getOriginType() == OriginType.RTSP_PUSH.ordinal() + || streamInfo.getOriginType() == OriginType.RTMP_PUSH.ordinal() + || streamInfo.getOriginType() == OriginType.RTC_PUSH.ordinal() ) { + String key = streamInfo.getApp() + "_" + streamInfo.getStream(); + StreamPush streamPushItem = result.get(key); + if (streamPushItem == null) { + streamPushItem = streamPushItem.getInstance(streamInfo); + result.put(key, streamPushItem); + } + } + } + return new ArrayList<>(result.values()); + } + + @Override + public void batchUpdate(List streamPushItemForUpdate) { + streamPushMapper.batchUpdate(streamPushItemForUpdate); + List commonGBChannels = new ArrayList<>(); + for (StreamPush streamPush : streamPushItemForUpdate) { + if (!ObjectUtils.isEmpty(streamPush.getGbDeviceId())) { + commonGBChannels.add(streamPush.buildCommonGBChannel()); + } + } + gbChannelService.batchUpdate(commonGBChannels); + } + + @Override + @Transactional + public int delete(int id) { + StreamPush streamPush = streamPushMapper.queryOne(id); + if (streamPush == null) { + return 0; + } + if(streamPush.isPushing()) { + MediaServer mediaServer = mediaServerService.getOne(streamPush.getMediaServerId()); + mediaServerService.closeStreams(mediaServer, streamPush.getApp(), streamPush.getStream()); + } + if (streamPush.getGbDeviceId() != null) { + gbChannelService.delete(streamPush.getGbId()); + } + return streamPushMapper.del(id); + } + + @Override + @Transactional + public void batchRemove(Set ids) { + List streamPushList = streamPushMapper.selectInSet(ids); + if (streamPushList.isEmpty()) { + return; + } + List commonGBChannelList = new ArrayList<>(); + streamPushList.stream().forEach(streamPush -> { + if (streamPush.getGbDeviceId() != null) { + commonGBChannelList.add(streamPush.buildCommonGBChannel()); + } + }); + streamPushMapper.batchDel(streamPushList); + gbChannelService.delete(ids); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/CivilCodeUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/CivilCodeUtil.java index 5be42b5bc..33c3e47d9 100644 --- a/src/main/java/com/genersoft/iot/vmp/utils/CivilCodeUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/utils/CivilCodeUtil.java @@ -1,18 +1,19 @@ package com.genersoft.iot.vmp.utils; import com.genersoft.iot.vmp.common.CivilCodePo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.genersoft.iot.vmp.gb28181.bean.Region; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.ObjectUtils; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +@Slf4j public enum CivilCodeUtil { INSTANCE; - private final static Logger log = LoggerFactory.getLogger(CivilCodeUtil.class); - // 用与消息的缓存 private final Map civilCodeMap = new ConcurrentHashMap<>(); @@ -27,6 +28,10 @@ public enum CivilCodeUtil { } } + public void add(CivilCodePo civilCodePo) { + civilCodeMap.put(civilCodePo.getCode(), civilCodePo); + } + public CivilCodePo get(String code) { return civilCodeMap.get(code); } @@ -50,4 +55,49 @@ public enum CivilCodeUtil { return civilCodeMap.get(parentCode); } } + + public CivilCodePo getCivilCodePo(String code) { + if (code.length() > 8) { + return null; + }else { + return civilCodeMap.get(code); + } + } + + public List getAllParentCode(String civilCode) { + List civilCodePoList = new ArrayList<>(); + CivilCodePo parentCode = getParentCode(civilCode); + if (parentCode != null) { + civilCodePoList.add(parentCode); + List allParentCode = getAllParentCode(parentCode.getCode()); + if (!allParentCode.isEmpty()) { + civilCodePoList.addAll(allParentCode); + }else { + return civilCodePoList; + } + } + return civilCodePoList; + } + + public boolean isEmpty() { + return civilCodeMap.isEmpty(); + } + + public int size() { + return civilCodeMap.size(); + } + + public List getAllChild(String parent) { + List result = new ArrayList<>(); + for (String key : civilCodeMap.keySet()) { + if (parent == null) { + if (ObjectUtils.isEmpty(civilCodeMap.get(key).getParentCode().trim())) { + result.add(Region.getInstance(key, civilCodeMap.get(key).getName(), civilCodeMap.get(key).getParentCode())); + } + }else if (civilCodeMap.get(key).getParentCode().equals(parent)) { + result.add(Region.getInstance(key, civilCodeMap.get(key).getName(), civilCodeMap.get(key).getParentCode())); + } + } + return result; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/utils/GpsUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/GpsUtil.java index 2d9bd39b1..2fdb26267 100755 --- a/src/main/java/com/genersoft/iot/vmp/utils/GpsUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/utils/GpsUtil.java @@ -1,18 +1,13 @@ package com.genersoft.iot.vmp.utils; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.Socket; +import com.genersoft.iot.vmp.gb28181.bean.BaiduPoint; +import lombok.extern.slf4j.Slf4j; + import java.util.Base64; -import com.genersoft.iot.vmp.gb28181.bean.BaiduPoint; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +@Slf4j public class GpsUtil { - private static Logger logger = LoggerFactory.getLogger(GpsUtil.class); public static BaiduPoint Wgs84ToBd09(String xx, String yy) { diff --git a/src/main/java/com/genersoft/iot/vmp/utils/SystemInfoUtils.java b/src/main/java/com/genersoft/iot/vmp/utils/SystemInfoUtils.java index ec2f3b222..534384e11 100755 --- a/src/main/java/com/genersoft/iot/vmp/utils/SystemInfoUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/utils/SystemInfoUtils.java @@ -1,16 +1,14 @@ package com.genersoft.iot.vmp.utils; -import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.ObjectUtils; +import lombok.extern.slf4j.Slf4j; import oshi.SystemInfo; -import oshi.hardware.*; +import oshi.hardware.CentralProcessor; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.hardware.NetworkIF; import oshi.software.os.OperatingSystem; -import oshi.util.FormatUtil; import java.io.File; -import java.text.DecimalFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -22,10 +20,9 @@ import java.util.concurrent.TimeUnit; * 版权声明:本文为xiaozhangnomoney原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明 * 原文出处链接:https://blog.csdn.net/xiaozhangnomoney/article/details/107769147 */ +@Slf4j public class SystemInfoUtils { - private final static Logger logger = LoggerFactory.getLogger(SystemInfoUtils.class); - /** * 获取cpu信息 * @return @@ -78,7 +75,7 @@ public class SystemInfoUtils { try { Thread.sleep(1000); } catch (InterruptedException e) { - logger.error("[线程休眠失败] : {}", e.getMessage()); + log.error("[线程休眠失败] : {}", e.getMessage()); } List afterNetworkIFs = hal.getNetworkIFs(); NetworkIF afterNet = afterNetworkIFs.get(afterNetworkIFs.size() - 1); diff --git a/src/main/java/com/genersoft/iot/vmp/utils/UJson.java b/src/main/java/com/genersoft/iot/vmp/utils/UJson.java index a2d403392..e39147c61 100755 --- a/src/main/java/com/genersoft/iot/vmp/utils/UJson.java +++ b/src/main/java/com/genersoft/iot/vmp/utils/UJson.java @@ -4,9 +4,8 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.Iterator; import java.util.Map; @@ -17,9 +16,9 @@ import java.util.Objects; * @version 1.0 * @date 2022/3/11 10:17 */ +@Slf4j public class UJson { - private static Logger logger = LoggerFactory.getLogger(UJson.class); public static final ObjectMapper JSON_MAPPER = new ObjectMapper(); static { @@ -39,7 +38,7 @@ public class UJson { try { this.node = JSON_MAPPER.readValue(json, ObjectNode.class); }catch (Exception e){ - logger.error(e.getMessage(), e); + log.error(e.getMessage(), e); this.node = JSON_MAPPER.createObjectNode(); } } @@ -90,7 +89,7 @@ public class UJson { try { return JSON_MAPPER.readValue(json, clazz); }catch (Exception e){ - logger.error(e.getMessage(), e); + log.error(e.getMessage(), e); return null; } } @@ -99,7 +98,7 @@ public class UJson { try{ return JSON_MAPPER.writeValueAsString(object); }catch (Exception e){ - logger.error(e.getMessage(), e); + log.error(e.getMessage(), e); return ""; } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/TestController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/TestController.java new file mode 100644 index 000000000..ec4f126b3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/TestController.java @@ -0,0 +1,40 @@ +package com.genersoft.iot.vmp.vmanager; + +import com.genersoft.iot.vmp.conf.security.JwtUtils; +import com.genersoft.iot.vmp.media.event.hook.Hook; +import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/api/test") +public class TestController { + + @Autowired + private HookSubscribe subscribe; + + + @GetMapping("/hook/list") + @Operation(summary = "查询角色", security = @SecurityRequirement(name = JwtUtils.HEADER)) + public List all(){ + return subscribe.getAll(); + } + +// @Bean +// public ServletRegistrationBean druidStatViewServlet() { +// ServletRegistrationBean registrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); +// registrationBean.addInitParameter("allow", "127.0.0.1");// IP白名单 (没有配置或者为空,则允许所有访问) +// registrationBean.addInitParameter("deny", "");// IP黑名单 (存在共同时,deny优先于allow) +// registrationBean.addInitParameter("loginUsername", "admin"); +// registrationBean.addInitParameter("loginPassword", "admin"); +// registrationBean.addInitParameter("resetEnable", "false"); +// return registrationBean; +// } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/BaseTree.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/BaseTree.java deleted file mode 100755 index 23690e545..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/BaseTree.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.bean; - -import org.jetbrains.annotations.NotNull; - -import java.text.Collator; -import java.util.Comparator; - -/** - * @author lin - */ -public class BaseTree implements Comparable{ - private String id; - - private String deviceId; - private String pid; - private String name; - private boolean parent; - - private T basicData; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getPid() { - return pid; - } - - public void setPid(String pid) { - this.pid = pid; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public T getBasicData() { - return basicData; - } - - public void setBasicData(T basicData) { - this.basicData = basicData; - } - - public boolean isParent() { - return parent; - } - - public void setParent(boolean parent) { - this.parent = parent; - } - - @Override - public int compareTo(@NotNull BaseTree treeNode) { - if (this.parent || treeNode.isParent()) { - if (!this.parent && !treeNode.isParent()) { - Comparator cmp = Collator.getInstance(java.util.Locale.CHINA); - return cmp.compare(treeNode.getName(), this.getName()); - }else { - if (this.isParent()) { - return 1; - }else { - return -1; - } - } - }else{ - Comparator cmp = Collator.getInstance(java.util.Locale.CHINA); - return cmp.compare(treeNode.getName(), this.getName()); - } - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java index ce7425127..e484dafa6 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java @@ -172,8 +172,10 @@ public class StreamContent { if (streamInfo.getRtcs() != null) { this.rtcs = streamInfo.getRtcs().getUrl(); } + if (streamInfo.getMediaServer() != null) { + this.mediaServerId = streamInfo.getMediaServer().getId(); + } - this.mediaServerId = streamInfo.getMediaServerId(); this.mediaInfo = streamInfo.getMediaInfo(); this.startTime = streamInfo.getStartTime(); this.endTime = streamInfo.getEndTime(); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamPushExcelDto.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamPushExcelDto.java deleted file mode 100755 index dcec60768..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamPushExcelDto.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.bean; - -import com.alibaba.excel.annotation.ExcelProperty; - -public class StreamPushExcelDto { - - @ExcelProperty("名称") - private String name; - - @ExcelProperty("应用名") - private String app; - - @ExcelProperty("流ID") - private String stream; - - @ExcelProperty("国标ID") - private String gbId; - - @ExcelProperty("平台ID") - private String platformId; - - @ExcelProperty("目录ID") - private String catalogId; - - @ExcelProperty("在线状态") - private boolean status; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - 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 getGbId() { - return gbId; - } - - public void setGbId(String gbId) { - this.gbId = gbId; - } - - - public String getPlatformId() { - return platformId; - } - - public void setPlatformId(String platformId) { - this.platformId = platformId; - } - - public String getCatalogId() { - return catalogId; - } - - public void setCatalogId(String catalogId) { - this.catalogId = catalogId; - } - - public boolean isStatus() { - return status; - } - - public boolean getStatus() { - return status; - } - - public void setStatus(boolean status) { - this.status = status; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/PageInfo.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/TablePageInfo.java similarity index 94% rename from src/main/java/com/genersoft/iot/vmp/vmanager/bean/PageInfo.java rename to src/main/java/com/genersoft/iot/vmp/vmanager/bean/TablePageInfo.java index 8894191ab..73d42235a 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/PageInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/TablePageInfo.java @@ -3,7 +3,7 @@ package com.genersoft.iot.vmp.vmanager.bean; import java.util.ArrayList; import java.util.List; -public class PageInfo { +public class TablePageInfo { //当前页 private int pageNum; //每页的数量 @@ -19,11 +19,11 @@ public class PageInfo { private List list; - public PageInfo(List resultData) { + public TablePageInfo(List resultData) { this.resultData = resultData; } - public PageInfo() { + public TablePageInfo() { } public void startPage(int page, int count) { diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java index ecf3a8dba..f19a9e0b0 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java @@ -4,8 +4,8 @@ import com.alibaba.fastjson2.JSONArray; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.security.JwtUtils; import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.service.ICloudRecordService; import com.genersoft.iot.vmp.media.service.IMediaServerService; +import com.genersoft.iot.vmp.gb28181.service.ICloudRecordService; import com.genersoft.iot.vmp.service.bean.CloudRecordItem; import com.genersoft.iot.vmp.service.bean.DownloadFileInfo; import com.genersoft.iot.vmp.utils.DateUtil; @@ -16,9 +16,8 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -35,14 +34,12 @@ import java.util.zip.ZipOutputStream; @SuppressWarnings("rawtypes") @Tag(name = "云端录像接口") - +@Slf4j @RestController @RequestMapping("/api/cloud/record") public class CloudRecordController { - private final static Logger logger = LoggerFactory.getLogger(CloudRecordController.class); - @Autowired private ICloudRecordService cloudRecordService; @@ -66,7 +63,7 @@ public class CloudRecordController { @RequestParam(required = false) String mediaServerId ) { - logger.info("[云端录像] 查询存在云端录像的日期 app->{}, stream->{}, mediaServerId->{}, year->{}, month->{}", + log.info("[云端录像] 查询存在云端录像的日期 app->{}, stream->{}, mediaServerId->{}, year->{}, month->{}", app, stream, mediaServerId, year, month); Calendar calendar = Calendar.getInstance(); if (ObjectUtils.isEmpty(year)) { @@ -117,7 +114,7 @@ public class CloudRecordController { @RequestParam(required = false) String callId ) { - logger.info("[云端录像] 查询 app->{}, stream->{}, mediaServerId->{}, page->{}, count->{}, startTime->{}, endTime->{}, callId->{}", + log.info("[云端录像] 查询 app->{}, stream->{}, mediaServerId->{}, page->{}, count->{}, startTime->{}, endTime->{}, callId->{}", app, stream, mediaServerId, page, count, startTime, endTime, callId); List mediaServers; @@ -232,7 +229,7 @@ public class CloudRecordController { @RequestParam(required = false) String callId, @RequestParam(required = false) Integer recordId ){ - logger.info("[云端录像] 添加收藏,app={},stream={},mediaServerId={},startTime={},endTime={},callId={},recordId={}", + log.info("[云端录像] 添加收藏,app={},stream={},mediaServerId={},startTime={},endTime={},callId={},recordId={}", app, stream, mediaServerId, startTime, endTime, callId, recordId); if (recordId != null) { return cloudRecordService.changeCollectById(recordId, true); @@ -260,7 +257,7 @@ public class CloudRecordController { @RequestParam(required = false) String callId, @RequestParam(required = false) Integer recordId ){ - logger.info("[云端录像] 移除收藏,app={},stream={},mediaServerId={},startTime={},endTime={},callId={},recordId={}", + log.info("[云端录像] 移除收藏,app={},stream={},mediaServerId={},startTime={},endTime={},callId={},recordId={}", app, stream, mediaServerId, startTime, endTime, callId, recordId); if (recordId != null) { return cloudRecordService.changeCollectById(recordId, false); @@ -306,7 +303,7 @@ public class CloudRecordController { @RequestParam(required = false) List ids ) { - logger.info("[下载指定录像文件的压缩包] 查询 app->{}, stream->{}, mediaServerId->{}, startTime->{}, endTime->{}, callId->{}", + log.info("[下载指定录像文件的压缩包] 查询 app->{}, stream->{}, mediaServerId->{}, startTime->{}, endTime->{}, callId->{}", app, stream, mediaServerId, startTime, endTime, callId); List mediaServers; @@ -367,7 +364,7 @@ public class CloudRecordController { } zos.close(); } catch (IOException e) { - logger.error("[下载指定录像文件的压缩包] 失败: 查询 app->{}, stream->{}, mediaServerId->{}, startTime->{}, endTime->{}, callId->{}", + log.error("[下载指定录像文件的压缩包] 失败: 查询 app->{}, stream->{}, mediaServerId->{}, startTime->{}, endTime->{}, callId->{}", app, stream, mediaServerId, startTime, endTime, callId, e); } } @@ -409,7 +406,7 @@ public class CloudRecordController { @RequestParam(required = false) String remoteHost ) { - logger.info("[云端录像] 查询URL app->{}, stream->{}, mediaServerId->{}, page->{}, count->{}, startTime->{}, endTime->{}, callId->{}", + log.info("[云端录像] 查询URL app->{}, stream->{}, mediaServerId->{}, page->{}, count->{}, startTime->{}, endTime->{}, callId->{}", app, stream, mediaServerId, page, count, startTime, endTime, callId); List mediaServers; diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java deleted file mode 100755 index e65a57950..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.gbStream; - -import com.genersoft.iot.vmp.conf.exception.ControllerException; -import com.genersoft.iot.vmp.conf.security.JwtUtils; -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.service.IGbStreamService; -import com.genersoft.iot.vmp.service.IPlatformService; -import com.genersoft.iot.vmp.service.IStreamPushService; -import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; -import com.genersoft.iot.vmp.vmanager.gb28181.gbStream.bean.GbStreamParam; -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; -import io.swagger.v3.oas.annotations.tags.Tag; -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.*; - -import java.util.List; - -@Tag(name = "视频流关联到级联平台") - -@RestController -@RequestMapping("/api/gbStream") -public class GbStreamController { - - private final static Logger logger = LoggerFactory.getLogger(GbStreamController.class); - - @Autowired - private IGbStreamService gbStreamService; - - @Autowired - private IStreamPushService service; - - @Autowired - private IPlatformService platformService; - - - /** - * 查询国标通道 - * @param page 当前页 - * @param count 每页条数 - * @param platformId 平台ID - * @return - */ - @Operation(summary = "查询国标通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "page", description = "当前页", required = true) - @Parameter(name = "count", description = "每页条数", required = true) - @Parameter(name = "platformId", description = "平台ID", required = true) - @Parameter(name = "catalogId", description = "目录ID") - @Parameter(name = "query", description = "查询内容") - @Parameter(name = "mediaServerId", description = "流媒体ID") - @GetMapping(value = "/list") - @ResponseBody - public PageInfo list(@RequestParam(required = true)Integer page, - @RequestParam(required = true)Integer count, - @RequestParam(required = true)String platformId, - @RequestParam(required = false)String catalogId, - @RequestParam(required = false)String query, - @RequestParam(required = false)String mediaServerId){ - if (ObjectUtils.isEmpty(catalogId)) { - catalogId = null; - } - if (ObjectUtils.isEmpty(query)) { - query = null; - } - if (ObjectUtils.isEmpty(mediaServerId)) { - mediaServerId = null; - } - - // catalogId 为null 查询未在平台下分配的数据 - // catalogId 不为null 查询平台下这个,目录下的通道 - return gbStreamService.getAll(page, count, platformId, catalogId, query, mediaServerId); - } - - - /** - * 移除国标关联 - * @param gbStreamParam - * @return - */ - @Operation(summary = "移除国标关联", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @DeleteMapping(value = "/del") - @ResponseBody - public void del(@RequestBody GbStreamParam gbStreamParam){ - - if (gbStreamParam.getGbStreams() == null || gbStreamParam.getGbStreams().isEmpty()) { - if (gbStreamParam.isAll()) { - gbStreamService.delAllPlatformInfo(gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId()); - } - }else { - gbStreamService.delPlatformInfo(gbStreamParam.getPlatformId(), gbStreamParam.getGbStreams()); - } - - } - - /** - * 保存国标关联 - * @param gbStreamParam - * @return - */ - @Operation(summary = "保存国标关联", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @PostMapping(value = "/add") - @ResponseBody - public void add(@RequestBody GbStreamParam gbStreamParam){ - if (gbStreamParam.getGbStreams() == null || gbStreamParam.getGbStreams().isEmpty()) { - if (gbStreamParam.isAll()) { - List allGBChannels = gbStreamService.getAllGBChannels(gbStreamParam.getPlatformId()); - gbStreamService.addPlatformInfo(allGBChannels, gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId()); - } - }else { - gbStreamService.addPlatformInfo(gbStreamParam.getGbStreams(), gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId()); - } - } - - /** - * 保存国标关联 - * @param gbId - * @return - */ - @Operation(summary = "保存国标关联", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @GetMapping(value = "/addWithGbid") - @ResponseBody - public void add(String gbId, String platformGbId, @RequestParam(required = false) String catalogGbId){ - List gbStreams = gbStreamService.getGbChannelWithGbid(gbId); - if (gbStreams.isEmpty()) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "gbId的信息未找到"); - } - gbStreamService.addPlatformInfo(gbStreams, platformGbId, catalogGbId); - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/bean/GbStreamParam.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/bean/GbStreamParam.java deleted file mode 100755 index 3705c7090..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/bean/GbStreamParam.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.gbStream.bean; - -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import io.swagger.v3.oas.annotations.media.Schema; - -import java.util.List; - -@Schema(description = "国标关联参数") -public class GbStreamParam { - - @Schema(description = "平台ID") - private String platformId; - - @Schema(description = "目录ID") - private String catalogId; - - @Schema(description = "关联所有通道") - private boolean all; - - @Schema(description = "流国标信息列表") - private List gbStreams; - - public String getPlatformId() { - return platformId; - } - - public String getCatalogId() { - return catalogId; - } - - public void setCatalogId(String catalogId) { - this.catalogId = catalogId; - } - - public void setPlatformId(String platformId) { - this.platformId = platformId; - } - - public List getGbStreams() { - return gbStreams; - } - - public void setGbStreams(List gbStreams) { - this.gbStreams = gbStreams; - } - - public boolean isAll() { - return all; - } - - public void setAll(boolean all) { - this.all = all; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java deleted file mode 100755 index debc3b2f0..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java +++ /dev/null @@ -1,554 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.platform; - -import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.JSONObject; -import com.genersoft.iot.vmp.common.VideoManagerConstants; -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.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch; -import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog; -import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; -import com.genersoft.iot.vmp.service.*; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; -import com.genersoft.iot.vmp.utils.DateUtil; -import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.UpdateChannelParam; -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; -import io.swagger.v3.oas.annotations.tags.Tag; -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.*; -import com.genersoft.iot.vmp.conf.SipConfig; - -import javax.sip.InvalidArgumentException; -import javax.sip.SipException; -import java.text.ParseException; -import java.util.List; - -/** - * 级联平台管理 - */ -@Tag(name = "级联平台管理") - -@RestController -@RequestMapping("/api/platform") -public class PlatformController { - - private final static Logger logger = LoggerFactory.getLogger(PlatformController.class); - - @Autowired - private UserSetting userSetting; - - @Autowired - private IVideoManagerStorage storager; - - @Autowired - private IPlatformChannelService platformChannelService; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - private SubscribeHolder subscribeHolder; - - @Autowired - private ISIPCommanderForPlatform commanderForPlatform; - - @Autowired - private SipConfig sipConfig; - - @Autowired - private DynamicTask dynamicTask; - - @Autowired - private IPlatformService platformService; - - @Autowired - private IDeviceChannelService deviceChannelService; - - @Autowired - private IGbStreamService gbStreamService; - - /** - * 获取国标服务的配置 - * - * @return - */ - @Operation(summary = "获取国标服务的配置", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @GetMapping("/server_config") - public JSONObject serverConfig() { - JSONObject result = new JSONObject(); - result.put("deviceIp", sipConfig.getShowIp()); - result.put("devicePort", sipConfig.getPort()); - result.put("username", sipConfig.getId()); - result.put("password", sipConfig.getPassword()); - return result; - } - - /** - * 获取级联服务器信息 - * - * @return - */ - @Operation(summary = "获取级联服务器信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "id", description = "平台国标编号", required = true) - @GetMapping("/info/{id}") - public ParentPlatform getPlatform(@PathVariable String id) { - ParentPlatform parentPlatform = platformService.queryPlatformByServerGBId(id); - if (parentPlatform != null) { - return parentPlatform; - } else { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "未查询到此平台"); - } - } - - /** - * 分页查询级联平台 - * - * @param page 当前页 - * @param count 每页条数 - * @return - */ - @GetMapping("/query/{count}/{page}") - @Operation(summary = "分页查询级联平台", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "page", description = "当前页", required = true) - @Parameter(name = "count", description = "每页条数", required = true) - public PageInfo platforms(@PathVariable int page, @PathVariable int count) { - - PageInfo parentPlatformPageInfo = platformService.queryParentPlatformList(page, count); - if (parentPlatformPageInfo.getList().size() > 0) { - for (ParentPlatform platform : parentPlatformPageInfo.getList()) { - platform.setMobilePositionSubscribe(subscribeHolder.getMobilePositionSubscribe(platform.getServerGBId()) != null); - platform.setCatalogSubscribe(subscribeHolder.getCatalogSubscribe(platform.getServerGBId()) != null); - } - } - return parentPlatformPageInfo; - } - - /** - * 添加上级平台信息 - * - * @param parentPlatform - * @return - */ - @Operation(summary = "添加上级平台信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @PostMapping("/add") - @ResponseBody - public void addPlatform(@RequestBody ParentPlatform parentPlatform) { - - if (logger.isDebugEnabled()) { - logger.debug("保存上级平台信息API调用"); - } - if (ObjectUtils.isEmpty(parentPlatform.getName()) - || ObjectUtils.isEmpty(parentPlatform.getServerGBId()) - || ObjectUtils.isEmpty(parentPlatform.getServerGBDomain()) - || ObjectUtils.isEmpty(parentPlatform.getServerIP()) - || ObjectUtils.isEmpty(parentPlatform.getServerPort()) - || ObjectUtils.isEmpty(parentPlatform.getDeviceGBId()) - || ObjectUtils.isEmpty(parentPlatform.getExpires()) - || ObjectUtils.isEmpty(parentPlatform.getKeepTimeout()) - || ObjectUtils.isEmpty(parentPlatform.getTransport()) - || ObjectUtils.isEmpty(parentPlatform.getCharacterSet()) - ) { - throw new ControllerException(ErrorCode.ERROR400); - } - if (parentPlatform.getServerPort() < 0 || parentPlatform.getServerPort() > 65535) { - throw new ControllerException(ErrorCode.ERROR400.getCode(), "error severPort"); - } - - - ParentPlatform parentPlatformOld = storager.queryParentPlatByServerGBId(parentPlatform.getServerGBId()); - if (parentPlatformOld != null) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台 " + parentPlatform.getServerGBId() + " 已存在"); - } - parentPlatform.setCreateTime(DateUtil.getNow()); - parentPlatform.setUpdateTime(DateUtil.getNow()); - boolean updateResult = platformService.add(parentPlatform); - - if (!updateResult) { - throw new ControllerException(ErrorCode.ERROR100.getCode(),"写入数据库失败"); - } - } - - /** - * 保存上级平台信息 - * - * @param parentPlatform - * @return - */ - @Operation(summary = "保存上级平台信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @PostMapping("/save") - @ResponseBody - public void savePlatform(@RequestBody ParentPlatform parentPlatform) { - - if (logger.isDebugEnabled()) { - logger.debug("保存上级平台信息API调用"); - } - if (ObjectUtils.isEmpty(parentPlatform.getName()) - || ObjectUtils.isEmpty(parentPlatform.getServerGBId()) - || ObjectUtils.isEmpty(parentPlatform.getServerGBDomain()) - || ObjectUtils.isEmpty(parentPlatform.getServerIP()) - || ObjectUtils.isEmpty(parentPlatform.getServerPort()) - || ObjectUtils.isEmpty(parentPlatform.getDeviceGBId()) - || ObjectUtils.isEmpty(parentPlatform.getExpires()) - || ObjectUtils.isEmpty(parentPlatform.getKeepTimeout()) - || ObjectUtils.isEmpty(parentPlatform.getTransport()) - || ObjectUtils.isEmpty(parentPlatform.getCharacterSet()) - ) { - throw new ControllerException(ErrorCode.ERROR400); - } - - platformService.update(parentPlatform); - } - - /** - * 删除上级平台 - * - * @param serverGBId 上级平台国标ID - * @return - */ - @Operation(summary = "删除上级平台", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "serverGBId", description = "上级平台的国标编号") - @DeleteMapping("/delete/{serverGBId}") - @ResponseBody - public void deletePlatform(@PathVariable String serverGBId) { - - if (logger.isDebugEnabled()) { - logger.debug("删除上级平台API调用"); - } - if (ObjectUtils.isEmpty(serverGBId) - ) { - throw new ControllerException(ErrorCode.ERROR400); - } - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId); - ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(serverGBId); - if (parentPlatform == null) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在"); - } - if (parentPlatformCatch == null) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在"); - } - parentPlatform.setEnable(false); - storager.updateParentPlatform(parentPlatform); - // 发送离线消息,无论是否成功都删除缓存 - try { - commanderForPlatform.unregister(parentPlatform, parentPlatformCatch.getSipTransactionInfo(), (event -> { - // 清空redis缓存 - redisCatchStorage.delPlatformCatchInfo(parentPlatform.getServerGBId()); - redisCatchStorage.delPlatformKeepalive(parentPlatform.getServerGBId()); - redisCatchStorage.delPlatformRegister(parentPlatform.getServerGBId()); - }), (event -> { - // 清空redis缓存 - redisCatchStorage.delPlatformCatchInfo(parentPlatform.getServerGBId()); - redisCatchStorage.delPlatformKeepalive(parentPlatform.getServerGBId()); - redisCatchStorage.delPlatformRegister(parentPlatform.getServerGBId()); - })); - } catch (InvalidArgumentException | ParseException | SipException e) { - logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); - } - - boolean deleteResult = storager.deleteParentPlatform(parentPlatform); - storager.delCatalogByPlatformId(parentPlatform.getServerGBId()); - storager.delRelationByPlatformId(parentPlatform.getServerGBId()); - // 停止发送位置订阅定时任务 - String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetting.getServerId() + "_MobilePosition_" + parentPlatform.getServerGBId(); - dynamicTask.stop(key); - // 删除缓存的订阅信息 - subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId()); - if (!deleteResult) { - throw new ControllerException(ErrorCode.ERROR100); - } - } - - /** - * 查询上级平台是否存在 - * - * @param serverGBId 上级平台国标ID - * @return - */ - @Operation(summary = "查询上级平台是否存在", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "serverGBId", description = "上级平台的国标编号") - @GetMapping("/exit/{serverGBId}") - @ResponseBody - public Boolean exitPlatform(@PathVariable String serverGBId) { - - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId); - return parentPlatform != null; - } - - /** - * 分页查询级联平台的所有所有通道 - * - * @param page 当前页 - * @param count 每页条数 - * @param platformId 上级平台ID - * @param query 查询内容 - * @param online 是否在线 - * @param channelType 通道类型 - * @return - */ - @Operation(summary = "查询上级平台是否存在", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "page", description = "当前页", required = true) - @Parameter(name = "count", description = "每页条数", required = true) - @Parameter(name = "platformId", description = "上级平台的国标编号") - @Parameter(name = "catalogId", description = "目录ID") - @Parameter(name = "query", description = "查询内容") - @Parameter(name = "online", description = "是否在线") - @Parameter(name = "channelType", description = "通道类型") - @GetMapping("/channel_list") - @ResponseBody - public PageInfo channelList(int page, int count, - @RequestParam(required = false) String platformId, - @RequestParam(required = false) String catalogId, - @RequestParam(required = false) String query, - @RequestParam(required = false) Boolean online, - @RequestParam(required = false) Boolean channelType) { - - if (ObjectUtils.isEmpty(platformId)) { - platformId = null; - } - if (ObjectUtils.isEmpty(query)) { - query = null; - } - if (ObjectUtils.isEmpty(platformId) || ObjectUtils.isEmpty(catalogId)) { - catalogId = null; - } - PageInfo channelReduces = storager.queryAllChannelList(page, count, query, online, channelType, platformId, catalogId); - - return channelReduces; - } - - /** - * 向上级平台添加国标通道 - * - * @param param 通道关联参数 - * @return - */ - @Operation(summary = "向上级平台添加国标通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @PostMapping("/update_channel_for_gb") - @ResponseBody - public void updateChannelForGB(@RequestBody UpdateChannelParam param) { - - if (logger.isDebugEnabled()) { - logger.debug("给上级平台添加国标通道API调用"); - } - int result = 0; - if (param.getChannelReduces() == null || param.getChannelReduces().size() == 0) { - if (param.isAll()) { - logger.info("[国标级联]添加所有通道到上级平台, {}", param.getPlatformId()); - List allChannelForDevice = deviceChannelService.queryAllChannelList(param.getPlatformId()); - result = platformChannelService.updateChannelForGB(param.getPlatformId(), allChannelForDevice, param.getCatalogId()); - } - }else { - result = platformChannelService.updateChannelForGB(param.getPlatformId(), param.getChannelReduces(), param.getCatalogId()); - } - if (result <= 0) { - throw new ControllerException(ErrorCode.ERROR100); - } - } - - /** - * 从上级平台移除国标通道 - * - * @param param 通道关联参数 - * @return - */ - @Operation(summary = "从上级平台移除国标通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @DeleteMapping("/del_channel_for_gb") - @ResponseBody - public void delChannelForGB(@RequestBody UpdateChannelParam param) { - - if (logger.isDebugEnabled()) { - logger.debug("给上级平台删除国标通道API调用"); - } - int result = 0; - if (param.getChannelReduces() == null || param.getChannelReduces().size() == 0) { - if (param.isAll()) { - logger.info("[国标级联]移除所有通道,上级平台, {}", param.getPlatformId()); - result = platformChannelService.delAllChannelForGB(param.getPlatformId(), param.getCatalogId()); - } - }else { - result = storager.delChannelForGB(param.getPlatformId(), param.getChannelReduces()); - } - if (result <= 0) { - throw new ControllerException(ErrorCode.ERROR100); - } - } - - /** - * 获取目录 - * - * @param platformId 平台ID - * @param parentId 目录父ID - * @return - */ - @Operation(summary = "获取目录", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "platformId", description = "上级平台的国标编号", required = true) - @Parameter(name = "parentId", description = "父级目录的国标编号", required = true) - @GetMapping("/catalog") - @ResponseBody - public List getCatalogByPlatform(String platformId, String parentId) { - - if (logger.isDebugEnabled()) { - logger.debug("查询目录,platformId: {}, parentId: {}", platformId, parentId); - } - ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); - if (platform == null) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台未找到"); - } -// if (platformId.equals(parentId)) { -// parentId = platform.getDeviceGBId(); -// } - - if (platformId.equals(platform.getDeviceGBId())) { - parentId = null; - } - - return storager.getChildrenCatalogByPlatform(platformId, parentId); - } - - /** - * 添加目录 - * - * @param platformCatalog 目录 - * @return - */ - @Operation(summary = "添加目录", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @PostMapping("/catalog/add") - @ResponseBody - public void addCatalog(@RequestBody PlatformCatalog platformCatalog) { - - if (logger.isDebugEnabled()) { - logger.debug("添加目录,{}", JSON.toJSONString(platformCatalog)); - } - PlatformCatalog platformCatalogInStore = storager.getCatalog(platformCatalog.getPlatformId(), platformCatalog.getId()); - - if (platformCatalogInStore != null) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), platformCatalog.getId() + " already exists"); - } - int addResult = storager.addCatalog(platformCatalog); - if (addResult <= 0) { - throw new ControllerException(ErrorCode.ERROR100); - } - } - - /** - * 编辑目录 - * - * @param platformCatalog 目录 - * @return - */ - @Operation(summary = "编辑目录", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @PostMapping("/catalog/edit") - @ResponseBody - public void editCatalog(@RequestBody PlatformCatalog platformCatalog) { - - if (logger.isDebugEnabled()) { - logger.debug("编辑目录,{}", JSON.toJSONString(platformCatalog)); - } - PlatformCatalog platformCatalogInStore = storager.getCatalog(platformCatalog.getPlatformId(), platformCatalog.getId()); - - if (platformCatalogInStore == null) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), platformCatalog.getId() + " not exists"); - } - int addResult = storager.updateCatalog(platformCatalog); - if (addResult <= 0) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "写入数据库失败"); - } - } - - /** - * 删除目录 - * - * @param id 目录Id - * @param platformId 平台Id - * @return - */ - @Operation(summary = "删除目录", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "id", description = "目录Id", required = true) - @Parameter(name = "platformId", description = "平台Id", required = true) - @DeleteMapping("/catalog/del") - @ResponseBody - public void delCatalog(String id, String platformId) { - - if (logger.isDebugEnabled()) { - logger.debug("删除目录,{}", id); - } - - if (ObjectUtils.isEmpty(id) || ObjectUtils.isEmpty(platformId)) { - throw new ControllerException(ErrorCode.ERROR400); - } - - int delResult = storager.delCatalog(platformId, id); - // 如果删除的是默认目录则根目录设置为默认目录 - PlatformCatalog parentPlatform = storager.queryDefaultCatalogInPlatform(platformId); - - // 默认节点被移除 - if (parentPlatform == null) { - storager.setDefaultCatalog(platformId, platformId); - } - - if (delResult <= 0) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "写入数据库失败"); - } - } - - /** - * 删除关联 - * - * @param platformCatalog 关联的信息 - * @return - */ - @Operation(summary = "删除关联", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @DeleteMapping("/catalog/relation/del") - @ResponseBody - public void delRelation(@RequestBody PlatformCatalog platformCatalog) { - - if (logger.isDebugEnabled()) { - logger.debug("删除关联,{}", JSON.toJSONString(platformCatalog)); - } - int delResult = storager.delRelation(platformCatalog); - - if (delResult <= 0) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "写入数据库失败"); - } - } - - - /** - * 修改默认目录 - * - * @param platformId 平台Id - * @param catalogId 目录Id - * @return - */ - @Operation(summary = "修改默认目录", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "catalogId", description = "目录Id", required = true) - @Parameter(name = "platformId", description = "平台Id", required = true) - @PostMapping("/catalog/default/update") - @ResponseBody - public void setDefaultCatalog(String platformId, String catalogId) { - - if (logger.isDebugEnabled()) { - logger.debug("修改默认目录,{},{}", platformId, catalogId); - } - int updateResult = storager.setDefaultCatalog(platformId, catalogId); - - if (updateResult <= 0) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "写入数据库失败"); - } - } - - -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/bean/UpdateChannelParam.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/bean/UpdateChannelParam.java deleted file mode 100755 index 2d598e44d..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/bean/UpdateChannelParam.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.platform.bean; - -import io.swagger.v3.oas.annotations.media.Schema; - -import java.util.List; - -/** - * 通道关联参数 - * @author lin - */ -@Schema(description = "通道关联参数") -public class UpdateChannelParam { - - @Schema(description = "上级平台的国标编号") - private String platformId; - - @Schema(description = "目录的国标编号") - private String catalogId; - - @Schema(description = "处理所有通道") - private boolean all; - - @Schema(description = "") - private List channelReduces; - - public String getPlatformId() { - return platformId; - } - - public void setPlatformId(String platformId) { - this.platformId = platformId; - } - - public List getChannelReduces() { - return channelReduces; - } - - public void setChannelReduces(List channelReduces) { - this.channelReduces = channelReduces; - } - - public String getCatalogId() { - return catalogId; - } - - public void setCatalogId(String catalogId) { - this.catalogId = catalogId; - } - - public boolean isAll() { - return all; - } - - public void setAll(boolean all) { - this.all = all; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java index 59a69437f..ea5b11cce 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java @@ -13,20 +13,17 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; @Tag(name = "日志管理") - +@Slf4j @RestController @RequestMapping("/api/log") public class LogController { - private final static Logger logger = LoggerFactory.getLogger(LogController.class); - @Autowired private ILogService logService; @@ -65,7 +62,7 @@ public class LogController { } if (!userSetting.getLogInDatabase()) { - logger.warn("自动记录日志功能已关闭,查询结果可能不完整。"); + log.warn("自动记录日志功能已关闭,查询结果可能不完整。"); } if (ObjectUtils.isEmpty(startTime)) { diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/ps/PsController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/ps/PsController.java index a587d5dfa..787defa28 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/ps/PsController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/ps/PsController.java @@ -5,7 +5,7 @@ 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.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.event.hook.Hook; import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; @@ -20,10 +20,9 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Request; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.*; @@ -37,13 +36,11 @@ import java.util.concurrent.TimeUnit; @SuppressWarnings("rawtypes") @Tag(name = "第三方PS服务对接") - +@Slf4j @RestController @RequestMapping("/api/ps") public class PsController { - private final static Logger logger = LoggerFactory.getLogger(PsController.class); - @Autowired private HookSubscribe hookSubscribe; @@ -75,7 +72,7 @@ public class PsController { @Parameter(name = "callBack", description = "回调地址,如果收流超时会通道回调通知,回调为get请求,参数为callId", required = true) public OtherPsSendInfo openRtpServer(Boolean isSend, @RequestParam(required = false)String ssrc, String callId, String stream, Integer tcpMode, String callBack) { - logger.info("[第三方PS服务对接->开启收流和获取发流信息] isSend->{}, ssrc->{}, callId->{}, stream->{}, tcpMode->{}, callBack->{}", + log.info("[第三方PS服务对接->开启收流和获取发流信息] isSend->{}, ssrc->{}, callId->{}, stream->{}, tcpMode->{}, callBack->{}", isSend, ssrc, callId, stream, tcpMode==0?"UDP":"TCP被动", callBack); MediaServer mediaServer = mediaServerService.getDefaultMediaServer(); @@ -109,7 +106,7 @@ public class PsController { hookSubscribe.addSubscribe(hook, (hookData)->{ if (stream.equals(hookData.getStream())) { - logger.info("[第三方PS服务对接->开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调", callId); + log.info("[第三方PS服务对接->开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调", callId); // 将信息写入redis中,以备后用 redisTemplate.delete(receiveKey); OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); @@ -119,7 +116,7 @@ public class PsController { try { client.newCall(request).execute(); } catch (IOException e) { - logger.error("[第三方PS服务对接->开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调失败", callId, e); + log.error("[第三方PS服务对接->开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调失败", callId, e); } hookSubscribe.removeSubscribe(hook); } @@ -142,7 +139,7 @@ public class PsController { otherPsSendInfo.setSendLocalPort(port); // 将信息写入redis中,以备后用 redisTemplate.opsForValue().set(key, otherPsSendInfo, 300, TimeUnit.SECONDS); - logger.info("[第三方PS服务对接->开启收流和获取发流信息] 结果,callId->{}, {}", callId, otherPsSendInfo); + log.info("[第三方PS服务对接->开启收流和获取发流信息] 结果,callId->{}, {}", callId, otherPsSendInfo); } return otherPsSendInfo; } @@ -152,7 +149,7 @@ public class PsController { @Operation(summary = "关闭收流", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Parameter(name = "stream", description = "流的ID", required = true) public void closeRtpServer(String stream) { - logger.info("[第三方PS服务对接->关闭收流] stream->{}", stream); + log.info("[第三方PS服务对接->关闭收流] stream->{}", stream); MediaServer mediaServerItem = mediaServerService.getDefaultMediaServer(); mediaServerService.closeRTPServer(mediaServerItem, stream); String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_PS_INFO + userSetting.getServerId() + "_*_" + stream; @@ -183,7 +180,7 @@ public class PsController { String callId, Boolean isUdp ) { - logger.info("[第三方PS服务对接->发送流] " + + log.info("[第三方PS服务对接->发送流] " + "ssrc->{}, \r\n" + "dstIp->{}, \n" + "dstPort->{}, \n" + @@ -205,18 +202,18 @@ public class PsController { sendInfo.setPushApp(app); sendInfo.setPushStream(stream); sendInfo.setPushSSRC(ssrc); - SendRtpItem sendRtpItem = SendRtpItem.getInstance(app, stream, ssrc, dstIp, dstPort, !isUdp, sendInfo.getSendLocalPort(), null); + SendRtpInfo sendRtpItem = SendRtpInfo.getInstance(app, stream, ssrc, dstIp, dstPort, !isUdp, sendInfo.getSendLocalPort(), null); Boolean streamReady = mediaServerService.isStreamReady(mediaServer, app, stream); if (streamReady) { mediaServerService.startSendRtp(mediaServer, sendRtpItem); - logger.info("[第三方PS服务对接->发送流] 视频流发流成功,callId->{},param->{}", callId, sendRtpItem); + log.info("[第三方PS服务对接->发送流] 视频流发流成功,callId->{},param->{}", callId, sendRtpItem); redisTemplate.opsForValue().set(key, sendInfo); }else { - logger.info("[第三方PS服务对接->发送流] 流不存在,等待流上线,callId->{}", callId); + log.info("[第三方PS服务对接->发送流] 流不存在,等待流上线,callId->{}", callId); String uuid = UUID.randomUUID().toString(); Hook hook = Hook.getInstance(HookType.on_media_arrival, app, stream, mediaServer.getId()); dynamicTask.startDelay(uuid, ()->{ - logger.info("[第三方PS服务对接->发送流] 等待流上线超时 callId->{}", callId); + log.info("[第三方PS服务对接->发送流] 等待流上线超时 callId->{}", callId); redisTemplate.delete(key); hookSubscribe.removeSubscribe(hook); }, 10000); @@ -227,14 +224,14 @@ public class PsController { hookSubscribe.addSubscribe(hook, (hookData)->{ dynamicTask.stop(uuid); - logger.info("[第三方PS服务对接->发送流] 流上线,开始发流 callId->{}", callId); + log.info("[第三方PS服务对接->发送流] 流上线,开始发流 callId->{}", callId); try { Thread.sleep(400); } catch (InterruptedException e) { throw new RuntimeException(e); } mediaServerService.startSendRtp(mediaServer, sendRtpItem); - logger.info("[第三方PS服务对接->发送流] 视频流发流成功,callId->{},param->{}", callId, sendRtpItem); + log.info("[第三方PS服务对接->发送流] 视频流发流成功,callId->{},param->{}", callId, sendRtpItem); redisTemplate.opsForValue().set(key, finalSendInfo); hookSubscribe.removeSubscribe(hook); }); @@ -246,7 +243,7 @@ public class PsController { @Operation(summary = "关闭发送流") @Parameter(name = "callId", description = "整个过程的唯一标识,不传则使用随机端口发流", required = true) public void closeSendRTP(String callId) { - logger.info("[第三方PS服务对接->关闭发送流] callId->{}", callId); + log.info("[第三方PS服务对接->关闭发送流] callId->{}", callId); String key = VideoManagerConstants.WVP_OTHER_SEND_PS_INFO + userSetting.getServerId() + "_" + callId; OtherPsSendInfo sendInfo = (OtherPsSendInfo)redisTemplate.opsForValue().get(key); if (sendInfo == null){ @@ -260,10 +257,10 @@ public class PsController { MediaServer mediaServerItem = mediaServerService.getDefaultMediaServer(); boolean result = mediaServerService.stopSendRtp(mediaServerItem, sendInfo.getPushApp(), sendInfo.getStream(), sendInfo.getPushSSRC()); if (!result) { - logger.info("[第三方PS服务对接->关闭发送流] 失败 callId->{}", callId); + log.info("[第三方PS服务对接->关闭发送流] 失败 callId->{}", callId); throw new ControllerException(ErrorCode.ERROR100.getCode(), "停止发流失败"); }else { - logger.info("[第三方PS服务对接->关闭发送流] 成功 callId->{}", callId); + log.info("[第三方PS服务对接->关闭发送流] 成功 callId->{}", callId); } redisTemplate.delete(key); } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java index 7832e3739..a8ef9df2f 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java @@ -5,7 +5,7 @@ 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.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.event.hook.Hook; import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; @@ -20,10 +20,9 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Request; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.util.ObjectUtils; @@ -36,7 +35,7 @@ import java.util.concurrent.TimeUnit; @SuppressWarnings("rawtypes") @Tag(name = "第三方服务对接") - +@Slf4j @RestController @RequestMapping("/api/rtp") public class RtpController { @@ -44,8 +43,6 @@ public class RtpController { @Autowired private SendRtpPortManager sendRtpPortManager; - private final static Logger logger = LoggerFactory.getLogger(RtpController.class); - @Autowired private HookSubscribe hookSubscribe; @@ -73,7 +70,7 @@ public class RtpController { @Parameter(name = "callBack", description = "回调地址,如果收流超时会通道回调通知,回调为get请求,参数为callId", required = true) public OtherRtpSendInfo openRtpServer(Boolean isSend, @RequestParam(required = false)String ssrc, String callId, String stream, Integer tcpMode, String callBack) { - logger.info("[第三方服务对接->开启收流和获取发流信息] isSend->{}, ssrc->{}, callId->{}, stream->{}, tcpMode->{}, callBack->{}", + log.info("[第三方服务对接->开启收流和获取发流信息] isSend->{}, ssrc->{}, callId->{}, stream->{}, tcpMode->{}, callBack->{}", isSend, ssrc, callId, stream, tcpMode==0?"UDP":"TCP被动", callBack); MediaServer mediaServer = mediaServerService.getDefaultMediaServer(); @@ -107,7 +104,7 @@ public class RtpController { hookSubscribe.addSubscribe(hook, (hookData)->{ if (stream.equals(hookData.getStream())) { - logger.info("[开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调", callId); + log.info("[开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调", callId); OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); OkHttpClient client = httpClientBuilder.build(); String url = callBack + "?callId=" + callId; @@ -115,7 +112,7 @@ public class RtpController { try { client.newCall(request).execute(); } catch (IOException e) { - logger.error("[第三方服务对接->开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调失败", callId, e); + log.error("[第三方服务对接->开启收流和获取发流信息] 等待收流超时 callId->{}, 发送回调失败", callId, e); } hookSubscribe.removeSubscribe(hook); } @@ -141,7 +138,7 @@ public class RtpController { otherRtpSendInfo.setSendLocalPortForAudio(portForAudio); // 将信息写入redis中,以备后用 redisTemplate.opsForValue().set(key, otherRtpSendInfo, 300, TimeUnit.SECONDS); - logger.info("[第三方服务对接->开启收流和获取发流信息] 结果,callId->{}, {}", callId, otherRtpSendInfo); + log.info("[第三方服务对接->开启收流和获取发流信息] 结果,callId->{}, {}", callId, otherRtpSendInfo); } // 将信息写入redis中,以备后用 redisTemplate.opsForValue().set(key, otherRtpSendInfo, 300, TimeUnit.SECONDS); @@ -153,7 +150,7 @@ public class RtpController { @Operation(summary = "关闭收流", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Parameter(name = "stream", description = "流的ID", required = true) public void closeRtpServer(String stream) { - logger.info("[第三方服务对接->关闭收流] stream->{}", stream); + log.info("[第三方服务对接->关闭收流] stream->{}", stream); MediaServer mediaServerItem = mediaServerService.getDefaultMediaServer(); mediaServerService.closeRTPServer(mediaServerItem, stream); mediaServerService.closeRTPServer(mediaServerItem, stream+ "_a"); @@ -193,7 +190,7 @@ public class RtpController { @RequestParam(required = false)Integer ptForAudio, @RequestParam(required = false)Integer ptForVideo ) { - logger.info("[第三方服务对接->发送流] " + + log.info("[第三方服务对接->发送流] " + "ssrc->{}, \r\n" + "dstIpForAudio->{}, \n" + "dstIpForAudio->{}, \n" + @@ -228,15 +225,15 @@ public class RtpController { sendInfo.setPushSSRC(ssrc); - SendRtpItem sendRtpItemForVideo; - SendRtpItem sendRtpItemForAudio; + SendRtpInfo sendRtpItemForVideo; + SendRtpInfo sendRtpItemForAudio; if (!ObjectUtils.isEmpty(dstIpForAudio) && dstPortForAudio > 0) { - sendRtpItemForAudio = SendRtpItem.getInstance(app, stream, ssrc, dstIpForAudio, dstPortForAudio, !isUdp, sendInfo.getSendLocalPortForAudio(), ptForAudio); + sendRtpItemForAudio = SendRtpInfo.getInstance(app, stream, ssrc, dstIpForAudio, dstPortForAudio, !isUdp, sendInfo.getSendLocalPortForAudio(), ptForAudio); } else { sendRtpItemForAudio = null; } if (!ObjectUtils.isEmpty(dstIpForVideo) && dstPortForVideo > 0) { - sendRtpItemForVideo = SendRtpItem.getInstance(app, stream, ssrc, dstIpForAudio, dstPortForAudio, !isUdp, sendInfo.getSendLocalPortForVideo(), ptForVideo); + sendRtpItemForVideo = SendRtpInfo.getInstance(app, stream, ssrc, dstIpForAudio, dstPortForAudio, !isUdp, sendInfo.getSendLocalPortForVideo(), ptForVideo); } else { sendRtpItemForVideo = null; } @@ -245,20 +242,20 @@ public class RtpController { if (streamReady) { if (sendRtpItemForVideo != null) { mediaServerService.startSendRtp(mediaServer, sendRtpItemForVideo); - logger.info("[第三方服务对接->发送流] 视频流发流成功,callId->{},param->{}", callId, sendRtpItemForVideo); + log.info("[第三方服务对接->发送流] 视频流发流成功,callId->{},param->{}", callId, sendRtpItemForVideo); redisTemplate.opsForValue().set(key, sendInfo); } if(sendRtpItemForAudio != null) { mediaServerService.startSendRtp(mediaServer, sendRtpItemForAudio); - logger.info("[第三方服务对接->发送流] 音频流发流成功,callId->{},param->{}", callId, sendRtpItemForAudio); + log.info("[第三方服务对接->发送流] 音频流发流成功,callId->{},param->{}", callId, sendRtpItemForAudio); redisTemplate.opsForValue().set(key, sendInfo); } }else { - logger.info("[第三方服务对接->发送流] 流不存在,等待流上线,callId->{}", callId); + log.info("[第三方服务对接->发送流] 流不存在,等待流上线,callId->{}", callId); String uuid = UUID.randomUUID().toString(); Hook hook = Hook.getInstance(HookType.on_media_arrival, app, stream, mediaServer.getId()); dynamicTask.startDelay(uuid, ()->{ - logger.info("[第三方服务对接->发送流] 等待流上线超时 callId->{}", callId); + log.info("[第三方服务对接->发送流] 等待流上线超时 callId->{}", callId); redisTemplate.delete(key); hookSubscribe.removeSubscribe(hook); }, 10000); @@ -269,7 +266,7 @@ public class RtpController { hookSubscribe.addSubscribe(hook, (hookData)->{ dynamicTask.stop(uuid); - logger.info("[第三方服务对接->发送流] 流上线,开始发流 callId->{}", callId); + log.info("[第三方服务对接->发送流] 流上线,开始发流 callId->{}", callId); try { Thread.sleep(400); } catch (InterruptedException e) { @@ -277,12 +274,12 @@ public class RtpController { } if (sendRtpItemForVideo != null) { mediaServerService.startSendRtp(mediaServer, sendRtpItemForVideo); - logger.info("[第三方服务对接->发送流] 视频流发流成功,callId->{},param->{}", callId, sendRtpItemForVideo); + log.info("[第三方服务对接->发送流] 视频流发流成功,callId->{},param->{}", callId, sendRtpItemForVideo); redisTemplate.opsForValue().set(key, finalSendInfo); } if(sendRtpItemForAudio != null) { mediaServerService.startSendRtp(mediaServer, sendRtpItemForAudio); - logger.info("[第三方服务对接->发送流] 音频流发流成功,callId->{},param->{}", callId, sendRtpItemForAudio); + log.info("[第三方服务对接->发送流] 音频流发流成功,callId->{},param->{}", callId, sendRtpItemForAudio); redisTemplate.opsForValue().set(key, finalSendInfo); } hookSubscribe.removeSubscribe(hook); @@ -295,7 +292,7 @@ public class RtpController { @Operation(summary = "关闭发送流", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Parameter(name = "callId", description = "整个过程的唯一标识,不传则使用随机端口发流", required = true) public void closeSendRTP(String callId) { - logger.info("[第三方服务对接->关闭发送流] callId->{}", callId); + log.info("[第三方服务对接->关闭发送流] callId->{}", callId); String key = VideoManagerConstants.WVP_OTHER_SEND_RTP_INFO + userSetting.getServerId() + "_" + callId; OtherRtpSendInfo sendInfo = (OtherRtpSendInfo)redisTemplate.opsForValue().get(key); if (sendInfo == null){ @@ -303,7 +300,7 @@ public class RtpController { } MediaServer mediaServerItem = mediaServerService.getDefaultMediaServer(); mediaServerService.stopSendRtp(mediaServerItem, sendInfo.getPushApp(), sendInfo.getPushStream(), sendInfo.getPushSSRC()); - logger.info("[第三方服务对接->关闭发送流] 成功 callId->{}", callId); + log.info("[第三方服务对接->关闭发送流] 成功 callId->{}", callId); redisTemplate.delete(key); } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java index a7d604162..bc72ce702 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java @@ -9,11 +9,14 @@ import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.VersionInfo; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.security.JwtUtils; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.media.service.IMediaServerService; import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.service.*; import com.genersoft.iot.vmp.service.bean.MediaServerLoad; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.streamProxy.service.IStreamProxyService; +import com.genersoft.iot.vmp.streamPush.service.IStreamPushService; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; import com.genersoft.iot.vmp.vmanager.bean.ResourceInfo; diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java deleted file mode 100755 index dd5e703a1..000000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java +++ /dev/null @@ -1,189 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.streamProxy; - -import com.alibaba.fastjson2.JSONObject; -import com.genersoft.iot.vmp.common.StreamInfo; -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.gb28181.transmit.callback.DeferredResultHolder; -import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; -import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; -import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.service.IStreamProxyService; -import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; -import com.genersoft.iot.vmp.vmanager.bean.StreamContent; -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -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; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.util.ObjectUtils; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.context.request.async.DeferredResult; - -import java.util.Map; -import java.util.UUID; - -@SuppressWarnings("rawtypes") -/** - * 拉流代理接口 - */ -@Tag(name = "拉流代理", description = "") -@Controller - -@RequestMapping(value = "/api/proxy") -public class StreamProxyController { - - private final static Logger logger = LoggerFactory.getLogger(StreamProxyController.class); - - @Autowired - private IMediaServerService mediaServerService; - - @Autowired - private IStreamProxyService streamProxyService; - - @Autowired - private DeferredResultHolder resultHolder; - - @Autowired - private UserSetting userSetting; - - - @Operation(summary = "分页查询流代理", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "page", description = "当前页") - @Parameter(name = "count", description = "每页查询数量") - @Parameter(name = "query", description = "查询内容") - @Parameter(name = "online", description = "是否在线") - @GetMapping(value = "/list") - @ResponseBody - public PageInfo list(@RequestParam(required = false)Integer page, - @RequestParam(required = false)Integer count, - @RequestParam(required = false)String query, - @RequestParam(required = false)Boolean online ){ - - return streamProxyService.getAll(page, count); - } - - @Operation(summary = "查询流代理", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "app", description = "应用名") - @Parameter(name = "stream", description = "流Id") - @GetMapping(value = "/one") - @ResponseBody - public StreamProxyItem one(String app, String stream){ - - return streamProxyService.getStreamProxyByAppAndStream(app, stream); - } - - @Operation(summary = "保存代理", security = @SecurityRequirement(name = JwtUtils.HEADER), parameters = { - @Parameter(name = "param", description = "代理参数", required = true), - }) - @PostMapping(value = "/save") - @ResponseBody - public DeferredResult save(@RequestBody StreamProxyItem param){ - logger.info("添加代理: " + JSONObject.toJSONString(param)); - if (ObjectUtils.isEmpty(param.getMediaServerId())) { - param.setMediaServerId("auto"); - } - if (ObjectUtils.isEmpty(param.getType())) { - param.setType("default"); - } - if (ObjectUtils.isEmpty(param.getRtpType())) { - param.setRtpType("1"); - } - if (ObjectUtils.isEmpty(param.getGbId())) { - param.setGbId(null); - } - StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream()); - if (streamProxyItem != null) { - streamProxyService.del(param.getApp(), param.getStream()); - } - - RequestMessage requestMessage = new RequestMessage(); - String key = DeferredResultHolder.CALLBACK_CMD_PROXY + param.getApp() + param.getStream(); - requestMessage.setKey(key); - String uuid = UUID.randomUUID().toString(); - requestMessage.setId(uuid); - DeferredResult result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); - // 录像查询以channelId作为deviceId查询 - resultHolder.put(key, uuid, result); - result.onTimeout(()->{ - WVPResult wvpResult = new WVPResult<>(); - wvpResult.setCode(ErrorCode.ERROR100.getCode()); - wvpResult.setMsg("超时"); - requestMessage.setData(wvpResult); - resultHolder.invokeAllResult(requestMessage); - }); - - streamProxyService.save(param, (code, msg, streamInfo) -> { - logger.info("[拉流代理] {}", code == ErrorCode.SUCCESS.getCode()? "成功":"失败: " + msg); - if (code == ErrorCode.SUCCESS.getCode()) { - requestMessage.setData(new StreamContent(streamInfo)); - }else { - requestMessage.setData(WVPResult.fail(code, msg)); - } - resultHolder.invokeAllResult(requestMessage); - }); - return result; - } - - @GetMapping(value = "/ffmpeg_cmd/list") - @ResponseBody - @Operation(summary = "获取ffmpeg.cmd模板", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "mediaServerId", description = "流媒体ID", required = true) - public Map getFFmpegCMDs(@RequestParam String mediaServerId){ - logger.debug("获取节点[ {} ]ffmpeg.cmd模板", mediaServerId ); - - MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId); - if (mediaServerItem == null) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "流媒体: " + mediaServerId + "未找到"); - } - return streamProxyService.getFFmpegCMDs(mediaServerItem); - } - - @DeleteMapping(value = "/del") - @ResponseBody - @Operation(summary = "移除代理", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "app", description = "应用名", required = true) - @Parameter(name = "stream", description = "流id", required = true) - public void del(@RequestParam String app, @RequestParam String stream){ - logger.info("移除代理: " + app + "/" + stream); - if (app == null || stream == null) { - throw new ControllerException(ErrorCode.ERROR400.getCode(), app == null ?"app不能为null":"stream不能为null"); - }else { - streamProxyService.del(app, stream); - } - } - - @GetMapping(value = "/start") - @ResponseBody - @Operation(summary = "启用代理", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "app", description = "应用名", required = true) - @Parameter(name = "stream", description = "流id", required = true) - public void start(String app, String stream){ - logger.info("启用代理: " + app + "/" + stream); - boolean result = streamProxyService.start(app, stream); - if (!result) { - throw new ControllerException(ErrorCode.ERROR100); - } - } - - @GetMapping(value = "/stop") - @ResponseBody - @Operation(summary = "停用代理", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "app", description = "应用名", required = true) - @Parameter(name = "stream", description = "流id", required = true) - public void stop(String app, String stream){ - logger.info("停用代理: " + app + "/" + stream); - boolean result = streamProxyService.stop(app, stream); - if (!result) { - logger.info("停用代理失败: " + app + "/" + stream); - throw new ControllerException(ErrorCode.ERROR100); - } - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/user/RoleController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/user/RoleController.java index 815b2398a..9969087e8 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/user/RoleController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/user/RoleController.java @@ -34,7 +34,7 @@ public class RoleController { // 获取当前登录用户id int currenRoleId = SecurityUtils.getUserInfo().getRole().getId(); if (currenRoleId != 1) { - // 只用角色id为1才可以删除和添加用户 + // 只用角色id为0才可以删除和添加用户 throw new ControllerException(ErrorCode.ERROR403); } @@ -71,7 +71,6 @@ public class RoleController { @Operation(summary = "查询角色", security = @SecurityRequirement(name = JwtUtils.HEADER)) public List all(){ // 获取当前登录用户id - List allRoles = roleService.getAll(); return roleService.getAll(); } } diff --git a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiControlController.java b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiControlController.java index 06ba33cec..edfd71a2d 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiControlController.java +++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiControlController.java @@ -3,10 +3,9 @@ package com.genersoft.iot.vmp.web.gb28181; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -20,18 +19,16 @@ import java.text.ParseException; /** * API兼容:设备控制 */ - +@Slf4j @RestController @RequestMapping(value = "/api/v1/control") public class ApiControlController { - private final static Logger logger = LoggerFactory.getLogger(ApiControlController.class); - @Autowired private SIPCommander cmder; @Autowired - private IVideoManagerStorage storager; + private IDeviceService deviceService; /** * 设备控制 - 云台控制 @@ -48,13 +45,13 @@ public class ApiControlController { @RequestParam(required = false)String code, @RequestParam(required = false)Integer speed){ - if (logger.isDebugEnabled()) { - logger.debug("模拟接口> 设备云台控制 API调用,deviceId:{} ,channelId:{} ,command:{} ,speed:{} ", + if (log.isDebugEnabled()) { + log.debug("模拟接口> 设备云台控制 API调用,deviceId:{} ,channelId:{} ,command:{} ,speed:{} ", serial, code, command, speed); } if (channel == null) {channel = 0;} if (speed == null) {speed = 0;} - Device device = storager.queryVideoDevice(serial); + Device device = deviceService.getDeviceByDeviceId(serial); if (device == null) { throw new ControllerException(ErrorCode.ERROR100.getCode(), "device[ " + serial + " ]未找到"); } @@ -100,7 +97,7 @@ public class ApiControlController { try { cmder.frontEndCmd(device, code, cmdCode, speed, speed, speed); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 云台控制: {}", e.getMessage()); + log.error("[命令发送失败] 云台控制: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } } @@ -122,13 +119,13 @@ public class ApiControlController { @RequestParam(required = false)String name, @RequestParam(required = false)Integer preset){ - if (logger.isDebugEnabled()) { - logger.debug("模拟接口> 预置位控制 API调用,deviceId:{} ,channelId:{} ,command:{} ,name:{} ,preset:{} ", + if (log.isDebugEnabled()) { + log.debug("模拟接口> 预置位控制 API调用,deviceId:{} ,channelId:{} ,command:{} ,name:{} ,preset:{} ", serial, code, command, name, preset); } if (channel == null) {channel = 0;} - Device device = storager.queryVideoDevice(serial); + Device device = deviceService.getDeviceByDeviceId(serial); if (device == null) { throw new ControllerException(ErrorCode.ERROR100.getCode(), "device[ " + serial + " ]未找到"); } @@ -149,7 +146,7 @@ public class ApiControlController { try { cmder.frontEndCmd(device, code, cmdCode, 0, preset, 0); } catch (SipException | InvalidArgumentException | ParseException e) { - logger.error("[命令发送失败] 预置位控制: {}", e.getMessage()); + log.error("[命令发送失败] 预置位控制: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } } diff --git a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiController.java b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiController.java index e0973b961..95996aa43 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiController.java +++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiController.java @@ -2,8 +2,7 @@ package com.genersoft.iot.vmp.web.gb28181; import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.conf.SipConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @@ -14,12 +13,10 @@ import org.springframework.web.bind.annotation.ResponseBody; * API兼容:系统接口 */ @Controller - +@Slf4j @RequestMapping(value = "/api/v1") public class ApiController { - private final static Logger logger = LoggerFactory.getLogger(ApiController.class); - @Autowired private SipConfig sipConfig; @@ -87,8 +84,8 @@ public class ApiController { @GetMapping(value = "/login") @ResponseBody private JSONObject login(String username,String password ){ - if (logger.isDebugEnabled()) { - logger.debug(String.format("模拟接口> 登录 API调用,username:%s ,password:%s ", + if (log.isDebugEnabled()) { + log.debug(String.format("模拟接口> 登录 API调用,username:%s ,password:%s ", username, password)); } diff --git a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java index df2c9a2a9..359d38145 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java +++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiDeviceController.java @@ -5,17 +5,16 @@ import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.PresetQuerySipReq; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; 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.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.vmanager.bean.DeferredResultEx; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; import com.github.pagehelper.PageInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.GetMapping; @@ -33,24 +32,22 @@ import java.util.*; * API兼容:设备信息 */ @SuppressWarnings("unchecked") - +@Slf4j @RestController @RequestMapping(value = "/api/v1/device") public class ApiDeviceController { - private final static Logger logger = LoggerFactory.getLogger(ApiDeviceController.class); - - @Autowired - private IVideoManagerStorage storager; - @Autowired private SIPCommander cmder; @Autowired - private IDeviceService deviceService; + private IDeviceChannelService channelService; @Autowired private DeferredResultHolder resultHolder; + @Autowired + private IDeviceService deviceService; + /** * 分页获取设备列表 现在直接返回,尚未实现分页 @@ -73,10 +70,10 @@ public class ApiDeviceController { JSONObject result = new JSONObject(); List devices; if (start == null || limit ==null) { - devices = storager.queryVideoDeviceList(online); + devices = deviceService.getAllByStatus(online); result.put("DeviceCount", devices.size()); }else { - PageInfo deviceList = storager.queryVideoDeviceList(start/limit, limit,online); + PageInfo deviceList = deviceService.getAll(start/limit, limit,null, online); result.put("DeviceCount", deviceList.getTotal()); devices = deviceList.getList(); } @@ -123,7 +120,7 @@ public class ApiDeviceController { String[] split = code.trim().split(","); channelIds = Arrays.asList(split); } - List allDeviceChannelList = storager.queryChannelsByDeviceId(serial,channelIds,online); + List allDeviceChannelList = channelService.queryChannelExtendsByDeviceId(serial,channelIds,online); if (start == null || limit ==null) { deviceChannels = allDeviceChannelList; result.put("ChannelCount", deviceChannels.size()); @@ -164,7 +161,7 @@ public class ApiDeviceController { // 1-IETF RFC3261, // 2-基于口令的双向认证, // 3-基于数字证书的双向认证 - deviceJOSNChannel.put("Status", deviceChannelExtend.isStatus() ? "ON":"OFF"); + deviceJOSNChannel.put("Status", deviceChannelExtend.getStatus()); deviceJOSNChannel.put("Longitude", deviceChannelExtend.getLongitude()); deviceJOSNChannel.put("Latitude", deviceChannelExtend.getLatitude()); deviceJOSNChannel.put("PTZType ", deviceChannelExtend.getPTZType()); // 云台类型, 0 - 未知, 1 - 球机, 2 - 半球, @@ -194,18 +191,18 @@ public class ApiDeviceController { @RequestParam(required = false)Boolean fill, @RequestParam(required = false)Integer timeout){ - if (logger.isDebugEnabled()) { - logger.debug("<模拟接口> 获取下级通道预置位 API调用,deviceId:{} ,channel:{} ,code:{} ,fill:{} ,timeout:{} ", + if (log.isDebugEnabled()) { + log.debug("<模拟接口> 获取下级通道预置位 API调用,deviceId:{} ,channel:{} ,code:{} ,fill:{} ,timeout:{} ", serial, channel, code, fill, timeout); } - Device device = storager.queryVideoDevice(serial); + Device device = deviceService.getDeviceByDeviceId(serial); String uuid = UUID.randomUUID().toString(); String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (ObjectUtils.isEmpty(code) ? serial : code); DeferredResult result = new DeferredResult<> (timeout * 1000L); DeferredResultEx deferredResultEx = new DeferredResultEx<>(result); result.onTimeout(()->{ - logger.warn("<模拟接口> 获取设备预置位超时"); + log.warn("<模拟接口> 获取设备预置位超时"); // 释放rtpserver RequestMessage msg = new RequestMessage(); msg.setId(uuid); @@ -246,7 +243,7 @@ public class ApiDeviceController { resultHolder.invokeResult(msg); }); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 获取设备预置位: {}", e.getMessage()); + log.error("[命令发送失败] 获取设备预置位: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } return result; diff --git a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java index 8937fd86e..a4cca7934 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java +++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java @@ -8,15 +8,14 @@ import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; +import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; +import com.genersoft.iot.vmp.gb28181.service.IPlayService; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.media.bean.MediaServer; -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.InviteErrorCode; -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; @@ -30,24 +29,23 @@ import java.text.ParseException; */ @SuppressWarnings(value = {"rawtypes", "unchecked"}) +@Slf4j @RestController @RequestMapping(value = "/api/v1/stream") public class ApiStreamController { - private final static Logger logger = LoggerFactory.getLogger(ApiStreamController.class); - @Autowired private SIPCommander cmder; - @Autowired - private IVideoManagerStorage storager; - @Autowired private UserSetting userSetting; @Autowired private IDeviceService deviceService; + @Autowired + private IDeviceChannelService deviceChannelService; + @Autowired private IPlayService playService; @@ -80,7 +78,7 @@ public class ApiStreamController { ){ DeferredResult result = new DeferredResult<>(userSetting.getPlayTimeout().longValue() + 10); - Device device = storager.queryVideoDevice(serial); + Device device = deviceService.getDeviceByDeviceId(serial); if (device == null ) { JSONObject resultJSON = new JSONObject(); resultJSON.put("error","device[ " + serial + " ]未找到"); @@ -92,28 +90,31 @@ public class ApiStreamController { result.setResult(resultJSON); return result; } - result.onTimeout(()->{ - logger.info("播放等待超时"); - JSONObject resultJSON = new JSONObject(); - resultJSON.put("error","timeout"); - result.setResult(resultJSON); - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, serial, code); - storager.stopPlay(serial, code); - // 清理RTP server - }); - DeviceChannel deviceChannel = storager.queryChannel(serial, code); + + DeviceChannel deviceChannel = deviceChannelService.getOne(serial, code); if (deviceChannel == null) { JSONObject resultJSON = new JSONObject(); resultJSON.put("error","channel[ " + code + " ]未找到"); result.setResult(resultJSON); return result; - }else if (!deviceChannel.isStatus()) { + }else if (!deviceChannel.getStatus().equalsIgnoreCase("ON")) { JSONObject resultJSON = new JSONObject(); resultJSON.put("error","channel[ " + code + " ]offline"); result.setResult(resultJSON); return result; } + + result.onTimeout(()->{ + log.info("播放等待超时"); + JSONObject resultJSON = new JSONObject(); + resultJSON.put("error","timeout"); + result.setResult(resultJSON); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceChannel.getId()); + deviceChannelService.stopPlay(deviceChannel.getId()); + // 清理RTP server + }); + MediaServer newMediaServerItem = playService.getNewMediaServerItem(device); playService.play(newMediaServerItem, serial, code, null, (errorCode, msg, data) -> { @@ -222,18 +223,26 @@ public class ApiStreamController { ){ - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, serial, code); - if (inviteInfo == null) { - JSONObject result = new JSONObject(); - result.put("error","未找到流信息"); - return result; - } - Device device = deviceService.getDevice(serial); + + Device device = deviceService.getDeviceByDeviceId(serial); if (device == null) { JSONObject result = new JSONObject(); result.put("error","未找到设备"); return result; } + DeviceChannel deviceChannel = deviceChannelService.getOne(serial, code); + if (deviceChannel == null) { + JSONObject result = new JSONObject(); + result.put("error","未找到通道"); + return result; + } + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceChannel.getId()); + if (inviteInfo == null) { + JSONObject result = new JSONObject(); + result.put("error","未找到流信息"); + return result; + } + try { cmder.streamByeCmd(device, code, inviteInfo.getStream(), null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { @@ -242,7 +251,7 @@ public class ApiStreamController { return result; } inviteStreamService.removeInviteInfo(inviteInfo); - storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId()); + deviceChannelService.stopPlay(inviteInfo.getChannelId()); return null; } diff --git a/src/main/java/com/genersoft/iot/vmp/web/gb28181/dto/DeviceChannelExtend.java b/src/main/java/com/genersoft/iot/vmp/web/gb28181/dto/DeviceChannelExtend.java index 5e76cb701..e963075e0 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/gb28181/dto/DeviceChannelExtend.java +++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/dto/DeviceChannelExtend.java @@ -1,5 +1,8 @@ package com.genersoft.iot.vmp.web.gb28181.dto; +import lombok.Data; + +@Data public class DeviceChannelExtend { @@ -146,7 +149,7 @@ public class DeviceChannelExtend { * OFF * 遇到过NVR下的IPC下发信令可以推流, 但是 Status 响应 OFF */ - private boolean status; + private String status; /** * 经度 @@ -208,21 +211,6 @@ public class DeviceChannelExtend { */ private String gpsTime; - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } public void setPTZType(int PTZType) { this.PTZType = PTZType; @@ -244,312 +232,4 @@ public class DeviceChannelExtend { break; } } - - public String getChannelId() { - return channelId; - } - - public void setChannelId(String channelId) { - this.channelId = channelId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getManufacture() { - return manufacture; - } - - public void setManufacture(String manufacture) { - this.manufacture = manufacture; - } - - public String getModel() { - return model; - } - - public void setModel(String model) { - this.model = model; - } - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.owner = owner; - } - - public String getCivilCode() { - return civilCode; - } - - public void setCivilCode(String civilCode) { - this.civilCode = civilCode; - } - - public String getBlock() { - return block; - } - - public void setBlock(String block) { - this.block = block; - } - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - public int getParental() { - return parental; - } - - public void setParental(int parental) { - this.parental = parental; - } - - public String getParentId() { - return parentId; - } - - public void setParentId(String parentId) { - this.parentId = parentId; - } - - public int getSafetyWay() { - return safetyWay; - } - - public void setSafetyWay(int safetyWay) { - this.safetyWay = safetyWay; - } - - public int getRegisterWay() { - return registerWay; - } - - public void setRegisterWay(int registerWay) { - this.registerWay = registerWay; - } - - public String getCertNum() { - return certNum; - } - - public void setCertNum(String certNum) { - this.certNum = certNum; - } - - public int getCertifiable() { - return certifiable; - } - - public void setCertifiable(int certifiable) { - this.certifiable = certifiable; - } - - public int getErrCode() { - return errCode; - } - - public void setErrCode(int errCode) { - this.errCode = errCode; - } - - public String getEndTime() { - return endTime; - } - - public void setEndTime(String endTime) { - this.endTime = endTime; - } - - public String getSecrecy() { - return secrecy; - } - - public void setSecrecy(String secrecy) { - this.secrecy = secrecy; - } - - public String getIpAddress() { - return ipAddress; - } - - public void setIpAddress(String ipAddress) { - this.ipAddress = ipAddress; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public int getPTZType() { - return PTZType; - } - - public String getPTZTypeText() { - return PTZTypeText; - } - - public void setPTZTypeText(String PTZTypeText) { - this.PTZTypeText = PTZTypeText; - } - - public boolean isStatus() { - return status; - } - - public void setStatus(boolean status) { - this.status = status; - } - - public double getLongitude() { - return longitude; - } - - public void setLongitude(double longitude) { - this.longitude = longitude; - } - - public double getLatitude() { - return latitude; - } - - public void setLatitude(double latitude) { - this.latitude = latitude; - } - - public double getLongitudeGcj02() { - return longitudeGcj02; - } - - public void setLongitudeGcj02(double longitudeGcj02) { - this.longitudeGcj02 = longitudeGcj02; - } - - public double getLatitudeGcj02() { - return latitudeGcj02; - } - - public void setLatitudeGcj02(double latitudeGcj02) { - this.latitudeGcj02 = latitudeGcj02; - } - - public double getLongitudeWgs84() { - return longitudeWgs84; - } - - public void setLongitudeWgs84(double longitudeWgs84) { - this.longitudeWgs84 = longitudeWgs84; - } - - public double getLatitudeWgs84() { - return latitudeWgs84; - } - - public void setLatitudeWgs84(double latitudeWgs84) { - this.latitudeWgs84 = latitudeWgs84; - } - - public int getSubCount() { - return subCount; - } - - public void setSubCount(int subCount) { - this.subCount = subCount; - } - - public boolean isHasAudio() { - return hasAudio; - } - - public void setHasAudio(boolean hasAudio) { - this.hasAudio = hasAudio; - } - - public String getStreamId() { - return streamId; - } - - public void setStreamId(String streamId) { - this.streamId = streamId; - } - - public String getCreateTime() { - return createTime; - } - - public void setCreateTime(String createTime) { - this.createTime = createTime; - } - - public String getUpdateTime() { - return updateTime; - } - - public void setUpdateTime(String updateTime) { - this.updateTime = updateTime; - } - - public int getChannelType() { - return channelType; - } - - public void setChannelType(int channelType) { - this.channelType = channelType; - } - - public String getBusinessGroupId() { - return businessGroupId; - } - - public void setBusinessGroupId(String businessGroupId) { - this.businessGroupId = businessGroupId; - } - - public String getGpsTime() { - return gpsTime; - } - - public void setGpsTime(String gpsTime) { - this.gpsTime = gpsTime; - } - - public String getDeviceName() { - return deviceName; - } - - public void setDeviceName(String deviceName) { - this.deviceName = deviceName; - } - - public boolean isDeviceOnline() { - return deviceOnline; - } - - public void setDeviceOnline(boolean deviceOnline) { - this.deviceOnline = deviceOnline; - } } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 93c8af3b0..fbc7d5e38 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -44,7 +44,7 @@ spring: max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位) #[可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 server: - port: 8080 + port: 18080 # [可选] HTTPS配置, 默认不开启 ssl: # [可选] 是否开启HTTPS访问 @@ -68,21 +68,17 @@ sip: # [可选] id: 41010500002000000001 # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 - password: bajiuwulian1006 + password: 12345678 # 是否存储alarm信息 - alarm: true + alarm: false #zlm 默认服务器配置 media: id: zlmediakit-local # [必须修改] zlm服务器的内网IP - ip: 172.19.128.50 - # [可选] 有公网IP就配置公网IP, 不可用域名 - wan_ip: + ip: 192.168.1.10 # [必须修改] zlm服务器的http.port http-port: 9092 - # [可选] zlm服务器访问WVP所使用的IP, 默认使用127.0.0.1,zlm和wvp没有部署在同一台服务器时必须配置 - hook-ip: 172.19.128.50 # [必选选] zlm服务器的hook.admin_params=secret secret: TWSYFgYJOQWB4ftgeYut8DW4wbs7pQnj # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 @@ -90,9 +86,9 @@ media: # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 enable: true # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 - port-range: 50000,50300 # 端口范围 + port-range: 40000,45000 # 端口范围 # [可选] 国标级联在此范围内选择端口发送媒体流, - send-port-range: 50000,50300 # 端口范围 + send-port-range: 50000,55000 # 端口范围 # [根据业务需求配置] user-settings: # 点播/录像回放 等待超时时间,单位:毫秒 diff --git a/src/main/resources/civilCode.csv b/src/main/resources/civilCode.csv index 7785924fa..6c2284a00 100644 --- a/src/main/resources/civilCode.csv +++ b/src/main/resources/civilCode.csv @@ -1,38 +1,40 @@ 编号,名称,上级 11,北京市, -110101,东城区,11 -110102,西城区,11 -110105,朝阳区,11 -110106,丰台区,11 -110107,石景山区,11 -110108,海淀区,11 -110109,门头沟区,11 -110111,房山区,11 -110112,通州区,11 -110113,顺义区,11 -110114,昌平区,11 -110115,大兴区,11 -110116,怀柔区,11 -110117,平谷区,11 -110118,密云区,11 -110119,延庆区,11 +1101,市辖区,11 +110101,东城区,1101 +110102,西城区,1101 +110105,朝阳区,1101 +110106,丰台区,1101 +110107,石景山区,1101 +110108,海淀区,1101 +110109,门头沟区,1101 +110111,房山区,1101 +110112,通州区,1101 +110113,顺义区,1101 +110114,昌平区,1101 +110115,大兴区,1101 +110116,怀柔区,1101 +110117,平谷区,1101 +110118,密云区,1101 +110119,延庆区,1101 12,天津市, -120101,和平区,12 -120102,河东区,12 -120103,河西区,12 -120104,南开区,12 -120105,河北区,12 -120106,红桥区,12 -120110,东丽区,12 -120111,西青区,12 -120112,津南区,12 -120113,北辰区,12 -120114,武清区,12 -120115,宝坻区,12 -120116,滨海新区,12 -120117,宁河区,12 -120118,静海区,12 -120119,蓟州区,12 +1201,市辖区,12 +120101,和平区,1201 +120102,河东区,1201 +120103,河西区,1201 +120104,南开区,1201 +120105,河北区,1201 +120106,红桥区,1201 +120110,东丽区,1201 +120111,西青区,1201 +120112,津南区,1201 +120113,北辰区,1201 +120114,武清区,1201 +120115,宝坻区,1201 +120116,滨海新区,1201 +120117,宁河区,1201 +120118,静海区,1201 +120119,蓟州区,1201 13,河北省, 1301,石家庄市,13 130102,长安区,1301 @@ -786,22 +788,23 @@ 232721,呼玛县,2327 232722,塔河县,2327 31,上海市, -310101,黄浦区,31 -310104,徐汇区,31 -310105,长宁区,31 -310106,静安区,31 -310107,普陀区,31 -310109,虹口区,31 -310110,杨浦区,31 -310112,闵行区,31 -310113,宝山区,31 -310114,嘉定区,31 -310115,浦东新区,31 -310116,金山区,31 -310117,松江区,31 -310118,青浦区,31 -310120,奉贤区,31 -310151,崇明区,31 +3101,市辖区,31 +310101,黄浦区,3101 +310104,徐汇区,3101 +310105,长宁区,3101 +310106,静安区,3101 +310107,普陀区,3101 +310109,虹口区,3101 +310110,杨浦区,3101 +310112,闵行区,3101 +310113,宝山区,3101 +310114,嘉定区,3101 +310115,浦东新区,3101 +310116,金山区,3101 +310117,松江区,3101 +310118,青浦区,3101 +310120,奉贤区,3101 +310151,崇明区,3101 32,江苏省, 3201,南京市,32 320102,玄武区,3201 @@ -2226,44 +2229,45 @@ 469029,保亭黎族苗族自治县,46 469030,琼中黎族苗族自治县,46 50,重庆市, -500101,万州区,50 -500102,涪陵区,50 -500103,渝中区,50 -500104,大渡口区,50 -500105,江北区,50 -500106,沙坪坝区,50 -500107,九龙坡区,50 -500108,南岸区,50 -500109,北碚区,50 -500110,綦江区,50 -500111,大足区,50 -500112,渝北区,50 -500113,巴南区,50 -500114,黔江区,50 -500115,长寿区,50 -500116,江津区,50 -500117,合川区,50 -500118,永川区,50 -500119,南川区,50 -500120,璧山区,50 -500151,铜梁区,50 -500152,潼南区,50 -500153,荣昌区,50 -500154,开州区,50 -500155,梁平区,50 -500156,武隆区,50 -500229,城口县,50 -500230,丰都县,50 -500231,垫江县,50 -500233,忠县,50 -500235,云阳县,50 -500236,奉节县,50 -500237,巫山县,50 -500238,巫溪县,50 -500240,石柱土家族自治县,50 -500241,秀山土家族苗族自治县,50 -500242,酉阳土家族苗族自治县,50 -500243,彭水苗族土家族自治县,50 +5001,市辖区,50 +500101,万州区,5001 +500102,涪陵区,5001 +500103,渝中区,5001 +500104,大渡口区,5001 +500105,江北区,5001 +500106,沙坪坝区,5001 +500107,九龙坡区,5001 +500108,南岸区,5001 +500109,北碚区,5001 +500110,綦江区,5001 +500111,大足区,5001 +500112,渝北区,5001 +500113,巴南区,5001 +500114,黔江区,5001 +500115,长寿区,5001 +500116,江津区,5001 +500117,合川区,5001 +500118,永川区,5001 +500119,南川区,5001 +500120,璧山区,5001 +500151,铜梁区,5001 +500152,潼南区,5001 +500153,荣昌区,5001 +500154,开州区,5001 +500155,梁平区,5001 +500156,武隆区,5001 +500229,城口县,5001 +500230,丰都县,5001 +500231,垫江县,5001 +500233,忠县,5001 +500235,云阳县,5001 +500236,奉节县,5001 +500237,巫山县,5001 +500238,巫溪县,5001 +500240,石柱土家族自治县,5001 +500241,秀山土家族苗族自治县,5001 +500242,酉阳土家族苗族自治县,5001 +500243,彭水苗族土家族自治县,5001 51,四川省, 5101,成都市,51 510104,锦江区,5101 @@ -3217,4 +3221,4 @@ 659009,昆玉市,65 71,台湾省, 81,香港特别行政区, -82,澳门特别行政区, \ No newline at end of file +82,澳门特别行政区, diff --git a/src/main/resources/all-application.yml b/src/main/resources/配置详情.yml similarity index 99% rename from src/main/resources/all-application.yml rename to src/main/resources/配置详情.yml index 31db6807e..2f8e8da15 100644 --- a/src/main/resources/all-application.yml +++ b/src/main/resources/配置详情.yml @@ -123,6 +123,8 @@ sip: # keepalliveToOnline: false # 是否存储alarm信息 alarm: false + # 命令发送等待回复的超时时间, 单位:秒 + timeout: 15 # 做为JT1078服务器的配置 jt1078: diff --git a/web_src/config/index.js b/web_src/config/index.js index 9d24d1ba3..50e92edd1 100644 --- a/web_src/config/index.js +++ b/web_src/config/index.js @@ -12,14 +12,14 @@ module.exports = { assetsPublicPath: '/', proxyTable: { '/debug': { - target: 'http://127.0.0.1:18082', + target: 'http://127.0.0.1:18080', changeOrigin: true, pathRewrite: { '^/debug': '/' } }, '/static/snap': { - target: 'http://127.0.0.1:18082', + target: 'http://127.0.0.1:18080', changeOrigin: true, // pathRewrite: { // '^/static/snap': '/static/snap' diff --git a/web_src/index.html b/web_src/index.html index bd66d3923..9029725a6 100644 --- a/web_src/index.html +++ b/web_src/index.html @@ -14,6 +14,8 @@ + +
diff --git a/web_src/package.json b/web_src/package.json index 255e78878..847b5d04b 100644 --- a/web_src/package.json +++ b/web_src/package.json @@ -11,14 +11,16 @@ }, "dependencies": { "@liveqing/liveplayer": "^2.7.10", + "@wchbrad/vue-easy-tree": "^1.0.12", "axios": "^0.24.0", "core-js": "^2.6.5", "echarts": "^4.9.0", - "element-ui": "^2.15.6", + "element-ui": "^2.15.14", "fingerprintjs2": "^2.1.2", "moment": "^2.29.1", "ol": "^6.14.1", "postcss-pxtorem": "^5.1.1", + "screenfull": "5.1.0", "uuid": "^8.3.2", "v-charts": "^1.19.0", "vue": "^2.6.11", @@ -26,7 +28,6 @@ "vue-clipboards": "^1.3.0", "vue-contextmenujs": "^1.3.13", "vue-cookies": "^1.8.3", - "vue-giant-tree": "^0.1.5", "vue-router": "^3.1.6", "vue-ztree-2.0": "^1.0.4" }, diff --git a/web_src/src/components/ChannelEdit.vue b/web_src/src/components/ChannelEdit.vue new file mode 100644 index 000000000..80f116e60 --- /dev/null +++ b/web_src/src/components/ChannelEdit.vue @@ -0,0 +1,37 @@ + + + diff --git a/web_src/src/components/CloudRecord.vue b/web_src/src/components/CloudRecord.vue index b6f02865b..3517587ba 100755 --- a/web_src/src/components/CloudRecord.vue +++ b/web_src/src/components/CloudRecord.vue @@ -7,7 +7,7 @@
搜索: - 开始时间:
- + @@ -83,7 +83,7 @@
设备列表
+ 搜索: + + 在线状态: + + + + + 添加设备 + 平台信息 +
- + @@ -55,7 +67,7 @@ - + @@ -94,10 +103,12 @@ import uiHeader from '../layout/UiHeader.vue' import deviceEdit from './dialog/deviceEdit.vue' import syncChannelProgress from './dialog/SyncChannelProgress.vue' +import configInfo from "./dialog/configInfo.vue"; export default { name: 'app', components: { + configInfo, uiHeader, deviceEdit, syncChannelProgress, @@ -106,7 +117,8 @@ export default { return { deviceList: [], //设备列表 currentDevice: {}, //当前操作设备对象 - + searchSrt: "", + online: null, videoComponentList: [], updateLooper: 0, //数据刷新轮训标志 currentDeviceChannelsLenth: 0, @@ -157,7 +169,9 @@ export default { url: `/api/device/query/devices`, params: { page: this.currentPage, - count: this.count + count: this.count, + query: this.searchSrt, + status: this.online, } }).then( (res)=> { if (res.data.code === 0) { @@ -220,12 +234,10 @@ export default { type: 'error' }); } else { - // that.$message({ - // showClose: true, - // message: res.data.msg, - // type: 'success' - // }); - this.$refs.syncChannelProgress.openDialog(itemData.deviceId) + this.$refs.syncChannelProgress.openDialog(itemData.deviceId, ()=>{ + console.log(32322) + this.initData() + }) } that.initData() }).catch((e) => { @@ -292,6 +304,21 @@ export default { setTimeout(this.getDeviceList, 200) }) + }, + showInfo: function (){ + + this.$axios({ + method: 'get', + url: `/api/server/system/configInfo`, + }).then( (res)=> { + console.log(res) + if (res.data.code === 0) { + console.log(2222) + console.log(this.$refs.configInfo) + this.$refs.configInfo.openDialog(res.data.data) + } + }).catch( (error)=> { + }); } diff --git a/web_src/src/components/ParentPlatformList.vue b/web_src/src/components/ParentPlatformList.vue deleted file mode 100755 index c3c9735f0..000000000 --- a/web_src/src/components/ParentPlatformList.vue +++ /dev/null @@ -1,193 +0,0 @@ - - - - diff --git a/web_src/src/components/PlatformEdit.vue b/web_src/src/components/PlatformEdit.vue new file mode 100644 index 000000000..f815adda5 --- /dev/null +++ b/web_src/src/components/PlatformEdit.vue @@ -0,0 +1,304 @@ + + + + diff --git a/web_src/src/components/PlatformList.vue b/web_src/src/components/PlatformList.vue new file mode 100755 index 000000000..0a9ece33a --- /dev/null +++ b/web_src/src/components/PlatformList.vue @@ -0,0 +1,301 @@ + + + + diff --git a/web_src/src/components/PushVideoList.vue b/web_src/src/components/PushVideoList.vue deleted file mode 100755 index e69f56da1..000000000 --- a/web_src/src/components/PushVideoList.vue +++ /dev/null @@ -1,359 +0,0 @@ - - - - - diff --git a/web_src/src/components/StreamProxyEdit.vue b/web_src/src/components/StreamProxyEdit.vue new file mode 100644 index 000000000..12413bfc9 --- /dev/null +++ b/web_src/src/components/StreamProxyEdit.vue @@ -0,0 +1,263 @@ + + + + diff --git a/web_src/src/components/StreamProxyList.vue b/web_src/src/components/StreamProxyList.vue index eb1f7d82c..c148c40ed 100755 --- a/web_src/src/components/StreamProxyList.vue +++ b/web_src/src/components/StreamProxyList.vue @@ -1,113 +1,107 @@ @@ -116,12 +110,15 @@ import onvifEdit from './dialog/onvifEdit.vue' import devicePlayer from './dialog/devicePlayer.vue' import uiHeader from '../layout/UiHeader.vue' + import StreamProxyEdit from "./StreamProxyEdit"; + import MediaServer from "./service/MediaServer"; export default { name: 'streamProxyList', components: { devicePlayer, streamProxyEdit, onvifEdit, + StreamProxyEdit, uiHeader }, data() { @@ -134,7 +131,13 @@ currentPage:1, count:15, total:0, - startBtnLoading: false + startBtnLoading: false, + streamProxy: null, + searchSrt: "", + mediaServerId: "", + pulling: "", + mediaServerObj: new MediaServer(), + mediaServerList: [], }; }, computed: { @@ -150,12 +153,20 @@ methods: { initData: function() { this.getStreamProxyList(); + this.mediaServerObj.getOnlineMediaServerList((data) => { + this.mediaServerList = data.data; + }) }, stopUpdateList: function (){ window.clearInterval(this.updateLooper) }, startUpdateList: function (){ - this.updateLooper = setInterval(this.initData, 1000); + this.updateLooper = setInterval(()=>{ + if (!this.streamProxy) { + this.getStreamProxyList() + } + + }, 1000); }, currentChange: function(val){ this.currentPage = val; @@ -172,7 +183,10 @@ url:`/api/proxy/list`, params: { page: that.currentPage, - count: that.count + count: that.count, + query: this.searchSrt, + pulling: this.pulling, + mediaServerId: this.mediaServerId, } }).then(function (res) { if (res.data.code === 0) { @@ -187,7 +201,15 @@ }); }, addStreamProxy: function(){ - this.$refs.streamProxyEdit.openDialog(null, this.initData) + // this.$refs.streamProxyEdit.openDialog(null, this.initData) + this.streamProxy = { + type: "default", + noneReader: 1, + enable: true, + enableAudio: true, + mediaServerId: "", + timeout: 10, + } }, addOnvif: function(){ this.$axios({ @@ -203,28 +225,47 @@ } }) }else { - this.$message.success("未找到可用设备"); + this.$message.success({ + showClose: true, + message: "未找到可用设备" + }); } }else { - this.$message.error(res.data.msg); + this.$message.error({ + showClose: true, + message: res.data.msg + }) } }).catch((error)=> { - this.$message.error(error.response.data.msg); + this.$message.error({ + showClose: true, + message: error + }) }); }, - saveStreamProxy: function(){ + edit: function(row){ + if (row.enableDisableNoneReader) { + this.$set(row, "noneReader", 1) + }else if (row.enableRemoveNoneReader) { + this.$set(row, "noneReader", 2) + }else { + this.$set(row, "noneReader", 0) + } + this.streamProxy = row + this.$set(this.streamProxy, "rtspType", row.rtspType) + }, + closeEdit: function(row){ + this.streamProxy = null }, play: function(row){ let that = this; this.$axios({ method: 'get', - url:`/api/push/getPlayUrl`, + url:`/api/proxy/start`, params: { - app: row.app, - stream: row.stream, - mediaServerId: row.mediaServerId + id: row.id, } }).then(function (res) { if (res.data.code === 0) { @@ -244,28 +285,50 @@ console.log(error); }); + }, + stopPlay: function(row){ + let that = this; + this.$axios({ + method: 'get', + url:`/api/proxy/stop`, + params: { + id: row.id, + } + }).then(function (res) { + if (res.data.code === 0) { + + }else { + that.$message.error(res.data.msg); + } + + }).catch(function (error) { + console.log(error); + }); + }, queryCloudRecords: function (row) { this.$router.push(`/cloudRecordDetail/${row.app}/${row.stream}`) }, deleteStreamProxy: function(row){ - let that = this; this.$confirm('确定删除此代理吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { - that.$axios({ + this.$axios({ method:"delete", - url:"/api/proxy/del", + url:"/api/proxy/delete", params:{ - app: row.app, - stream: row.stream + id: row.id, } }).then((res)=>{ - that.initData() - }).catch(function (error) { + this.$message.success({ + showClose: true, + message: "删除成功" + }) + this.initData() + }).catch((error) =>{ console.log(error); }); }).catch(() => { diff --git a/web_src/src/components/StreamPushEdit.vue b/web_src/src/components/StreamPushEdit.vue new file mode 100644 index 000000000..1a4a35950 --- /dev/null +++ b/web_src/src/components/StreamPushEdit.vue @@ -0,0 +1,138 @@ + + + + diff --git a/web_src/src/components/StreamPushList.vue b/web_src/src/components/StreamPushList.vue new file mode 100755 index 000000000..8e89c6bfe --- /dev/null +++ b/web_src/src/components/StreamPushList.vue @@ -0,0 +1,367 @@ + + + + + diff --git a/web_src/src/components/UserApiKeyManager.vue b/web_src/src/components/UserApiKeyManager.vue index 3d6e5ebfa..52a698ee6 100644 --- a/web_src/src/components/UserApiKeyManager.vue +++ b/web_src/src/components/UserApiKeyManager.vue @@ -11,7 +11,7 @@ - @@ -64,7 +64,7 @@ - @@ -35,7 +35,7 @@
- - + - - + - - - - +